diff --git a/AGENTS.md b/AGENTS.md
index 716956871d..3175bdd7d6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -48,6 +48,10 @@ dotnet test tests/Spice86.Tests --filter 'FullyQualifiedName!~SingleStepTest'
> **SingleStepTest exclusion rule**: `SingleStepTest` runs millions of CPU instruction test cases and take an extremely long time to complete. **Always exclude it** using `--filter 'FullyQualifiedName!~SingleStepTest'` unless the change being tested directly touches CPU instruction decoding, execution, or flag handling (e.g. changes to `CfgCpu`, instruction parsers, ALU operations, or flag computation). When in doubt, exclude it.
+> **Test output rule (agents only)**: no need to redirect `dotnet test` output to a file. On success the console summary is short (a single `Passed! - Failed: 0, Passed: N ...` line), so run it directly and read the summary. Only capture to a file when a run actually fails and you need the full failure detail.
+
+> **Long-running test rule (Kiro agents only)**: the foreground shell tool (`execute_bash`) enforces a hard wall-clock cap (roughly 25-60s in practice) that fires *before* the `timeout` argument. When it trips it force-returns exit 1 while the detached `dotnet` process keeps running and completes normally, which looks exactly like a crash (abrupt exit 1, no test summary, no dump) but is not one. Any test run expected to exceed ~25s (the full suite is ~2 min; `CfgGraphReloadTest` alone is ~55s) MUST be launched with the background-process tool (which has no such cap), writing a completion marker, then polled with short (<20s) foreground calls until the marker appears. Do not conclude a suite "crashed" from a bare exit 1 with no summary; re-run it in the background and check the real result first. Splitting `--filter` so each foreground run stays under the cap also works.
+
### Debugging Workflow
- **GDB Integration**: Server runs on port 10000 by default (`--GdbPort 10000`)
- Use `--Debug` to pause at startup for breakpoint setup
@@ -117,9 +121,10 @@ Variants: `MemoryBasedDataStructureWithCsBaseAddress`, `MemoryBasedDataStructure
- **No scripts outside the project** - do NOT create temporary scripts (Python, Bash, PowerShell, Node, etc.) anywhere on the filesystem, including `/tmp`, the user's home directory, or any location outside the workspace. Do not create throwaway helper scripts inside the workspace either. Use the available tools (file editing, grep, search, terminal one-liners) directly. If a multi-step computation is truly needed, run it as an inline shell one-liner in the terminal without writing a file.
- **Use `tmp/` for temporary files** - if a temporary file must be written (e.g., captured command output, intermediate data), place it inside the `tmp/` folder at the root of the repository. Never write to `/tmp` or any path outside the workspace.
- **Avoid complexity** - keep cyclomatic complexity low, prefer simple, linear code over nested conditionals
-- **No optional parameters** - avoid nullable or optional parameters in new code
+- **No optional parameters** - avoid optional/defaulted method parameters in new code. Prefer non-nullable types; a field or dependency should be nullable only when its absence is a real, distinct state (e.g. a feature disabled by a flag). When a nullable feature-gate would otherwise force `?.`/`is not null` guards across many call sites, prefer a null-object (no-op) implementation or unconditional construction over a nullable reference.
- **No complex ternary expressions** - avoid nested, chained, or multi-line ternaries; simple single-line ternaries with short operands are allowed (e.g. `int x = a > b ? a : b;`), but non-trivial conditions or non-trivial branches must use explicit `if/else`
- **Minimal comments** - write self-documenting code with clear names; avoid obvious comments
+- **No references to plan/spec sections in code** - never point comments, XML docs, or test names at sections of a plan, spec, or implementation-tracking document (e.g. "Phase 5", "Gap D", "Rule 4", "section S6.1.1", "Test 18", "implementation plan"). Those documents are temporary or drift over time, leaving dangling pointers that mean nothing to a future reader. Describe the actual behavior, invariant, or scenario directly instead.
- **Test before submit** - always run tests after code changes to verify functionality
- **Rebuild and verify** - For any task that changes code or tests, rebuild the project and run the full test suite; do not stop until all tests are green.
- **Concise documentation** - XML docs should be precise and complete but not verbose; avoid excessive remarks
diff --git a/src/Spice86.Core/CLI/Configuration.cs b/src/Spice86.Core/CLI/Configuration.cs
index 17ef4c1f63..62d46f7024 100644
--- a/src/Spice86.Core/CLI/Configuration.cs
+++ b/src/Spice86.Core/CLI/Configuration.cs
@@ -357,4 +357,17 @@ public sealed class Configuration : CommandSettings {
[CommandOption("--AllowIvtAddress0")]
public bool AllowIvtAddress0 { get; init; }
+ ///
+ /// When true (default), the speculative CFG explorer is enabled: after each newly-observed
+ /// instruction is parsed, the explorer performs a recursive-descent static decode of all
+ /// statically-reachable successors that have not yet been observed, marking the resulting nodes
+ /// as speculative. This allows the C# code generator to emit real branches for unobserved
+ /// conditional arms instead of always falling back to FailAsUntested .
+ /// When false, no speculative nodes, edges, or index entries are created; the graph content is
+ /// byte-identical to the pre-feature baseline.
+ ///
+ [CommandOption("--EnableSpeculativeCfgExploration ")]
+ [DefaultValue(true)]
+ public bool EnableSpeculativeCfgExploration { get; init; } = true;
+
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs
index 1f42126399..7e5fbfe2b3 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs
@@ -34,14 +34,14 @@ public CfgCpu(IMemory memory, State state, IOPortDispatcher ioPortDispatcher, Ca
DualPic dualPic, EmulatorBreakpointsManager emulatorBreakpointsManager,
IPauseHandler pauseHandler,
FunctionCatalogue functionCatalogue,
- bool useCodeOverride, bool failOnInvalidOpcode, bool allowIvtAddress0, ILoggerService loggerService, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator, CpuHeavyLogger? cpuHeavyLogger = null) {
+ bool useCodeOverride, bool failOnInvalidOpcode, bool allowIvtAddress0, bool enableSpeculativeExploration, ILoggerService loggerService, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator, CpuHeavyLogger? cpuHeavyLogger = null) {
_loggerService = loggerService;
_state = state;
_dualPic = dualPic;
_cpuHeavyLogger = cpuHeavyLogger;
_emulatorBreakpointsManager = emulatorBreakpointsManager;
_pauseHandler = pauseHandler;
- CfgNodeFeeder = new(memory, state, emulatorBreakpointsManager, _replacerRegistry, executionCompiler, idAllocator);
+ CfgNodeFeeder = new(memory, state, emulatorBreakpointsManager, _replacerRegistry, executionCompiler, idAllocator, enableSpeculativeExploration);
_executionContextManager = new(memory, state, CfgNodeFeeder, _replacerRegistry, functionCatalogue, useCodeOverride, loggerService, cpuHeavyLogger);
_instructionExecutionHelper = new(state, memory, ioPortDispatcher, callbackHandler, emulatorBreakpointsManager, _executionContextManager, failOnInvalidOpcode, allowIvtAddress0, loggerService);
}
@@ -61,6 +61,7 @@ public CfgCpu(IMemory memory, State state, IOPortDispatcher ioPortDispatcher, Ca
public FunctionHandler FunctionHandlerInUse => ExecutionContextManager.CurrentExecutionContext.FunctionHandler;
public bool IsInitialExecutionContext => ExecutionContextManager.CurrentExecutionContext.Depth == 0;
private ExecutionContext CurrentExecutionContext => _executionContextManager.CurrentExecutionContext;
+
public ICfgNode ToExecute() {
return CfgNodeFeeder.GetLinkedCfgNodeToExecute(CurrentExecutionContext);
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgBlock.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgBlock.cs
index b14d1f5833..417b932686 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgBlock.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgBlock.cs
@@ -23,6 +23,12 @@ public sealed class CfgBlock : CfgNode {
///
private int _nonLiveCounter;
+ ///
+ /// Number of contained nodes whose is currently true .
+ /// is (_speculativeCounter > 0) .
+ ///
+ private int _speculativeCounter;
+
private BlockNode? _cachedDisplayAst;
private bool _isDisplayAstStale = true;
@@ -50,6 +56,13 @@ public CfgBlock(int id, ICfgNode entry)
///
public override bool IsLive => _nonLiveCounter == 0;
+ ///
+ ///
+ /// O(1). The block is speculative iff at least one contained instruction is speculative;
+ /// computed from the maintained without iterating.
+ ///
+ public override bool IsSpeculative => _speculativeCounter > 0;
+
///
///
/// Always null : a is itself the container, not contained in one.
@@ -140,19 +153,22 @@ public override IVisitableAstNode DisplayAst {
///
/// Appends to the end of the block, making it the new
- /// . Updates the non-live counter from the node's live state.
+ /// . Updates the non-live and speculative counters from the node's state.
///
internal void Append(ICfgNode node) {
_instructions.Add(node);
if (!node.IsLive) {
_nonLiveCounter++;
}
+ if (node.IsSpeculative) {
+ _speculativeCounter++;
+ }
_isDisplayAstStale = true;
}
///
/// Replaces the node at with , preserving
- /// order. Adjusts the non-live counter to reflect the new node's live state.
+ /// order. Adjusts the non-live and speculative counters to reflect the new node's state.
///
internal void ReplaceInPlace(int index, ICfgNode newNode) {
ICfgNode old = _instructions[index];
@@ -163,18 +179,46 @@ internal void ReplaceInPlace(int index, ICfgNode newNode) {
if (!newNode.IsLive) {
_nonLiveCounter++;
}
+ if (old.IsSpeculative) {
+ _speculativeCounter--;
+ }
+ if (newNode.IsSpeculative) {
+ _speculativeCounter++;
+ }
_isDisplayAstStale = true;
}
+ ///
+ /// Removes from the block by value, maintaining the non-live and
+ /// speculative counters via O(1) incremental decrement (mirrors ).
+ /// Returns false when the node is not part of the block. Tolerates transient
+ /// non-contiguity: it is a batch-internal primitive for removing a contiguous suffix of
+ /// nodes one at a time.
+ ///
+ internal bool Remove(ICfgNode node) {
+ if (!_instructions.Remove(node)) {
+ return false;
+ }
+ if (!node.IsLive) {
+ _nonLiveCounter--;
+ }
+ if (node.IsSpeculative) {
+ _speculativeCounter--;
+ }
+ _isDisplayAstStale = true;
+ return true;
+ }
+
///
/// Removes nodes from through the end and returns them as a
- /// new list. The non-live counter is recomputed for the surviving prefix.
+ /// new list. The non-live and speculative counters are recomputed for the surviving prefix.
///
internal List SliceFrom(int splitIndex) {
int tailLength = _instructions.Count - splitIndex;
List tail = _instructions.GetRange(splitIndex, tailLength);
_instructions.RemoveRange(splitIndex, tailLength);
RecountNonLiveFromInstructions();
+ RecountSpeculativeFromInstructions();
_isDisplayAstStale = true;
return tail;
}
@@ -191,6 +235,18 @@ internal void OnContainedInstructionLiveChanged(bool nowLive) {
}
}
+ ///
+ /// Counter choke-point invoked by exactly once per
+ /// actual transition of a contained instruction.
+ ///
+ internal void OnContainedInstructionSpeculativeChanged(bool nowSpeculative) {
+ if (nowSpeculative) {
+ _speculativeCounter++;
+ } else {
+ _speculativeCounter--;
+ }
+ }
+
///
/// Recomputes the non-live counter from scratch by iterating the contained instructions.
/// Used after a split to re-base the counter on the new instruction set.
@@ -199,6 +255,14 @@ internal void RecountNonLiveFromInstructions() {
_nonLiveCounter = _instructions.Count(node => !node.IsLive);
}
+ ///
+ /// Recomputes the speculative counter from scratch by iterating the contained instructions.
+ /// Used after a split to re-base the counter on the new instruction set.
+ ///
+ internal void RecountSpeculativeFromInstructions() {
+ _speculativeCounter = _instructions.Count(node => node.IsSpeculative);
+ }
+
///
/// Returns the index of in the contained list, or -1 if
/// the node is not part of this block.
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgNode.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgNode.cs
index 48e124b7b4..04fc6e8e36 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgNode.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/CfgNode.cs
@@ -43,6 +43,21 @@ public CfgNodeExecutionAction CompiledExecution {
}
public abstract bool IsLive { get; }
+
+ ///
+ /// Default is false . overrides this to
+ /// expose its stored speculative flag, maintained by calls.
+ /// and keep the default false .
+ ///
+ public virtual bool IsSpeculative => false;
+
+ ///
+ /// No-op default: and have no speculative
+ /// state. overrides this to maintain its stored
+ /// flag and notify its containing block.
+ ///
+ public virtual void SetSpeculative(bool isSpeculative) {
+ }
public abstract void UpdateSuccessorCache();
public abstract ICfgNode? GetNextSuccessor(InstructionExecutionHelper helper);
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/ICfgNode.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/ICfgNode.cs
index a58eef8f3d..f344a3af10 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/ICfgNode.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ControlFlowGraph/ICfgNode.cs
@@ -37,6 +37,24 @@ public interface ICfgNode : IEquatable {
///
bool IsLive { get; }
+ ///
+ /// Returns whether the node is speculative.
+ /// A speculative node was decoded by the static explorer from memory bytes but has never been
+ /// executed and confirmed. It is a separate axis from : a speculative node
+ /// is always non-live, but a non-live observed node (e.g. SMC-evicted) is not speculative.
+ /// Speculative nodes never enter ,
+ /// , or a .
+ ///
+ bool IsSpeculative { get; }
+
+ ///
+ /// Updates the speculative provenance of this node. On an actual transition, keeps the
+ /// containing 's speculative counter in sync. Setting speculative also
+ /// forces the node non-live. No-op for node kinds that have no speculative state
+ /// ( , ).
+ ///
+ void SetSpeculative(bool isSpeculative);
+
///
/// True when the node execution can lead to going back to previous execution context if the next to execute is the correct address
///
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs
index 0b14f8ed6b..bb6a955af5 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs
@@ -11,6 +11,9 @@
using Spice86.Core.Emulator.Memory;
using Spice86.Shared.Emulator.Memory;
using Spice86.Shared.Interfaces;
+using Spice86.Shared.Utils;
+
+using System.Linq;
public class ExecutionContextManager : InstructionReplacer, IClearable {
private readonly ILoggerService _loggerService;
@@ -121,6 +124,39 @@ private void RegisterCurrentInstructionAsEntryPoint(SegmentedAddress entryAddres
nodes.Add(toExecute);
}
+ ///
+ /// Registers as a CFG entry point so it is treated as a generation root by
+ /// the graph exporter and the function partitioner. Used to seed emulator-installed hardware
+ /// interrupt handlers: these fire on external events with nondeterministic timing and may never be
+ /// reached from the program's observed entry points during discovery, yet must still become
+ /// generated overrides so generated code can service the interrupt.
+ ///
+ /// The handler entry instruction to register.
+ public void RegisterEntryPoint(CfgInstruction node) {
+ if (!ExecutionContextEntryPoints.TryGetValue(node.Address, out ISet? nodes)) {
+ nodes = new HashSet();
+ ExecutionContextEntryPoints.Add(node.Address, nodes);
+ }
+ nodes.Add(node);
+ }
+
+ ///
+ /// Seeds the given known-safe handler entry addresses for speculative exploration and registers
+ /// each decoded handler entry node as a CFG entry point. No-op per handler when speculative
+ /// exploration is disabled (no seeded node is produced).
+ ///
+ /// Entry addresses of emulator-installed interrupt handlers.
+ public void SeedKnownSafeHandlersAndRegisterEntryPoints(IReadOnlyList handlerAddresses) {
+ _cfgNodeFeeder.SeedKnownSafeHandlers(handlerAddresses);
+ foreach (SegmentedAddress handlerAddress in handlerAddresses) {
+ CfgInstruction? entryNode = _cfgNodeFeeder.NodeIndex.GetAtAddress(handlerAddress)
+ .FirstOrDefault(node => node.ContainingBlock is not null);
+ if (entryNode is not null) {
+ RegisterEntryPoint(entryNode);
+ }
+ }
+ }
+
public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction newInstruction) {
if (ExecutionContextEntryPoints.TryGetValue(newInstruction.Address, out ISet? entriesAtAddress)
&& entriesAtAddress.Remove(oldInstruction)) {
@@ -132,6 +168,24 @@ public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstru
}
}
+ ///
+ /// Handles removal fan-out: drops from the
+ /// entry-point set at its address so a removed generation root cannot linger as a
+ /// detached, de-indexed dead root. Removes the address key when its set empties.
+ ///
+ ///
+ /// Deliberately does NOT clear a context's
+ /// that still points at the removed node, unlike which repoints it.
+ /// Replacement has a live successor to point at; removal does not. The feeder's
+ /// reconcile-with-memory path relies on the stale pointer surviving the sweep: a non-live graph
+ /// node whose address still matches memory but is no longer indexed is how it detects a swept
+ /// speculative node and routes to the live memory node. Nulling it here makes that node null and
+ /// trips the address-mismatch guard instead.
+ ///
+ public override void RemoveInstruction(CfgInstruction instruction) {
+ DictionaryUtils.RemoveFromCollection(ExecutionContextEntryPoints, instruction.Address, instruction);
+ }
+
private static void UpdateNodeToExecuteIfStale(ExecutionContext context,
CfgInstruction oldInstruction, CfgInstruction newInstruction) {
if (oldInstruction.Equals(context.NodeToExecuteNextAccordingToGraph)) {
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs
index 4f0f3a7ac3..3027d8bcaa 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs
@@ -8,6 +8,8 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.VM.Breakpoint;
+using Spice86.Shared.Emulator.Memory;
+
using SequentialIdAllocator = Spice86.Shared.Utils.SequentialIdAllocator;
///
@@ -23,14 +25,26 @@ public class CfgNodeFeeder {
private readonly NodeLinker _nodeLinker;
public CfgNodeFeeder(IMemory memory, State state, EmulatorBreakpointsManager emulatorBreakpointsManager,
- InstructionReplacerRegistry replacerRegistry, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator) {
+ InstructionReplacerRegistry replacerRegistry, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator,
+ bool enableSpeculativeExploration) {
_state = state;
- InstructionsFeeder = new(emulatorBreakpointsManager, memory, state, replacerRegistry, executionCompiler, idAllocator);
_nodeLinker = new(replacerRegistry, executionCompiler, idAllocator);
+ InstructionsFeeder = new(emulatorBreakpointsManager, memory, state, replacerRegistry, executionCompiler, idAllocator,
+ enableSpeculativeExploration ? _nodeLinker : null);
}
public InstructionsFeeder InstructionsFeeder { get; }
+ /// Persistent graph index spanning all nodes (observed + speculative).
+ public CfgNodeIndex NodeIndex => InstructionsFeeder.NodeIndex;
+
+ ///
+ /// Seeds known-safe interrupt handler entry points for speculative exploration.
+ /// No-op when speculative exploration is disabled.
+ ///
+ public void SeedKnownSafeHandlers(IReadOnlyList handlerAddresses) =>
+ InstructionsFeeder.SeedKnownSafeHandlers(handlerAddresses);
+
///
/// Parses or retrieves from cache the instruction at the current IP from memory.
/// May trigger SignatureReducer and InstructionReplacerRegistry side effects.
@@ -92,6 +106,18 @@ private ICfgNode ReconcileGraphWithMemory(ExecutionContext executionContext, ICf
$"From graph: {graphNodeAfterReconciliation}");
}
+ // Provenance-gated path: when the stale graph node is speculative, either promote it
+ // (memory match) or discard-and-replace (mismatch). This must run BEFORE the generic
+ // SelectorNode path so that speculative nodes never enter a SelectorNode. Reconciliation is
+ // owned entirely by InstructionsFeeder (which holds the reconciler); we only ask it to
+ // resolve this specific graph node.
+ if (graphNodeAfterReconciliation is CfgInstruction speculativeInGraph && speculativeInGraph.IsSpeculative) {
+ if (InstructionsFeeder.TryPromoteStaleSpeculativeNode(speculativeInGraph, fromMemory)) {
+ return speculativeInGraph;
+ }
+ return fromMemory;
+ }
+
// Genuinely different instructions at the same address. Inject a SelectorNode
return _nodeLinker.CreateSelectorNodeBetween(fromMemory, (CfgInstruction)graphNodeAfterReconciliation);
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeIndex.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeIndex.cs
new file mode 100644
index 0000000000..9eff5316cb
--- /dev/null
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeIndex.cs
@@ -0,0 +1,132 @@
+namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Utils;
+
+///
+/// Persistent graph index grouping nodes by address, spanning all nodes (observed and speculative).
+/// Unlike the hot memory caches / ,
+/// this index has no memory-state semantics and no eviction on write: it is a durable lookup table
+/// for the CFG explorer and the cold-path promotion logic.
+///
+/// Nodes are stored per address by identity (a node has a unique ).
+/// The index deliberately does NOT key on : a signature's equality
+/// ( ) treats null bytes
+/// as wildcards and is therefore non-transitive, which makes it unsuitable as a hash/dictionary key;
+/// and a node's computed signature changes when the signature reducer nullifies non-final fields,
+/// which would leave a snapshot key stale. Signature-based queries instead scan the small per-address
+/// list and read each node's live signature (see ).
+///
+/// Implements so that signature-reducer fan-out via
+/// keeps the index coherent: when two instructions are
+/// merged the old node is replaced by the surviving one.
+///
+public class CfgNodeIndex : InstructionReplacer {
+ private readonly Dictionary> _index = new();
+
+ ///
+ /// Set of addresses proven byte-unstable between explore-time and execution-time.
+ /// Speculation stops permanently at poisoned addresses.
+ ///
+ public HashSet PoisonSet { get; } = new();
+
+ public CfgNodeIndex(InstructionReplacerRegistry replacerRegistry) : base(replacerRegistry) {
+ }
+
+ ///
+ /// Inserts into the index.
+ /// Idempotent by node identity: inserting a node already present at its address is a no-op.
+ ///
+ public void Insert(CfgInstruction node) {
+ if (!_index.TryGetValue(node.Address, out List? atAddress)) {
+ atAddress = new List();
+ _index[node.Address] = atAddress;
+ }
+ if (!atAddress.Contains(node)) {
+ atAddress.Add(node);
+ }
+ }
+
+ ///
+ /// Removes from the index. Does not touch graph edges.
+ ///
+ public void Remove(CfgInstruction node) {
+ DictionaryUtils.RemoveFromCollection(_index, node.Address, node);
+ }
+
+ ///
+ /// Returns all nodes indexed at , or an empty enumerable if none.
+ ///
+ public IEnumerable GetAtAddress(SegmentedAddress address) {
+ if (!_index.TryGetValue(address, out List? atAddress)) {
+ return [];
+ }
+ return atAddress;
+ }
+
+ ///
+ /// Returns whether the index has any entry at .
+ ///
+ public bool HasAddress(SegmentedAddress address) => _index.ContainsKey(address);
+
+ ///
+ /// Returns whether (by identity) is currently indexed at its address.
+ /// A node that has been removed is no longer present.
+ ///
+ public bool Contains(CfgInstruction node) =>
+ _index.TryGetValue(node.Address, out List? atAddress) && atAddress.Contains(node);
+
+ ///
+ /// Returns the node at whose final-field signature matches
+ /// , or null when none matches. An observed node is
+ /// preferred over a speculative one when both match.
+ /// Matching on (grammar + opcode, never
+ /// nullified by signature reduction) answers "is this the same instruction?": different opcodes
+ /// do not match (so distinct self-modified variants stay separate), while instructions differing
+ /// only in a non-final field (e.g. an immediate) do match and converge, leaving the value to the
+ /// reduction machinery. The comparison reads each node's live .
+ ///
+ public CfgInstruction? GetAtAddressMatchingFinalSignature(SegmentedAddress address, Signature signatureFinal) {
+ CfgInstruction? speculativeMatch = null;
+ foreach (CfgInstruction node in GetAtAddress(address)) {
+ if (!node.SignatureFinal.Equals(signatureFinal)) {
+ continue;
+ }
+ if (!node.IsSpeculative) {
+ return node;
+ }
+ speculativeMatch ??= node;
+ }
+ return speculativeMatch;
+ }
+
+ ///
+ /// Handles signature-reducer fan-out: replaces in the index
+ /// with . Matching is by node identity, so it is unaffected by
+ /// signature changes. No-op when is not indexed.
+ ///
+ public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction newInstruction) {
+ if (!_index.TryGetValue(oldInstruction.Address, out List? atAddress)) {
+ return;
+ }
+ if (!atAddress.Remove(oldInstruction)) {
+ return;
+ }
+ if (!atAddress.Contains(newInstruction)) {
+ atAddress.Add(newInstruction);
+ }
+ if (atAddress.Count == 0) {
+ _index.Remove(oldInstruction.Address);
+ }
+ }
+
+ ///
+ /// Handles removal fan-out: evicts from the index. Delegates to
+ /// the existing logic.
+ ///
+ public override void RemoveInstruction(CfgInstruction instruction) {
+ Remove(instruction);
+ }
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs
index 3b92e38c4f..2c3731a405 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs
@@ -51,6 +51,17 @@ public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstru
}
}
+ ///
+ /// Handles removal fan-out: if is the current entry at its
+ /// address, evicts it and unregisters its memory-write breakpoints. No-op when the instruction
+ /// is not the current entry.
+ ///
+ public override void RemoveInstruction(CfgInstruction instruction) {
+ if (GetAtAddress(instruction.Address) == instruction) {
+ ClearCurrentInstruction(instruction);
+ }
+ }
+
public void SetAsCurrent(CfgInstruction instruction) {
// Clear it because in some cases it can be added twice (signature reducer)
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/IInstructionReplacer.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/IInstructionReplacer.cs
index c441fa4f7c..ce98c09746 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/IInstructionReplacer.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/IInstructionReplacer.cs
@@ -4,4 +4,11 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
public interface IInstructionReplacer {
void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction newInstruction);
-}
\ No newline at end of file
+
+ ///
+ /// Removes from the subscriber's state. Inverse of
+ /// : the single fan-out point every identity-level removal is
+ /// routed through, so each subscriber cleans up its own reference to the node.
+ ///
+ void RemoveInstruction(CfgInstruction instruction);
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs
index c30086c996..99a4d3bf9a 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs
@@ -7,4 +7,5 @@ protected InstructionReplacer(InstructionReplacerRegistry replacerRegistry) {
replacerRegistry.Register(this);
}
public abstract void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction newInstruction);
+ public abstract void RemoveInstruction(CfgInstruction instruction);
}
\ No newline at end of file
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs
index 31d3903f99..ea24831e25 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs
@@ -14,4 +14,10 @@ public void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction new
instructionReplacer.ReplaceInstruction(oldInstruction, newInstruction);
}
}
+
+ public void RemoveInstruction(CfgInstruction instruction) {
+ foreach (InstructionReplacer instructionReplacer in _replacers) {
+ instructionReplacer.RemoveInstruction(instruction);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs
index a2ecc68b17..9eee101609 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs
@@ -2,12 +2,14 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
using Spice86.Core.Emulator.CPU.CfgCpu.InstructionExecutor.Expressions;
+using Spice86.Core.Emulator.CPU.CfgCpu.Linker;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
using Spice86.Core.Emulator.CPU.CfgCpu.Parser;
using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.VM.Breakpoint;
using Spice86.Shared.Emulator.Memory;
+using System.Linq;
using System.Runtime.CompilerServices;
using SequentialIdAllocator = Spice86.Shared.Utils.SequentialIdAllocator;
@@ -23,18 +25,45 @@ public class InstructionsFeeder : IClearable {
private readonly InstructionParser _instructionParser;
private readonly SignatureReducer _signatureReducer;
private readonly CfgNodeExecutionCompiler _executionCompiler;
+ private readonly SpeculativeExplorer? _speculativeExplorer;
+ private readonly SpeculativeReconciler? _speculativeReconciler;
public InstructionsFeeder(EmulatorBreakpointsManager emulatorBreakpointsManager, IMemory memory, State cpuState,
- InstructionReplacerRegistry replacerRegistry, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator) {
+ InstructionReplacerRegistry replacerRegistry, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator,
+ NodeLinker? nodeLinker) {
_instructionParser = new(memory, cpuState, idAllocator);
_executionCompiler = executionCompiler;
CurrentInstructions = new(memory, emulatorBreakpointsManager, replacerRegistry);
PreviousInstructions = new(memory, replacerRegistry);
_signatureReducer = new(replacerRegistry);
+ NodeIndex = new(replacerRegistry);
+ if (nodeLinker is not null) {
+ _speculativeExplorer = new(_instructionParser, NodeIndex, nodeLinker);
+ SpeculativePromoter promoter = new(executionCompiler, CurrentInstructions, PreviousInstructions);
+ SpeculativeReachabilityPruner pruner = new(replacerRegistry);
+ _speculativeReconciler = new(promoter, pruner, _signatureReducer, NodeIndex);
+ }
}
public CurrentInstructions CurrentInstructions { get; }
public PreviousInstructions PreviousInstructions { get; }
+ public CfgNodeIndex NodeIndex { get; }
+
+ ///
+ /// Seeds known-safe interrupt handler entry points for speculative exploration with
+ /// continuation-following enabled. Each address is decoded speculatively (if not already
+ /// in the index) and its full handler body is explored including post-call/int continuations.
+ /// No-op when the speculative explorer is disabled.
+ ///
+ /// Entry addresses of emulator-installed interrupt handlers.
+ public void SeedKnownSafeHandlers(IReadOnlyList handlerAddresses) {
+ if (_speculativeExplorer is null) {
+ return;
+ }
+ foreach (SegmentedAddress entry in handlerAddresses) {
+ _speculativeExplorer.SeedKnownSafe(entry);
+ }
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CfgInstruction GetInstructionFromMemory(SegmentedAddress address) {
@@ -62,20 +91,79 @@ private CfgInstruction GetFromPreviousOrParse(SegmentedAddress address) {
return previousMatching;
}
- return ParseAndSetAsCurrent(address);
+ return ParseCheckingExistingSpeculative(address);
}
- private CfgInstruction ParseAndSetAsCurrent(SegmentedAddress address) {
+
+ private CfgInstruction ParseCheckingExistingSpeculative(SegmentedAddress address) {
+ if (_speculativeReconciler is null) {
+ return ParseAndSetAsCurrent(address);
+ }
+ CfgInstruction? existingSpeculative = NodeIndex.GetAtAddress(address)
+ .FirstOrDefault(n => n.IsSpeculative);
+ if (existingSpeculative is null) {
+ return ParseAndSetAsCurrent(address);
+ }
+ // Light parse only for signature comparison — avoids SignatureReducer fan-out
+ // and discards the node cheaply on the fast path (promotion).
+ CfgInstruction parsed = _instructionParser.ParseInstructionAt(address);
+ // Reconcile against the speculative variant whose final signature matches the live bytes.
+ // Self-modified code can leave several speculative variants (different opcodes) at one
+ // address; reconciling an arbitrary one would sweep it and permanently poison the address on
+ // a signature mismatch even when a sibling variant matches memory. When no variant matches
+ // the live bytes it is a genuine divergence, so the arbitrary variant is kept and the
+ // mismatch sweeps and poisons the address as before.
+ CfgInstruction? matchingSpeculative = NodeIndex.GetAtAddressMatchingFinalSignature(address, parsed.SignatureFinal);
+ if (matchingSpeculative is { IsSpeculative: true }) {
+ existingSpeculative = matchingSpeculative;
+ }
+ if (_speculativeReconciler.Reconcile(existingSpeculative, parsed, address)) {
+ return existingSpeculative;
+ }
+ // Signatures differ: run the full uniqueness check (signature reduction against
+ // previous instructions) so we reuse an existing node when possible.
+ CfgInstruction live = ReduceAgainstPrevious(parsed, address);
+ CommitObserved(live);
+ return live;
+ }
+
+ ///
+ /// Reconciles a stale speculative node that the graph wants to execute against the live
+ /// instruction parsed from memory. This is the graph-driven counterpart to the promote-on-parse
+ /// path in ; both live here so reconciliation has a
+ /// single owning class rather than being split between the two feeders.
+ /// Returns true when the node was promoted and the caller should keep executing it;
+ /// false when it was swept, diverged, or speculation is disabled and the caller should use
+ /// the live node instead.
+ ///
+ public bool TryPromoteStaleSpeculativeNode(CfgInstruction speculativeGraphNode, CfgInstruction liveInstruction) {
+ // GetInstructionFromMemory already reconciled the memory-matching variant at this address on
+ // its way here. Only reconcile when this specific variant survived that pass (still indexed);
+ // otherwise it was already resolved and there is nothing left to do.
+ if (_speculativeReconciler is null || !NodeIndex.Contains(speculativeGraphNode)) {
+ return false;
+ }
+ return _speculativeReconciler.Reconcile(speculativeGraphNode, liveInstruction, liveInstruction.Address);
+ }
+ internal CfgInstruction ParseAndSetAsCurrent(SegmentedAddress address) {
CfgInstruction parsed = ParseEnsuringUnique(address);
- // Recompile instruction
- _executionCompiler.Compile(parsed);
- CurrentInstructions.SetAsCurrent(parsed);
- PreviousInstructions.AddInstructionInPrevious(parsed);
+ CommitObserved(parsed);
+ _speculativeExplorer?.ExploreFrom(parsed);
return parsed;
}
- private CfgInstruction ParseEnsuringUnique(SegmentedAddress address) {
+ private void CommitObserved(CfgInstruction instruction) {
+ _executionCompiler.Compile(instruction);
+ CurrentInstructions.SetAsCurrent(instruction);
+ PreviousInstructions.AddInstructionInPrevious(instruction);
+ NodeIndex.Insert(instruction);
+ }
+
+ internal CfgInstruction ParseEnsuringUnique(SegmentedAddress address) {
CfgInstruction parsed = _instructionParser.ParseInstructionAt(address);
- // Let's try with the signature reducer to see if the parsed instruction has an existing match
+ return ReduceAgainstPrevious(parsed, address);
+ }
+
+ private CfgInstruction ReduceAgainstPrevious(CfgInstruction parsed, SegmentedAddress address) {
HashSet? previousSet = PreviousInstructions.GetAtAddress(address);
if (previousSet != null) {
foreach (CfgInstruction existing in previousSet) {
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs
index 84eb32f97d..2a805d4872 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs
@@ -3,6 +3,7 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
using Spice86.Core.Emulator.Memory;
using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Utils;
using System.Linq;
///
@@ -48,6 +49,14 @@ public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstru
}
}
+ ///
+ /// Handles removal fan-out: removes from the historical set at its
+ /// address, dropping the address key when the set empties. No-op when the instruction is absent.
+ ///
+ public override void RemoveInstruction(CfgInstruction instruction) {
+ DictionaryUtils.RemoveFromCollection(_previousInstructionsAtAddress, instruction.Address, instruction);
+ }
+
public void AddInstructionInPrevious(CfgInstruction instruction) {
SegmentedAddress instructionAddress = instruction.Address;
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeExplorer.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeExplorer.cs
new file mode 100644
index 0000000000..d495246533
--- /dev/null
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeExplorer.cs
@@ -0,0 +1,192 @@
+namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Linker;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.Parser;
+using Spice86.Shared.Emulator.Memory;
+
+using System.Linq;
+
+///
+/// Performs static recursive-descent exploration of the CFG along statically-knowable successor
+/// edges, starting from a freshly observed instruction. All nodes created here are marked
+/// = true and inserted into
+/// ; they are never placed in or
+/// .
+///
+/// The explorer emits a fully-formed live graph via :
+/// edges and blocks are built at explore time using the same reconciliation path as observed edges.
+/// On promotion the edges remain in place (in-place flip); on mismatch the pruner sweeps real edges.
+///
+/// The explorer stops at:
+///
+/// Addresses already holding a node with the same final-field signature (observed or
+/// previously speculated): a convergence edge is wired from the predecessor to that existing node.
+/// A node with a different opcode at the same address (self-modified variant) does not converge;
+/// a distinct variant is minted instead.
+/// Addresses in the .
+/// Instructions with no static successors (indirect jumps, RET, IRET, invalid).
+/// CALL successors: the callee entry is enqueued but call-continuations are not
+/// (unless under known-safe trust).
+///
+///
+/// Known-safe seeding ( ): seeds emulator-installed interrupt
+/// handler entry points with continuation-following enabled. Under trust, call/int instructions get
+/// an eagerly-wired continuation edge to their
+/// (computed from instruction length, never by
+/// decoding through a possibly-switchable operand). Trust propagates along intra-routine successors
+/// but drops into callees.
+///
+public class SpeculativeExplorer {
+ ///
+ /// Per-worklist-item data carrying the target address, predecessor, edge type, and trust flag.
+ /// Trust ( ) propagates along intra-routine successors (Normal edges)
+ /// but is dropped into callees. Only trusted items eagerly wire
+ /// continuation edges past call/int instructions.
+ ///
+ private readonly record struct ExploreItem(
+ SegmentedAddress Target,
+ CfgInstruction? Predecessor,
+ InstructionSuccessorType EdgeType,
+ bool FollowContinuations);
+
+ private readonly InstructionParser _instructionParser;
+ private readonly CfgNodeIndex _nodeIndex;
+ private readonly NodeLinker _nodeLinker;
+
+ public SpeculativeExplorer(InstructionParser instructionParser, CfgNodeIndex nodeIndex, NodeLinker nodeLinker) {
+ _instructionParser = instructionParser;
+ _nodeIndex = nodeIndex;
+ _nodeLinker = nodeLinker;
+ }
+
+ ///
+ /// Enqueues all statically-reachable successors of that are not
+ /// yet in the index or poison set, then drains the worklist by speculatively decoding each
+ /// address in turn. Wires full successor/predecessor edges with block reconciliation between
+ /// each predecessor and its explored target via .
+ /// All items are untrusted (no continuation-following).
+ ///
+ public void ExploreFrom(CfgInstruction observed) {
+ Queue worklist = new();
+ EnqueueStaticSuccessors(observed, worklist, followContinuations: false);
+ Drain(worklist);
+ }
+
+ ///
+ /// Seeds a known-safe interrupt handler entry point for speculative exploration with
+ /// continuation-following enabled. Under trust, the explorer eagerly wires
+ /// edges past call/int instructions,
+ /// allowing the full handler body to be decoded including post-call continuations.
+ /// No-op if the address is already in the index (handler already explored or observed).
+ ///
+ /// Handler entry address to seed.
+ public void SeedKnownSafe(SegmentedAddress entry) {
+ if (_nodeIndex.PoisonSet.Contains(entry)) {
+ return;
+ }
+ if (_nodeIndex.HasAddress(entry)) {
+ return;
+ }
+ Queue worklist = new();
+ worklist.Enqueue(new ExploreItem(entry, null, InstructionSuccessorType.Normal, FollowContinuations: true));
+ Drain(worklist);
+ }
+
+ // ponytail: this uses a queue, not real recursion, so it cannot overflow the stack.
+ // One Drain call decodes all connected code reachable from its start, in one pass.
+ // This is finite. Each decoded address is added to _nodeIndex, so it is decoded only
+ // once for the whole run. Later visits find that node and stop. Invalid bytes are
+ // poisoned and stop too. We also stop at RET/IRET/indirect jumps/call continuations,
+ // so random bytes stop fast. This only runs the first time we see code (cache miss),
+ // which happens during disk loading, so the cost is hidden.
+ // If it is ever too slow: limit the number of items per call, and continue on the next visit.
+ private void Drain(Queue worklist) {
+ while (worklist.Count > 0) {
+ ExploreItem item = worklist.Dequeue();
+ SegmentedAddress address = item.Target;
+ if (_nodeIndex.PoisonSet.Contains(address)) {
+ continue;
+ }
+ CfgInstruction candidate = _instructionParser.ParseInstructionAt(address);
+ if (candidate.IsInvalid) {
+ _nodeIndex.PoisonSet.Add(address);
+ continue;
+ }
+ // Converge only onto a node that is the SAME instruction (same final-field signature)
+ // as the bytes currently in memory. Matching by address alone would wire a predecessor
+ // to an unrelated self-modified variant living at the same address.
+ CfgInstruction? existing = _nodeIndex.GetAtAddressMatchingFinalSignature(address, candidate.SignatureFinal);
+ if (existing is not null) {
+ if (item.Predecessor is not null) {
+ _nodeLinker.Link(item.EdgeType, item.Predecessor, existing);
+ }
+ continue;
+ }
+ candidate.SetSpeculative(true);
+ _nodeIndex.Insert(candidate);
+ if (item.Predecessor is not null) {
+ _nodeLinker.Link(item.EdgeType, item.Predecessor, candidate);
+ }
+ EnqueueSuccessorsForNode(candidate, worklist, item.FollowContinuations);
+ }
+ }
+
+ ///
+ /// Classifies and enqueues successors of according to its instruction kind
+ /// and the current trust level. Under trust, call/int instructions additionally get their
+ /// continuation address enqueued as a item.
+ /// For callback instructions (Kind=None, MaxSuccessorsCount=null) under trust, the continuation
+ /// is enqueued as a Normal successor since the parser does not register static successors for them.
+ ///
+ private void EnqueueSuccessorsForNode(CfgInstruction node, Queue worklist, bool followContinuations) {
+ if (node.IsCall) {
+ // Call or INT: static successors are callees (enqueued untrusted, always Normal edges).
+ // Continuation is only enqueued if under trust.
+ foreach (SegmentedAddress successor in node.StaticSuccessorAddresses.Where(s => !_nodeIndex.PoisonSet.Contains(s))) {
+ worklist.Enqueue(new ExploreItem(successor, node, InstructionSuccessorType.Normal, FollowContinuations: false));
+ }
+ if (followContinuations) {
+ EnqueueContinuation(node, worklist);
+ }
+ return;
+ }
+ if (node.StaticSuccessorAddresses.Count > 0) {
+ // Non-call instructions with known static successors: propagate trust through intra-routine successors.
+ // Static successors are always Normal edges.
+ EnqueueStaticSuccessors(node, worklist, followContinuations);
+ return;
+ }
+ // No static successors and not a call. This could be:
+ // - A callback instruction (Kind=None, MaxSuccessorsCount=null): its continuation is the next
+ // instruction in memory but the parser doesn't register it because IsBlockTerminator is true.
+ // - A RET/IRET: no continuation (natural hard-stop).
+ // - An indirect jump: no continuation (natural hard-stop).
+ // Under trust, if the instruction is NOT a return/jump/invalid, follow its memory continuation.
+ if (followContinuations && !node.IsReturn && !node.IsJump && !node.IsInvalid) {
+ SegmentedAddress continuation = node.NextInMemoryAddress32.ToSegmentedAddress();
+ if (!_nodeIndex.PoisonSet.Contains(continuation)) {
+ worklist.Enqueue(new ExploreItem(continuation, node, InstructionSuccessorType.Normal, followContinuations));
+ }
+ }
+ }
+
+ ///
+ /// Enqueues the call/int continuation address (NextInMemoryAddress32) as a CallToReturn item.
+ /// The continuation address is computed from instruction length, never by decoding the operand.
+ ///
+ private void EnqueueContinuation(CfgInstruction callNode, Queue worklist) {
+ SegmentedAddress continuation = callNode.NextInMemoryAddress32.ToSegmentedAddress();
+ if (_nodeIndex.PoisonSet.Contains(continuation)) {
+ return;
+ }
+ worklist.Enqueue(new ExploreItem(continuation, callNode, InstructionSuccessorType.CallToReturn, FollowContinuations: true));
+ }
+
+ private void EnqueueStaticSuccessors(CfgInstruction instruction, Queue worklist, bool followContinuations) {
+ foreach (SegmentedAddress successor in instruction.StaticSuccessorAddresses.Where(s => !_nodeIndex.PoisonSet.Contains(s))) {
+ worklist.Enqueue(new ExploreItem(successor, instruction, InstructionSuccessorType.Normal, followContinuations));
+ }
+ }
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativePromoter.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativePromoter.cs
new file mode 100644
index 0000000000..eff5f2890b
--- /dev/null
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativePromoter.cs
@@ -0,0 +1,37 @@
+namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.InstructionExecutor.Expressions;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+
+///
+/// Promotes a speculative to observed status in place.
+///
+/// Promotion flips to false , compiles the
+/// node, installs its SMC write-breakpoints via , and
+/// adds it to so future hot-cache lookups find it. The node's
+/// out-edges (still speculative hints) are left intact; they will be promoted or discarded lazily
+/// as execution visits them.
+///
+public class SpeculativePromoter {
+ private readonly CfgNodeExecutionCompiler _executionCompiler;
+ private readonly CurrentInstructions _currentInstructions;
+ private readonly PreviousInstructions _previousInstructions;
+
+ public SpeculativePromoter(CfgNodeExecutionCompiler executionCompiler, CurrentInstructions currentInstructions, PreviousInstructions previousInstructions) {
+ _executionCompiler = executionCompiler;
+ _currentInstructions = currentInstructions;
+ _previousInstructions = previousInstructions;
+ }
+
+ ///
+ /// Promotes in place: marks it observed, compiles it, and
+ /// registers it in both instruction caches. Returns the same node (now observed).
+ ///
+ public CfgInstruction Promote(CfgInstruction speculative) {
+ speculative.SetSpeculative(false);
+ _executionCompiler.Compile(speculative);
+ _currentInstructions.SetAsCurrent(speculative);
+ _previousInstructions.AddInstructionInPrevious(speculative);
+ return speculative;
+ }
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReachabilityPruner.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReachabilityPruner.cs
new file mode 100644
index 0000000000..b5b3a95fb6
--- /dev/null
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReachabilityPruner.cs
@@ -0,0 +1,97 @@
+namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+
+using System.Linq;
+
+///
+/// Performs the reachability sweep that removes speculative nodes once a speculative node is
+/// proven wrong and must be discarded.
+///
+/// Given a speculative that must be discarded, the sweep:
+///
+///
+/// Forward-DFS from the discard root, collecting all speculative nodes reachable from it (candidate set C).
+/// Stops at non-speculative nodes (observed boundary).
+///
+///
+/// Computes the survivor set S (subset of C): nodes still reachable from a non-speculative predecessor
+/// via a path that does not pass through the discard root.
+///
+///
+/// Removes C \ S by fanning each node out through ,
+/// so every subscriber (index, linker/block teardown, entry-point roots, memory caches) cleans up its own state.
+///
+///
+///
+/// Speculative in-edges to the discarded root are dropped, not redirected.
+///
+public class SpeculativeReachabilityPruner {
+ private readonly InstructionReplacerRegistry _replacerRegistry;
+
+ public SpeculativeReachabilityPruner(InstructionReplacerRegistry replacerRegistry) {
+ _replacerRegistry = replacerRegistry;
+ }
+
+ ///
+ /// Discards and all speculative nodes exclusively reachable
+ /// through it. Returns the set of removed nodes.
+ ///
+ public HashSet Sweep(CfgInstruction discardRoot) {
+ HashSet candidates = CollectCandidates(discardRoot);
+ HashSet survivors = ComputeSurvivors(candidates, discardRoot);
+ HashSet toRemove = new(candidates);
+ toRemove.ExceptWith(survivors);
+ RemoveNodes(toRemove);
+ return toRemove;
+ }
+
+ private HashSet CollectCandidates(CfgInstruction discardRoot) {
+ HashSet visited = new();
+ Queue queue = new();
+ queue.Enqueue(discardRoot);
+ while (queue.Count > 0) {
+ ICfgNode node = queue.Dequeue();
+ if (!node.IsSpeculative || !visited.Add(node)) {
+ continue;
+ }
+ foreach (ICfgNode successor in node.Successors) {
+ queue.Enqueue(successor);
+ }
+ }
+ return visited;
+ }
+
+ private HashSet ComputeSurvivors(HashSet candidates, ICfgNode discardRoot) {
+ HashSet survivors = new();
+ Queue queue = new();
+ foreach (ICfgNode candidate in candidates) {
+ if (candidate.Equals(discardRoot)) {
+ continue;
+ }
+ foreach (ICfgNode predecessor in candidate.Predecessors) {
+ if (!candidates.Contains(predecessor) && !predecessor.Equals(discardRoot)) {
+ queue.Enqueue(candidate);
+ break;
+ }
+ }
+ }
+ while (queue.Count > 0) {
+ ICfgNode node = queue.Dequeue();
+ if (!candidates.Contains(node) || node.Equals(discardRoot) || !survivors.Add(node)) {
+ continue;
+ }
+ foreach (ICfgNode successor in node.Successors.Where(candidates.Contains)) {
+ queue.Enqueue(successor);
+ }
+ }
+ return survivors;
+ }
+
+ private void RemoveNodes(HashSet toRemove) {
+ foreach (CfgInstruction instruction in toRemove.OfType()) {
+ _replacerRegistry.RemoveInstruction(instruction);
+ }
+ }
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReconciler.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReconciler.cs
new file mode 100644
index 0000000000..69bd81b6e3
--- /dev/null
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReconciler.cs
@@ -0,0 +1,77 @@
+namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+
+///
+/// Encapsulates the reconciliation decision for speculative nodes.
+/// When a pre-existing speculative node at a given address is encountered during execution,
+/// we compare its signature to the live memory signature:
+///
+/// Byte-for-byte match: the speculative node is promoted in place.
+/// Same opcode/grammar, only a non-final field differs (e.g. a self-modified
+/// immediate): the difference is reducible, so the speculative node is merged with the live
+/// instruction via and the surviving node is promoted. This keeps
+/// the speculatively-explored subgraph hanging off the node instead of throwing it away.
+/// Different opcode/grammar: a genuine divergence, so the speculative node is
+/// swept and the address is poisoned to stop future speculation.
+///
+///
+public class SpeculativeReconciler {
+ private readonly SpeculativePromoter _promoter;
+ private readonly SpeculativeReachabilityPruner _pruner;
+ private readonly SignatureReducer _signatureReducer;
+ private readonly CfgNodeIndex _nodeIndex;
+
+ public SpeculativeReconciler(SpeculativePromoter promoter, SpeculativeReachabilityPruner pruner,
+ SignatureReducer signatureReducer, CfgNodeIndex nodeIndex) {
+ _promoter = promoter;
+ _pruner = pruner;
+ _signatureReducer = signatureReducer;
+ _nodeIndex = nodeIndex;
+ }
+
+ ///
+ /// Reconciles a pre-existing speculative node with the live memory state.
+ /// Returns true if the node was promoted (caller should use it directly).
+ /// Returns false if the node was swept (caller should use the live replacement node
+ /// instead). On a genuine divergence the address is also poisoned to stop future speculation.
+ ///
+ /// The speculative node currently in the graph at the address.
+ /// The live (parsed/observed) instruction at the same address.
+ /// The segmented address being reconciled.
+ public bool Reconcile(CfgInstruction speculativeNode, CfgInstruction liveInstruction, SegmentedAddress address) {
+ if (speculativeNode.Signature.ListEquivalent(liveInstruction.Signature.SignatureValue)) {
+ _promoter.Promote(speculativeNode);
+ return true;
+ }
+ // Same opcode/grammar, only a non-final field diverges (e.g. a self-modified immediate):
+ // reducible. Merge instead of sweeping so the speculatively-explored subgraph survives.
+ // The speculative node is passed first so the reducer keeps it as the survivor (it is the
+ // first uncompiled instruction); its diverging field is nullified and it is promoted in place.
+ // The live instruction is a throwaway parse not registered anywhere, so replacing it is a no-op
+ // for every replacer subscriber.
+ bool sameFinalSignature = speculativeNode.SignatureFinal.Equals(liveInstruction.SignatureFinal);
+ if (sameFinalSignature) {
+ CfgInstruction? reduced = _signatureReducer.ReduceToOne(speculativeNode, liveInstruction);
+ if (reduced is not null) {
+ _promoter.Promote(reduced);
+ return true;
+ }
+ }
+ // A genuine divergence: the speculative decode is wrong for the live bytes. Sweep the node
+ // and its exclusively-reachable speculative subgraph. Reconcile runs at most once per node
+ // (the graph-driven caller checks the index before delegating here; the parse-driven caller
+ // passes a freshly-indexed node), so the sweep never runs twice on the same node.
+ _pruner.Sweep(speculativeNode);
+ // Poison only on a genuine divergence: a different opcode/grammar (final-field signatures
+ // differ) is a real conflict. A reducible same-opcode variant is handled above, so reaching
+ // here with matching final signatures means reduction was not possible; leave the address
+ // un-poisoned so speculation can resume rather than killing the benefit there permanently.
+ if (!sameFinalSignature) {
+ _nodeIndex.PoisonSet.Add(address);
+ }
+ return false;
+ }
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs
index 304124bce2..4719415189 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs
@@ -15,10 +15,12 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Linker;
public class NodeLinker : InstructionReplacer {
private readonly CfgNodeExecutionCompiler _executionCompiler;
private readonly SequentialIdAllocator _idAllocator;
+ private readonly SpeculativeReachabilityPruner _speculativePruner;
public NodeLinker(InstructionReplacerRegistry replacerRegistry, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator) : base(replacerRegistry) {
_executionCompiler = executionCompiler;
_idAllocator = idAllocator;
+ _speculativePruner = new SpeculativeReachabilityPruner(replacerRegistry);
}
///
@@ -75,13 +77,46 @@ private ICfgNode LinkCfgInstructionWithType(InstructionSuccessorType linkToNextT
}
if (!shouldBeNext.Equals(next)) {
- return ResolveSuccessorConflict(current, next, shouldBeNext);
+ return ResolveSuccessorConflict(linkToNextType, current, next, shouldBeNext);
+ }
+ // Edge to this exact node already exists.
+ bool speculativelyWired = current.SpeculativelyWiredSuccessors.Contains(next);
+ if (current.SuccessorsPerType.TryGetValue(linkToNextType, out ISet? existingForType)
+ && existingForType.Contains(next)) {
+ // Already recorded under the requested type. If an observed link (target no longer
+ // speculative) re-traverses an edge the explorer wired speculatively, the type is now
+ // observed-confirmed, so drop the speculative provenance.
+ if (speculativelyWired && !next.IsSpeculative) {
+ current.SpeculativelyWiredSuccessors.Remove(next);
+ }
+ return next;
+ }
+ // A different successor type is requested for an existing edge. Only override the recorded
+ // type when the edge was wired speculatively (an explorer guess, typically a Normal
+ // fallthrough) and observed execution now reaches that same target via a different type
+ // (e.g. a fault handler returning to the next instruction as CallToReturn). Genuinely
+ // observed edges (e.g. a real fallthrough into a block that is also a return target via a
+ // different predecessor) keep their first-recorded type, so each (source,target) edge carries
+ // exactly one type. Successors.Count is unchanged, so CanHaveMoreSuccessors is unaffected.
+ if (speculativelyWired && !next.IsSpeculative) {
+ RetypeSuccessor(current, linkToNextType, next);
+ current.SpeculativelyWiredSuccessors.Remove(next);
}
return next;
}
private void AttachNewLink(InstructionSuccessorType linkToNextType, CfgInstruction current, ICfgNode next) {
AttachToNext(current, next);
+ RecordSuccessorType(current, linkToNextType, next);
+ // Edges the explorer wires to a still-speculative target carry a speculative type guess; record
+ // their provenance so a later observed link can override the type. Observed links (target not
+ // speculative) create real edges and are never marked.
+ if (next.IsSpeculative) {
+ current.SpeculativelyWiredSuccessors.Add(next);
+ }
+ }
+
+ private static void RecordSuccessorType(CfgInstruction current, InstructionSuccessorType linkToNextType, ICfgNode next) {
if (!current.SuccessorsPerType.TryGetValue(linkToNextType, out ISet? successorsForType)) {
successorsForType = new HashSet();
current.SuccessorsPerType[linkToNextType] = successorsForType;
@@ -89,12 +124,33 @@ private void AttachNewLink(InstructionSuccessorType linkToNextType, CfgInstructi
successorsForType.Add(next);
}
- private ICfgNode ResolveSuccessorConflict(CfgInstruction current, ICfgNode next, ICfgNode shouldBeNext) {
+ private static void RetypeSuccessor(CfgInstruction current, InstructionSuccessorType linkToNextType, ICfgNode next) {
+ // Drop the target from every existing type bucket so it ends up under exactly one type.
+ foreach (ISet bucket in current.SuccessorsPerType.Values) {
+ bucket.Remove(next);
+ }
+ RecordSuccessorType(current, linkToNextType, next);
+ }
+
+ private ICfgNode ResolveSuccessorConflict(InstructionSuccessorType linkToNextType, CfgInstruction current, ICfgNode next, ICfgNode shouldBeNext) {
if (shouldBeNext is SelectorNode selectorNode) {
LinkSelectorNode(selectorNode, next);
return selectorNode;
}
- return CreateSelectorNodeBetween((CfgInstruction)shouldBeNext, (CfgInstruction)next);
+ CfgInstruction existing = (CfgInstruction)shouldBeNext;
+ if (existing.IsSpeculative) {
+ // Discard the stale speculative node and its exclusively-reachable speculative subgraph
+ // (same machinery SpeculativeReconciler uses on a divergence), not just the incoming edge:
+ // an unswept node lingers in the index/graph and gets exported as dead-but-guarded code.
+ // The sweep detaches the current -> existing edge as part of removing existing.
+ _speculativePruner.Sweep(existing);
+ AttachNewLink(linkToNextType, current, next);
+ return next;
+ }
+ if (next is CfgInstruction nextInstr && nextInstr.IsSpeculative) {
+ return existing;
+ }
+ return CreateSelectorNodeBetween(existing, (CfgInstruction)next);
}
///
@@ -112,6 +168,11 @@ private ICfgNode ResolveSuccessorConflict(CfgInstruction current, ICfgNode next,
/// selector → variant edge fires.
///
public SelectorNode CreateSelectorNodeBetween(CfgInstruction instruction1, CfgInstruction instruction2) {
+ if (instruction1.IsSpeculative || instruction2.IsSpeculative) {
+ throw new UnhandledCfgDiscrepancyException(
+ $"CreateSelectorNodeBetween must never be called with a speculative operand. " +
+ $"instruction1.IsSpeculative={instruction1.IsSpeculative}, instruction2.IsSpeculative={instruction2.IsSpeculative}");
+ }
SelectorNode selectorNode = new SelectorNode(_idAllocator.AllocateId(), instruction1.Address);
_executionCompiler.Compile(selectorNode);
// Wire the instruction that already has predecessors FIRST so the selector enters a block
@@ -162,6 +223,9 @@ private void ReplaceSuccessorsPerType(CfgInstruction oldInstruction, CfgInstruct
}
newInstruction.SuccessorsPerType[oldEntry.Key] = successorsForType;
}
+ // Carry the speculative-wiring provenance too, so the replacement instruction keeps the ability
+ // to have those edges re-typed by a later observed link.
+ newInstruction.SpeculativelyWiredSuccessors.UnionWith(oldInstruction.SpeculativelyWiredSuccessors);
}
private void AttachToNext(ICfgNode current, ICfgNode next) {
@@ -332,6 +396,80 @@ private void AssignBlockForNext(ICfgNode next) {
}
}
+ // -----------------------------------------------------------------------
+ // Bidirectional edge mutation: removal API.
+ //
+ // RemoveEdge(from, to) is the inverse of LinkToNext: it drops the edge from
+ // both endpoints, refreshes derived state on the predecessor (so that a
+ // gap-filled slot re-opens for future lazy re-convergence), and does NOT
+ // touch MaxSuccessorsCount (static instruction-type cap, never lowered).
+ // -----------------------------------------------------------------------
+
+ ///
+ /// Drops the directed edge from → to from both and
+ /// , then refreshes derived successor state on
+ /// (UniqueSuccessor, CanHaveMoreSuccessors, SuccessorsPerAddress,
+ /// SuccessorsPerType). No-op when the edge does not exist.
+ ///
+ public void RemoveEdge(ICfgNode from, ICfgNode to) {
+ if (!from.Successors.Remove(to)) {
+ return;
+ }
+ to.Predecessors.Remove(from);
+ if (from is CfgInstruction fromInstr) {
+ fromInstr.SuccessorsPerAddress.Remove(to.Address);
+ foreach (ISet perTypeSet in fromInstr.SuccessorsPerType.Values) {
+ perTypeSet.Remove(to);
+ }
+ fromInstr.SpeculativelyWiredSuccessors.Remove(to);
+ }
+ SuccessorInvariant.Refresh(from);
+ from.UpdateSuccessorCache();
+ }
+
+ ///
+ /// Removes all edges leaving and all edges pointing to it from
+ /// its predecessors. After this call has no successors and no
+ /// predecessors (fully detached from the graph).
+ ///
+ public void DetachNode(ICfgNode node) {
+ foreach (ICfgNode successor in node.Successors.ToList()) {
+ successor.Predecessors.Remove(node);
+ }
+ node.Successors.Clear();
+ foreach (ICfgNode predecessor in node.Predecessors.ToList()) {
+ RemoveEdge(predecessor, node);
+ }
+ }
+
+ ///
+ /// Handles removal fan-out: fully detaches
+ /// from the graph and repairs its containing block. When the block empties it becomes an
+ /// unreferenced object (there is no Dispose: is not disposable); otherwise
+ /// is recomputed from the surviving terminator so a
+ /// non-terminator fallthrough tail leaves the block discovery-incomplete and the real successor
+ /// can extend it.
+ ///
+ ///
+ /// Recomputing on every call is order-independent: within a block the swept nodes are always a
+ /// contiguous suffix (interior nodes have a single block-prev predecessor, so sweeping a node
+ /// sweeps all its block-successors with no gaps), and the surviving instruction list does not
+ /// depend on removal order. Transient mid-batch values are overwritten by the last removal and
+ /// never observed, since no other subscriber in a node's fan-out reads block structure.
+ ///
+ public override void RemoveInstruction(CfgInstruction instruction) {
+ DetachNode(instruction);
+ CfgBlock? block = instruction.ContainingBlock;
+ if (block is null) {
+ return;
+ }
+ block.Remove(instruction);
+ instruction.ContainingBlock = null;
+ if (block.Instructions.Count > 0) {
+ block.IsDiscoveryComplete = block.Terminator.IsBlockTerminator;
+ }
+ }
+
public override void ReplaceInstruction(CfgInstruction oldInstruction, CfgInstruction newInstruction) {
// In-place block fix-up: replace oldInstruction in its block and transfer the
// back-pointer BEFORE rewiring edges, so subsequent intra-block rewires hit
@@ -398,5 +536,11 @@ private void ReplaceSuccessorOfCallInstruction(CfgInstruction instruction, ICfgN
successors.Add(newSuccesor);
}
}
+ // Migrate speculative-wiring provenance along with the edge, otherwise the old (now detached)
+ // node lingers in SpeculativelyWiredSuccessors as a dangling reference and the new target loses
+ // its re-type ability.
+ if (instruction.SpeculativelyWiredSuccessors.Remove(currentSuccesor)) {
+ instruction.SpeculativelyWiredSuccessors.Add(newSuccesor);
+ }
}
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/CfgInstruction.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/CfgInstruction.cs
index 0fe38d59f9..fea6b2dc43 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/CfgInstruction.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/CfgInstruction.cs
@@ -59,6 +59,11 @@ internal void AttachAsts(InstructionNode instructionAst, IVisitableAstNode execu
_executionAst = executionAst;
}
+ ///
+ /// Instructions are born observed (not speculative).
+ ///
+ private bool _isSpeculative;
+
///
/// Instructions are born live.
///
@@ -133,6 +138,15 @@ private void UpdateLength() {
///
public Dictionary> SuccessorsPerType { get; } = new();
+ ///
+ /// Successors whose edge from this node was wired speculatively (created by the explorer while the
+ /// target was speculative), as opposed to being observed during execution. The recorded successor
+ /// type of such an edge is a speculative guess (typically a Normal fallthrough); when observed
+ /// execution later reaches the same target via a different edge type, the observed type replaces
+ /// the speculative one. Membership is dropped once an observed link confirms or re-types the edge.
+ ///
+ public ISet SpeculativelyWiredSuccessors { get; } = new HashSet();
+
public override void UpdateSuccessorCache() {
SuccessorsPerAddress = Successors.ToDictionary(node => node.Address);
}
@@ -147,6 +161,9 @@ public override void UpdateSuccessorCache() {
public override bool IsLive => _isLive;
+ ///
+ public override bool IsSpeculative => _isSpeculative;
+
///
///
/// Re-evaluated on every read so that any change to
@@ -253,6 +270,27 @@ public void SetLive(bool isLive) {
ContainingBlock?.OnContainedInstructionLiveChanged(isLive);
}
+ ///
+ /// Updates the speculative state of this instruction.
+ ///
+ ///
+ /// On an actual transition notifies the exactly once via
+ /// so the block's speculative
+ /// counter stays in sync. A speculative instruction is always non-live; promotion (clearing the
+ /// speculative flag) is the only direction that matters for .
+ /// No-op if the speculative state is unchanged.
+ ///
+ public override void SetSpeculative(bool isSpeculative) {
+ if (_isSpeculative == isSpeculative) {
+ return;
+ }
+ _isSpeculative = isSpeculative;
+ if (isSpeculative) {
+ SetLive(false);
+ }
+ ContainingBlock?.OnContainedInstructionSpeculativeChanged(isSpeculative);
+ }
+
///
/// Returns the pre-built display AST set by .
///
@@ -279,6 +317,28 @@ public override IVisitableAstNode ExecutionAst {
}
}
+ ///
+ /// Statically-known successor addresses derived from the instruction's decode-time operands.
+ /// Populated by parsers for instructions with immediate branch
+ /// targets (Jcc, Jcxz, Loop, near/short jmp imm, near/far call imm). Empty for indirect, RET,
+ /// IRET, and other forms where the target is not statically knowable.
+ /// For conditional jumps and loops this contains two entries (taken, fall-through);
+ /// for unconditional direct jumps/calls it contains the single target.
+ /// For non-terminator instructions it contains the single fall-through (NextInMemoryAddress32).
+ /// Every static successor is a Normal control-flow edge; runtime-only classifications
+ /// (call continuations, misaligned returns, CPU faults) are wired elsewhere, never here.
+ ///
+ public IReadOnlyList StaticSuccessorAddresses => _staticSuccessorAddresses;
+
+ private readonly List _staticSuccessorAddresses = new();
+
+ ///
+ /// Records a statically-known successor address. Called by parsers at creation time.
+ ///
+ internal void RegisterStaticSuccessorAddress(SegmentedAddress address) {
+ _staticSuccessorAddresses.Add(address);
+ }
+
public void IncreaseMaxSuccessorsCount(SegmentedAddress target) {
if (MaxSuccessorsCount is not null && !SuccessorsPerAddress.ContainsKey(target)) {
// Ensure the subsequent link attempt will be done
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/SelfModifying/SelectorNode.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/SelfModifying/SelectorNode.cs
index e021b73002..ac4e7f2482 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/SelfModifying/SelectorNode.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/SelfModifying/SelectorNode.cs
@@ -43,6 +43,7 @@ public class SelectorNode(int id, SegmentedAddress address) : CfgNode(id, addres
public override void UpdateSuccessorCache() {
SuccessorsPerSignature = Successors.OfType()
+ .Where(node => !node.IsSpeculative)
.OrderBy(node => node.Signature)
.ToDictionary(node => node.Signature);
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/Signature.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/Signature.cs
index 4052f0f56c..f2c3fe2dc5 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/Signature.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ParsedInstruction/Signature.cs
@@ -136,8 +136,12 @@ public override bool Equals(object? obj) {
///
public override int GetHashCode() {
- // Hashcode cannot depend on the signature value because 2 values can be equals if they have null bytes
- return 1;
+ // Equality (ListEquivalent) treats null bytes as wildcards, so two signatures with different
+ // concrete bytes can compare equal; the hash therefore cannot depend on byte values.
+ // Equality does require equal length though, and NullifySignature preserves length, so the
+ // byte count is a stable, equality-consistent hash. It keeps signatures of different lengths
+ // in separate buckets instead of collapsing everything into one (as a constant would).
+ return SignatureValue.Count;
}
///
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/InstructionParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/InstructionParser.cs
index 70fb63c4c7..77411dc20a 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/InstructionParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/InstructionParser.cs
@@ -175,17 +175,32 @@ public CfgInstruction ParseInstructionAt(SegmentedAddress address) {
try {
CfgInstruction parsed = ParseCfgInstruction(context);
ValidateLockPrefix(parsed, prefixes);
+ if (!parsed.IsBlockTerminator && parsed.StaticSuccessorAddresses.Count == 0) {
+ parsed.RegisterStaticSuccessorAddress(parsed.NextInMemoryAddress32.ToSegmentedAddress());
+ }
return parsed;
} catch (CpuException e) {
- // Capture how many bytes the decoder consumed before it gave up. NextField / Advance keep
- // this index in step with every field read (prefixes, opcode, ModRM, SIB, displacement,
- // immediate), so at the point of failure it is the authoritative consumed length. Read it
- // here, before BuildInvalidInstruction touches the reader to re-read the trailing span.
- int consumedLength = _parsingTools.InstructionReader.InstructionReaderAddressSource.IndexInInstruction;
- return BuildInvalidInstruction(address, prefixes, opcodeField, e, consumedLength);
+ return BuildInvalidInstruction(address, prefixes, opcodeField, e);
+ } catch (InvalidGroupIndexException) {
+ // An undefined GRP reg-field selection (e.g. GRP5 reg=7) is a #UD invalid opcode: route it
+ // through the same invalid-node machinery as other invalid opcodes instead of crashing the decode.
+ return BuildInvalidInstruction(address, prefixes, opcodeField, new CpuInvalidOpcodeException($"Invalid group index at {address}"));
}
}
+ ///
+ /// Builds an invalid node for a failed parse, capturing the full byte span the decoder consumed.
+ /// NextField / Advance keep the in-instruction index in step with every field read (prefixes,
+ /// opcode, ModRM, SIB, displacement, immediate), so at the point of failure it is the authoritative
+ /// consumed length. It must be read before touches the reader
+ /// to re-read the trailing span.
+ ///
+ private CfgInstruction BuildInvalidInstruction(SegmentedAddress address, List prefixes,
+ InstructionField opcodeField, CpuException exception) {
+ int consumedLength = _parsingTools.InstructionReader.InstructionReaderAddressSource.IndexInInstruction;
+ return BuildInvalidInstruction(address, prefixes, opcodeField, exception, consumedLength);
+ }
+
///
/// Builds the invalid node for a failed parse, capturing the full byte span the decoder
/// consumed ( ) as its byte image. Prefix + opcode fields are
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/CallParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/CallParser.cs
index 463277398b..c49de0eaca 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/CallParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/CallParser.cs
@@ -25,6 +25,7 @@ public CfgInstruction ParseCallFarImm(ParsingContext context) {
InstructionNode displayAst = new InstructionNode(InstructionOperation.CALL_FAR, _astBuilder.InstructionField.ToNode(addrField32));
IVisitableAstNode execAst = new CallFarNode(instr, targetAddress, operandBitWidth);
instr.AttachAsts(displayAst, execAst);
+ instr.RegisterStaticSuccessorAddress(addrField32.Value.ToSegmentedAddress());
return instr;
}
InstructionField addrField = _instructionReader.SegmentedAddress16.NextField(true);
@@ -34,6 +35,7 @@ public CfgInstruction ParseCallFarImm(ParsingContext context) {
InstructionNode displayAst16 = new InstructionNode(InstructionOperation.CALL_FAR, _astBuilder.InstructionField.ToNode(addrField));
IVisitableAstNode execAst16 = new CallFarNode(instr16, targetAddress16, operandBitWidth);
instr16.AttachAsts(displayAst16, execAst16);
+ instr16.RegisterStaticSuccessorAddress(addrField.Value);
return instr16;
}
@@ -48,6 +50,7 @@ public CfgInstruction ParseCallNearImm(ParsingContext context) {
instr.AttachAsts(
new InstructionNode(InstructionOperation.CALL_NEAR, targetIpNode),
new CallNearNode(instr, targetIpNode, operandBitWidth));
+ instr.RegisterStaticSuccessorAddress(new SegmentedAddress(instr.NextInMemoryAddress32.Segment, targetIp));
return instr;
}
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JccParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JccParser.cs
index a89d5f33b1..eb16ad0cad 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JccParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JccParser.cs
@@ -67,11 +67,15 @@ private CfgInstruction ParseJcc(ParsingContext context, int conditionCode, BitWi
instr.AddField(offsetField);
instr.MaxSuccessorsCount = 2;
ushort targetIp = (ushort)(instr.NextInMemoryAddress32.Offset + offsetValue);
+ SegmentedAddress takenAddress = new(instr.NextInMemoryAddress32.Segment, targetIp);
+ SegmentedAddress fallthroughAddress = instr.NextInMemoryAddress32.ToSegmentedAddress();
ValueNode targetIpNode = _astBuilder.Constant.ToNearAddressNode(targetIp, instr.NextInMemoryAddress32.ToSegmentedAddress());
ValueNode conditionNode = _astBuilder.Flag.BuildSetCondition(conditionCode);
InstructionNode displayAst = new InstructionNode(displayOps[conditionCode], targetIpNode);
IfElseNode execAst = _astBuilder.ControlFlow.ConditionalNearJump(instr, conditionNode, targetIpNode);
instr.AttachAsts(displayAst, execAst);
+ instr.RegisterStaticSuccessorAddress(takenAddress);
+ instr.RegisterStaticSuccessorAddress(fallthroughAddress);
return instr;
}
-}
\ No newline at end of file
+}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JcxzParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JcxzParser.cs
index e89724866c..4ebac1a00b 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JcxzParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JcxzParser.cs
@@ -36,6 +36,8 @@ public CfgInstruction Parse(ParsingContext context) {
InstructionNode displayAst = new InstructionNode(mnemonicOp, targetIpNode);
IfElseNode execAst = _astBuilder.ControlFlow.ConditionalNearJump(instr, condition, targetIpNode);
instr.AttachAsts(displayAst, execAst);
+ instr.RegisterStaticSuccessorAddress(new SegmentedAddress(instr.NextInMemoryAddress32.Segment, targetIp));
+ instr.RegisterStaticSuccessorAddress(instr.NextInMemoryAddress32.ToSegmentedAddress());
return instr;
}
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JmpParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JmpParser.cs
index 7706c7cef3..d6b6846f63 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JmpParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/JmpParser.cs
@@ -40,6 +40,7 @@ public CfgInstruction ParseJmpFarImm(ParsingContext context) {
instr.AttachAsts(
new InstructionNode(InstructionOperation.JMP_FAR, targetAddressNode),
new JumpFarNode(instr, targetAddressNode));
+ instr.RegisterStaticSuccessorAddress(targetAddress);
return instr;
}
@@ -53,6 +54,7 @@ private CfgInstruction ParseJmpNear(ParsingContext context, BitWidth offsetWidth
instr.AttachAsts(
new InstructionNode(displayOp, targetIpNode),
new JumpNearNode(instr, targetIpNode));
+ instr.RegisterStaticSuccessorAddress(new SegmentedAddress(instr.NextInMemoryAddress32.Segment, targetIp));
return instr;
}
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LoopParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LoopParser.cs
index fe7b7a04fd..bd8d25f21d 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LoopParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LoopParser.cs
@@ -67,6 +67,8 @@ private CfgInstruction ParseLoop(ParsingContext context, InstructionOperation di
InstructionNode displayAst = new InstructionNode(displayOp, targetIpNode);
BlockNode execAst = new BlockNode(decrementCounter, conditionalJump);
instr.AttachAsts(displayAst, execAst);
+ instr.RegisterStaticSuccessorAddress(new SegmentedAddress(instr.NextInMemoryAddress32.Segment, targetIp));
+ instr.RegisterStaticSuccessorAddress(instr.NextInMemoryAddress32.ToSegmentedAddress());
return instr;
}
diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SimpleInstructionParser.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SimpleInstructionParser.cs
index 6d0f6a5984..e572287ebe 100644
--- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SimpleInstructionParser.cs
+++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SimpleInstructionParser.cs
@@ -13,6 +13,12 @@ public class SimpleInstructionParser : BaseInstructionParser {
public SimpleInstructionParser(ParsingTools parsingTools) : base(parsingTools) {
}
+ ///
+ /// Parses the HLT instruction. MaxSuccessorsCount is 0 (no static successors): this makes
+ /// IsBlockTerminator true and prevents the InstructionParser catch-all from adding a
+ /// fallthrough edge. Execution after HLT resumes via a hardware interrupt which is handled
+ /// dynamically, not through static successors.
+ ///
public CfgInstruction ParseHlt(ParsingContext context) {
CfgInstruction instr = new(_idAllocator.AllocateId(), context.Address, context.OpcodeField, context.Prefixes, 0);
InstructionNode displayAst = new InstructionNode(InstructionOperation.HLT);
diff --git a/src/Spice86.Core/Emulator/Devices/ExternalInput/DualPic.cs b/src/Spice86.Core/Emulator/Devices/ExternalInput/DualPic.cs
index b122c07a22..fd38defccb 100644
--- a/src/Spice86.Core/Emulator/Devices/ExternalInput/DualPic.cs
+++ b/src/Spice86.Core/Emulator/Devices/ExternalInput/DualPic.cs
@@ -208,6 +208,23 @@ public void AcknowledgeInterrupt(byte irq) {
return vectorNumber;
}
+ ///
+ /// Enumerates the CPU interrupt vector numbers that the two PICs deliver for their hardware IRQ
+ /// lines (8 lines per controller). Derived from each controller's programmed vector base so the
+ /// set reflects the current programming rather than assuming the BIOS defaults
+ /// (0x08-0x0F for the primary, 0x70-0x77 for the secondary).
+ ///
+ /// The hardware interrupt vector numbers, primary controller first.
+ public IEnumerable EnumerateHardwareInterruptVectorNumbers() {
+ const int irqLinesPerController = 8;
+ for (int line = 0; line < irqLinesPerController; line++) {
+ yield return (byte)(_primaryPic.InterruptVectorBase + line);
+ }
+ for (int line = 0; line < irqLinesPerController; line++) {
+ yield return (byte)(_secondaryPic.InterruptVectorBase + line);
+ }
+ }
+
///
/// Resets both PIC controllers and applies the default mask/unmask configuration.
///
diff --git a/src/Spice86.Core/Emulator/InterruptHandlers/Common/RoutineInstall/InterruptInstaller.cs b/src/Spice86.Core/Emulator/InterruptHandlers/Common/RoutineInstall/InterruptInstaller.cs
index 00d5e160db..cf96ed852e 100644
--- a/src/Spice86.Core/Emulator/InterruptHandlers/Common/RoutineInstall/InterruptInstaller.cs
+++ b/src/Spice86.Core/Emulator/InterruptHandlers/Common/RoutineInstall/InterruptInstaller.cs
@@ -1,6 +1,7 @@
namespace Spice86.Core.Emulator.InterruptHandlers.Common.RoutineInstall;
using Spice86.Core.Emulator.CPU;
+using Spice86.Core.Emulator.Devices.ExternalInput;
using Spice86.Core.Emulator.Function;
using Spice86.Core.Emulator.InterruptHandlers.Common.MemoryWriter;
using Spice86.Shared.Emulator.Memory;
@@ -13,6 +14,8 @@ namespace Spice86.Core.Emulator.InterruptHandlers.Common.RoutineInstall;
///
public class InterruptInstaller : AssemblyRoutineInstaller {
private readonly InterruptVectorTable _interruptVectorTable;
+ private readonly HashSet _hardwareInterruptVectorNumbers;
+ private readonly List _installedHardwareInterruptHandlerAddresses = new();
///
/// Initializes a new instance of the class.
@@ -20,10 +23,23 @@ public class InterruptInstaller : AssemblyRoutineInstaller {
/// The interrupt vector table
/// The class that writes machine code for interrupt handlers
/// List of all functions.
- public InterruptInstaller(InterruptVectorTable interruptVectorTable, MemoryAsmWriter memoryAsmWriter, FunctionCatalogue functionCatalogue) : base(memoryAsmWriter, functionCatalogue) {
+ /// PIC pair used to classify which installed handlers are hardware-interrupt (external event) handlers.
+ public InterruptInstaller(InterruptVectorTable interruptVectorTable, MemoryAsmWriter memoryAsmWriter,
+ FunctionCatalogue functionCatalogue, DualPic dualPic) : base(memoryAsmWriter, functionCatalogue) {
_interruptVectorTable = interruptVectorTable;
+ _hardwareInterruptVectorNumbers = new HashSet(dualPic.EnumerateHardwareInterruptVectorNumbers());
}
+ ///
+ /// Entry addresses of the emulator-installed handlers whose vector is a PIC hardware-interrupt
+ /// (external event) vector. These fire on external events with nondeterministic timing and may
+ /// never be reached from the program's observed entry points, so they are seeded as known-safe
+ /// CFG roots for speculative exploration. Captured at install time, so every address here is by
+ /// construction emulator-installed (no need to scan the IVT or filter uninstalled entries).
+ ///
+ public IReadOnlyList InstalledHardwareInterruptHandlerAddresses =>
+ _installedHardwareInterruptHandlerAddresses;
+
///
/// Writes ASM code of the given handler in RAM.
/// Registers it in the vector table and register its name in the function handler as well.
@@ -36,6 +52,9 @@ public SegmentedAddress InstallInterruptHandler(IInterruptHandler interruptHandl
// Define ASM in vector table
_interruptVectorTable[interruptHandler.VectorNumber] = new(handlerAddress.Segment, handlerAddress.Offset);
+ if (_hardwareInterruptVectorNumbers.Contains(interruptHandler.VectorNumber)) {
+ _installedHardwareInterruptHandlerAddresses.Add(handlerAddress);
+ }
return handlerAddress;
}
}
\ No newline at end of file
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/CSharpOverrideHelper.cs b/src/Spice86.Core/Emulator/ReverseEngineer/CSharpOverrideHelper.cs
index eaaba31785..b4a21cea76 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/CSharpOverrideHelper.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/CSharpOverrideHelper.cs
@@ -81,6 +81,36 @@ public class CSharpOverrideHelper {
///
public UInt32Indexer UInt32 => Memory.UInt32;
+ ///
+ /// Gets the big-endian 16-bit indexer of the memory bus.
+ ///
+ public UInt16BigEndianIndexer UInt16BigEndian => Memory.UInt16BigEndian;
+
+ ///
+ /// Gets the signed 8-bit indexer of the memory bus.
+ ///
+ public Int8Indexer Int8 => Memory.Int8;
+
+ ///
+ /// Gets the signed 16-bit indexer of the memory bus.
+ ///
+ public Int16Indexer Int16 => Memory.Int16;
+
+ ///
+ /// Gets the signed 32-bit indexer of the memory bus.
+ ///
+ public Int32Indexer Int32 => Memory.Int32;
+
+ ///
+ /// Gets the 16-bit segmented address indexer of the memory bus.
+ ///
+ public SegmentedAddress16Indexer SegmentedAddress16 => Memory.SegmentedAddress16;
+
+ ///
+ /// Gets the 32-bit segmented address indexer of the memory bus.
+ ///
+ public SegmentedAddress32Indexer SegmentedAddress32 => Memory.SegmentedAddress32;
+
///
/// Gets the stack of the CPU.
///
@@ -821,6 +851,26 @@ public bool SelectorSignatureMatches(ushort segment, ushort offset, byte?[] sign
return new Signature(ImmutableList.CreateRange(signature)).ListEquivalent(bytes);
}
+ ///
+ /// Verifies that a single speculative instruction at the given segmented address still matches the
+ /// signature decoded at exploration time. The generated code calls this immediately before the
+ /// instruction's body so that self-modifying code performed by an earlier instruction in the same block
+ /// is detected before the modified instruction's decode-time-baked body executes. If memory no longer
+ /// matches, the speculative decode was wrong and execution falls back to .
+ /// Reads via raw segment*16+offset (no MMU translation) to stay identical to the interpreter oracle that recorded the signature; must change in lockstep with it, never alone.
+ ///
+ /// The segment of the speculative instruction.
+ /// The offset of the speculative instruction.
+ /// The signature of the speculative instruction; null entries are wildcards for fields the decoder reads from memory at execution time.
+ /// Thrown when memory at the instruction address does not match the expected signature.
+ public void VerifySpeculativeEntryOrFail(ushort segment, ushort offset, byte?[] runSignature) {
+ uint linearAddress = MemoryUtils.ToPhysicalAddress(segment, offset);
+ IList bytes = Memory.GetSlice((int)linearAddress, runSignature.Length);
+ if (!new Signature(ImmutableList.CreateRange(runSignature)).ListEquivalent(bytes)) {
+ throw FailAsUntested($"Speculative code at {segment:X4}:{offset:X4} no longer matches memory");
+ }
+ }
+
///
/// Throws an exception if the given value is not one of the possible values.
///
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CSharpAstEmitter.cs b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CSharpAstEmitter.cs
index a4aced9f00..e30bc91b3c 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CSharpAstEmitter.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CSharpAstEmitter.cs
@@ -175,9 +175,17 @@ public EmittedCode VisitCallFarNode(CallFarNode node) {
edge => CallBranchBody(node.Instruction, runtimeContinuation, helperName, edge, isFarCall: true, forceSameMethodGoto: true));
}
- ResolvedCfgEdge targetEdge = Context.ResolveEdge(node.Instruction, InstructionSuccessorType.Normal,
- new SegmentedAddress(targetSegment.Value, targetOffset.Value));
- return Transfer.EmitCallHelperAndContinuation(helperName, node.Instruction, Transfer.FunctionExpression(targetEdge), CurrentMethod, farCallTargetCs: targetSegment.Value);
+ SegmentedAddress targetAddress = new SegmentedAddress(targetSegment.Value, targetOffset.Value);
+ ResolvedCfgEdge? targetEdge = Context.TryResolveEdge(node.Instruction, InstructionSuccessorType.Normal, targetAddress);
+ if (targetEdge is null) {
+ // Constant-target far call whose callee was never observed (e.g. a speculatively-seeded
+ // interrupt handler where the far call target is switchable and thus not explored).
+ // Emit a direct FarCall using SearchFunctionOverride to resolve the callee at runtime.
+ string segExpr = Context.GetSegmentVariable(targetSegment.Value);
+ string functionExpr = $"SearchFunctionOverride(new Spice86.Shared.Emulator.Memory.SegmentedAddress({segExpr}, 0x{targetOffset.Value:X4}))";
+ return Transfer.EmitCallHelperAndContinuation(helperName, node.Instruction, functionExpr, CurrentMethod, farCallTargetCs: targetSegment.Value);
+ }
+ return Transfer.EmitCallHelperAndContinuation(helperName, node.Instruction, Transfer.FunctionExpression(targetEdge.Value), CurrentMethod, farCallTargetCs: targetSegment.Value);
}
public EmittedCode VisitInterruptCallNode(InterruptCallNode node) {
@@ -344,7 +352,9 @@ private EmittedCode LowerFallthroughArm(CfgInstruction instruction) {
private EmittedCode BuildNearRuntimeDispatch(CfgInstruction instruction, string targetExpression, string targetKind,
Func> bodyForEdge) {
- IReadOnlyList edges = RequireObservedEdges(instruction, targetKind);
+ if (!TryGetObservedDispatchEdges(instruction, targetKind, out IReadOnlyList edges, out EmittedCode failure)) {
+ return failure;
+ }
List cases = edges
.OrderBy(edge => edge.Target.Address.Offset)
.Select(edge => new SwitchCase($"0x{edge.Target.Address.Offset:X4}", bodyForEdge(edge)))
@@ -355,7 +365,9 @@ private EmittedCode BuildNearRuntimeDispatch(CfgInstruction instruction, string
private EmittedCode BuildFarRuntimeDispatch(CfgInstruction instruction, string segmentExpression, string offsetExpression, string targetKind,
Func> bodyForEdge) {
- IReadOnlyList edges = RequireObservedEdges(instruction, $"far {targetKind}");
+ if (!TryGetObservedDispatchEdges(instruction, $"far {targetKind}", out IReadOnlyList edges, out EmittedCode failure)) {
+ return failure;
+ }
string segmentVariable = $"targetSegment_{instruction.Id}";
string offsetVariable = $"targetOffset_{instruction.Id}";
List items = [
@@ -373,12 +385,22 @@ private EmittedCode BuildFarRuntimeDispatch(CfgInstruction instruction, string s
return EmittedCode.Statements(items);
}
- private IReadOnlyList RequireObservedEdges(CfgInstruction instruction, string targetKind) {
- IReadOnlyList edges = Context.GetObservedEdges(instruction, InstructionSuccessorType.Normal);
+ ///
+ /// Resolves the observed normal successor edges that an indirect (computed) jump or call dispatches over.
+ /// Returns false with a runtime FailAsUntested throw in when the
+ /// instruction was decoded into the CFG but never observed taking any edge during discovery. Failing at
+ /// generation time would abort the whole dump; instead the generated code compiles and only fails if this
+ /// branch is actually reached at runtime, matching the untested-path convention used elsewhere.
+ ///
+ private bool TryGetObservedDispatchEdges(CfgInstruction instruction, string targetKind,
+ out IReadOnlyList edges, out EmittedCode failure) {
+ edges = Context.GetSuccessorEdges(instruction, InstructionSuccessorType.Normal);
if (edges.Count == 0) {
- throw new NotSupportedException($"Instruction {instruction.Address} has no observed normal {targetKind} targets.");
+ failure = EmittedCode.Diverging($"throw FailAsUntested(\"Indirect {targetKind} at {instruction.Address} has no observed targets.\");");
+ return false;
}
- return edges;
+ failure = EmittedCode.None;
+ return true;
}
private IReadOnlyList CallBranchBody(CfgInstruction instruction, CallContinuation continuation,
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CfgGeneratorContext.cs b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CfgGeneratorContext.cs
index 2d684a5309..1a227c2aa9 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CfgGeneratorContext.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CfgGeneratorContext.cs
@@ -83,7 +83,7 @@ public int GetEntryLoadOffset(CfgCodePartition partition, ICfgNode entryNode) {
};
}
- public IReadOnlyList GetObservedEdges(CfgInstruction source, InstructionSuccessorType type) {
+ public IReadOnlyList GetSuccessorEdges(CfgInstruction source, InstructionSuccessorType type) {
if (!source.SuccessorsPerType.TryGetValue(type, out ISet? successors)) {
return [];
}
@@ -108,7 +108,7 @@ public ResolvedCfgEdge ResolveEdge(CfgInstruction source, InstructionSuccessorTy
/// when more than one matches (ambiguous semantic dispatch).
///
public ResolvedCfgEdge? TryResolveEdge(CfgInstruction source, InstructionSuccessorType type, SegmentedAddress targetAddress) {
- List matches = GetObservedEdges(source, type)
+ List matches = GetSuccessorEdges(source, type)
.Where(edge => edge.Target.Address == targetAddress)
.ToList();
return matches.Count switch {
@@ -161,7 +161,7 @@ public ResolvedCfgEdge ResolveEdge(CfgInstruction source, InstructionSuccessorTy
public CallContinuation ResolveCallContinuation(CfgInstruction call) {
SegmentedAddress expectedReturn = call.NextInMemoryAddress32.ToSegmentedAddress();
- IReadOnlyList aligned = GetObservedEdges(call, InstructionSuccessorType.CallToReturn);
+ IReadOnlyList aligned = GetSuccessorEdges(call, InstructionSuccessorType.CallToReturn);
if (aligned.Count > 1) {
throw new NotSupportedException(
$"Call {call.Address} has {aligned.Count} observed aligned continuations; the generator cannot pick one continuation for a single call.");
@@ -170,7 +170,7 @@ public CallContinuation ResolveCallContinuation(CfgInstruction call) {
return new CallContinuation(expectedReturn, aligned[0]);
}
- IReadOnlyList misaligned = GetObservedEdges(call, InstructionSuccessorType.CallToMisalignedReturn);
+ IReadOnlyList misaligned = GetSuccessorEdges(call, InstructionSuccessorType.CallToMisalignedReturn);
if (misaligned.Count == 1) {
return new CallContinuation(expectedReturn, misaligned[0]);
}
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CpuFaultWrapper.cs b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CpuFaultWrapper.cs
index e2724ac0a0..4b35a08b12 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CpuFaultWrapper.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CpuFaultWrapper.cs
@@ -21,7 +21,7 @@ internal sealed class CpuFaultWrapper(CfgGeneratorContext context, TransferEmitt
/// observed CPU-fault edges; otherwise returns the body unchanged.
///
public EmittedCode Wrap(CfgInstruction instruction, EmittedCode body, MethodPlan method) {
- IReadOnlyList faultEdges = context.GetObservedEdges(instruction, InstructionSuccessorType.CpuFault);
+ IReadOnlyList faultEdges = context.GetSuccessorEdges(instruction, InstructionSuccessorType.CpuFault);
if (faultEdges.Count == 0) {
return body;
}
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/MethodEmitter.cs b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/MethodEmitter.cs
index 10d5e3d480..4d974752fa 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/MethodEmitter.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/MethodEmitter.cs
@@ -7,6 +7,8 @@ namespace Spice86.Core.Emulator.ReverseEngineer.CfgCodeGeneration;
using Spice86.Core.Emulator.ReverseEngineer.CfgCodeGeneration.Model.Plan;
using Spice86.Core.Emulator.ReverseEngineer.FunctionPartitioning.Model;
+using System.Linq;
+
using CfgSelectorNode = Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying.SelectorNode;
///
@@ -35,6 +37,14 @@ public void Emit(CSharpSourceWriter writer, MethodPlan method) {
writer.Label(nodePlan.Label);
EmitBlockEntryExternalEventCheck(writer, nodePlan.Block);
}
+ // Speculative instructions are verified against memory immediately before they execute.
+ // A per-instruction guard (rather than a single block-entry guard) is required so that
+ // self-modifying code performed by an earlier instruction in the same block is detected
+ // before the modified instruction's decode-time-baked body runs: at block entry the bytes
+ // still match, the divergence only appears once the earlier store has executed.
+ if (nodePlan.Node is CfgInstruction speculativeInstruction && speculativeInstruction.IsSpeculative) {
+ EmitSpeculativeGuard(writer, speculativeInstruction);
+ }
EmittedCode nodeCode = BuildNode(writer, nodePlan.Node, method);
EmittedCodeRenderer.Render(nodeCode, writer);
bodyCompletesNormally = nodeCode.CompletesNormally;
@@ -104,4 +114,23 @@ private EmittedCode BuildNode(CSharpSourceWriter writer, ICfgNode node, MethodPl
throw new NotSupportedException($"CFG C# generation does not support node {node.GetType().FullName} yet at {node.Address}.");
}
}
+
+ ///
+ /// Emits a VerifySpeculativeEntryOrFail guard for a single speculative instruction. The guard
+ /// re-reads the instruction's bytes from memory immediately before its body executes and fails as
+ /// untested if they no longer match the signature decoded at exploration time. Emitting one guard per
+ /// speculative instruction (rather than a single block-entry guard covering the whole run) is what lets
+ /// the generated code detect self-modifying code that an earlier instruction in the same block performs
+ /// against a later speculative instruction: an entry-only guard runs before any instruction executes and
+ /// so cannot observe such a mutation.
+ ///
+ private void EmitSpeculativeGuard(CSharpSourceWriter writer, CfgInstruction speculativeInstruction) {
+ IReadOnlyList signatureValue = speculativeInstruction.Signature.SignatureValue;
+ if (signatureValue.Count == 0) {
+ return;
+ }
+ string signatureBytes = string.Join(", ", signatureValue.Select(value => value is byte byteValue ? $"(byte)0x{byteValue:X2}" : "null"));
+ string segmentVariable = context.GetSegmentVariable(speculativeInstruction.Address.Segment);
+ writer.Line($"VerifySpeculativeEntryOrFail({segmentVariable}, 0x{speculativeInstruction.Address.Offset:X4}, [{signatureBytes}]);");
+ }
}
diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/ControlFlowGraph/CfgBlockGraphExporter.cs b/src/Spice86.Core/Emulator/ReverseEngineer/ControlFlowGraph/CfgBlockGraphExporter.cs
index bbc6aba1b4..eed952ca5e 100644
--- a/src/Spice86.Core/Emulator/ReverseEngineer/ControlFlowGraph/CfgBlockGraphExporter.cs
+++ b/src/Spice86.Core/Emulator/ReverseEngineer/ControlFlowGraph/CfgBlockGraphExporter.cs
@@ -11,11 +11,17 @@ namespace Spice86.Core.Emulator.ReverseEngineer.ControlFlowGraph;
///
/// Shared CFG block traversal layer. Owns BFS over blocks, seed collection, and edge discovery.
/// Both JSON serialization and UI rendering consume the exported graph without running their own traversal.
+///
+/// Speculative blocks are discovered for free during BFS traversal because the explorer
+/// wires full edges (observed-terminator -> speculative-entry) at explore time. No generation-time
+/// block building is needed.
///
public sealed class CfgBlockGraphExporter {
///
/// Exports a from the given execution context manager,
/// seeded by entry-point blocks and the last-executed block.
+ /// Speculative blocks reachable from observed block terminators are included automatically
+ /// via BFS traversal of the live edges wired by the explorer.
///
public CfgExecutionContextGraph ExportFromExecutionContext(ExecutionContextManager contextManager, int? nodeLimit) {
ExecutionContext currentContext = contextManager.CurrentExecutionContext;
diff --git a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgGraphReloader.cs b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgGraphReloader.cs
index 99e147b0c2..b91362f1df 100644
--- a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgGraphReloader.cs
+++ b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgGraphReloader.cs
@@ -34,12 +34,14 @@ internal sealed class CfgGraphReloader {
private readonly PreviousInstructions _previousInstructions;
private readonly SequentialIdAllocator _idAllocator;
private readonly CfgNodeExecutionCompiler _executionCompiler;
+ private readonly CfgNodeIndex _nodeIndex;
public CfgGraphReloader(CfgCpu cfgCpu, State state, CfgNodeExecutionCompiler executionCompiler, SequentialIdAllocator idAllocator) {
_state = state;
_executionCompiler = executionCompiler;
_executionContextManager = cfgCpu.ExecutionContextManager;
_previousInstructions = cfgCpu.CfgNodeFeeder.InstructionsFeeder.PreviousInstructions;
+ _nodeIndex = cfgCpu.CfgNodeFeeder.NodeIndex;
_idAllocator = idAllocator;
}
@@ -54,25 +56,33 @@ public void Reload(CfgReloadDump dump) {
foreach (CfgReloadNodeInfo nodeInfo in dump.Nodes) {
ICfgNode node = reconstructor.Reconstruct(nodeInfo);
nodesById[nodeInfo.Id] = node;
+ node.SetSpeculative(nodeInfo.Speculative == true);
}
- // 2. Force every reconstructed instruction non-live (they are born live). Execution promotes
- // them to live via CurrentInstructions.SetAsCurrent on first encounter, which is the only
- // path that also installs the memory-write breakpoints required for self-modifying-code
+ // 2. Force every reconstructed observed instruction non-live (they are born live). Execution
+ // promotes them to live via CurrentInstructions.SetAsCurrent on first encounter, which is the
+ // only path that also installs the memory-write breakpoints required for self-modifying-code
// eviction. This must run BEFORE blocks are built so each block's non-live counter
// initializes correctly (CfgBlock reads IsLive on Append). Selectors are always live and
- // carry no breakpoints, so they are left as-is.
+ // carry no breakpoints, so they are left as-is. Speculative nodes are already non-live.
foreach (ICfgNode node in nodesById.Values) {
if (node is CfgInstruction instruction) {
instruction.SetLive(false);
}
}
- // 3. Register every reconstructed instruction in PreviousInstructions (NOT CurrentInstructions),
- // so resumed execution reuses reloaded nodes via memory matching instead of parsing anew.
+ // 3. Register all reconstructed instructions in the CfgNodeIndex (the index spans every node,
+ // observed and speculative, mirroring the live feeder path where ParseAndSetAsCurrent calls
+ // NodeIndex.Insert for observed nodes too). Observed instructions are additionally registered
+ // in PreviousInstructions (NOT CurrentInstructions) so resumed execution reuses reloaded
+ // nodes via memory matching instead of parsing anew. Speculative nodes stay out of both
+ // memory caches per the speculative invariant.
foreach (ICfgNode node in nodesById.Values) {
if (node is CfgInstruction instruction) {
- _previousInstructions.AddInstructionInPrevious(instruction);
+ _nodeIndex.Insert(instruction);
+ if (!instruction.IsSpeculative) {
+ _previousInstructions.AddInstructionInPrevious(instruction);
+ }
}
}
@@ -88,7 +98,10 @@ public void Reload(CfgReloadDump dump) {
// 7. Seed the live allocator past the highest reloaded id.
_idAllocator.NextId = dump.IdAllocatorNext;
- // 8. LastExecuted / NodeToExecuteNextAccordingToGraph and the context stack stay reset (graph-only scope).
+ // 8. Restore the poison set (addresses proven byte-unstable by prior runs).
+ RestorePoisonSet(dump);
+
+ // 9. LastExecuted / NodeToExecuteNextAccordingToGraph and the context stack stay reset (graph-only scope).
}
private void RestoreEdges(CfgReloadDump dump, Dictionary nodesById) {
@@ -168,4 +181,14 @@ private static InstructionSuccessorType ParseSuccessorType(string type) {
}
return parsed;
}
+
+ private void RestorePoisonSet(CfgReloadDump dump) {
+ if (dump.PoisonedAddresses is null) {
+ return;
+ }
+ foreach (string addressStr in dump.PoisonedAddresses) {
+ SegmentedAddress address = ReloadAddressParser.ParseOrThrow(addressStr, "poisoned address");
+ _nodeIndex.PoisonSet.Add(address);
+ }
+ }
}
diff --git a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadDump.cs b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadDump.cs
index d3aeae49ea..f5853be59f 100644
--- a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadDump.cs
+++ b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadDump.cs
@@ -27,4 +27,11 @@ internal sealed record CfgReloadDump {
/// Ordered block membership; authoritative on import (blocks are rebuilt from this).
[JsonPropertyName("blocks")] public required CfgReloadBlockInfo[] Blocks { get; init; }
+
+ ///
+ /// Addresses proven byte-unstable between explore-time and execution-time.
+ /// Speculation permanently stops at these addresses. Persisted to make poisoning monotonic
+ /// across runs (a speculative-only instability leaves no graph trace otherwise).
+ ///
+ [JsonPropertyName("poisonedAddresses")] public string[]? PoisonedAddresses { get; init; }
}
diff --git a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadExporter.cs b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadExporter.cs
index 5509956f26..aec339db17 100644
--- a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadExporter.cs
+++ b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadExporter.cs
@@ -2,9 +2,11 @@ namespace Spice86.Core.Emulator.StateSerialization.CfgReload;
using Spice86.Core.Emulator.CPU.CfgCpu;
using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying;
using Spice86.Core.Emulator.ReverseEngineer.Graph;
+using Spice86.Shared.Emulator.Memory;
using System.Linq;
@@ -21,6 +23,17 @@ internal sealed class CfgReloadExporter {
/// node, their typed edges, the blocks that contain them, and the entry-point addresses.
///
public CfgReloadDump Export(ExecutionContextManager contextManager) {
+ return Export(contextManager, poisonSet: null);
+ }
+
+ ///
+ /// Traverses from following both
+ /// Successors and Predecessors , collecting every reachable instruction and selector
+ /// node, their typed edges, the blocks that contain them, and the entry-point addresses.
+ /// When is provided, includes poisoned addresses in the dump for
+ /// monotonic persistence across runs.
+ ///
+ public CfgReloadDump Export(ExecutionContextManager contextManager, HashSet? poisonSet) {
HashSet reachable = TraverseReachableNodes(contextManager);
List nodes = new();
@@ -56,6 +69,12 @@ public CfgReloadDump Export(ExecutionContextManager contextManager) {
.OrderBy(s => s, StringComparer.Ordinal)
.ToArray();
+ string[]? poisonedAddresses = poisonSet is { Count: > 0 }
+ ? poisonSet.Select(address => address.ToString())
+ .OrderBy(s => s, StringComparer.Ordinal)
+ .ToArray()
+ : null;
+
return new CfgReloadDump {
IdAllocatorNext = maxId + 1,
EntryPoints = entryPoints,
@@ -63,7 +82,8 @@ public CfgReloadDump Export(ExecutionContextManager contextManager) {
Edges = edges
.OrderBy(e => e.From).ThenBy(e => e.To).ThenBy(e => e.Type, StringComparer.Ordinal)
.ToArray(),
- Blocks = blockInfos.OrderBy(b => b.Id).ToArray()
+ Blocks = blockInfos.OrderBy(b => b.Id).ToArray(),
+ PoisonedAddresses = poisonedAddresses
};
}
@@ -84,7 +104,8 @@ private static CfgReloadNodeInfo BuildInstructionInfo(CfgInstruction instruction
Type = CfgReloadNodeType.Instruction,
Addr = instruction.Address.ToString(),
Bytes = sigHex,
- MaxSucc = instruction.MaxSuccessorsCount
+ MaxSucc = instruction.MaxSuccessorsCount,
+ Speculative = instruction.IsSpeculative ? true : null
};
}
diff --git a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadNodeInfo.cs b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadNodeInfo.cs
index 0e99d1f520..5c112d2733 100644
--- a/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadNodeInfo.cs
+++ b/src/Spice86.Core/Emulator/StateSerialization/CfgReload/CfgReloadNodeInfo.cs
@@ -14,4 +14,11 @@ internal sealed record CfgReloadNodeInfo {
[JsonPropertyName("addr")] public required string Addr { get; init; }
[JsonPropertyName("bytes")] public string? Bytes { get; init; }
[JsonPropertyName("maxSucc")] public int? MaxSucc { get; init; }
+
+ ///
+ /// True when the node was speculatively decoded but never confirmed by execution.
+ /// Null (omitted) means observed. Restoring this flag on reload lets the block-level
+ /// _speculativeCounter reconstruct correctly from contained nodes.
+ ///
+ [JsonPropertyName("speculative")] public bool? Speculative { get; init; }
}
diff --git a/src/Spice86.Core/Emulator/StateSerialization/EmulationStateDataWriter.cs b/src/Spice86.Core/Emulator/StateSerialization/EmulationStateDataWriter.cs
index fbe9ecad44..c447d926e2 100644
--- a/src/Spice86.Core/Emulator/StateSerialization/EmulationStateDataWriter.cs
+++ b/src/Spice86.Core/Emulator/StateSerialization/EmulationStateDataWriter.cs
@@ -5,6 +5,7 @@ namespace Spice86.Core.Emulator.StateSerialization;
using Spice86.Core.CLI;
using Spice86.Core.Emulator.CPU;
using Spice86.Core.Emulator.CPU.CfgCpu;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.Function;
using Spice86.Core.Emulator.ReverseEngineer.CfgCodeGeneration;
using Spice86.Core.Emulator.ReverseEngineer.ControlFlowGraph;
@@ -33,6 +34,7 @@ public class EmulationStateDataWriter : EmulationStateDataIoHandler {
private readonly FunctionCatalogue _functionCatalogue;
private readonly ISerializableBreakpointsSource _serializableBreakpointsSource;
private readonly Configuration _configuration;
+ private readonly CfgNodeIndex _nodeIndex;
///
/// Initializes a new instance.
@@ -49,6 +51,7 @@ public class EmulationStateDataWriter : EmulationStateDataIoHandler {
/// Where to save the data.
/// Source for breakpoints to serialize
/// The emulator configuration, used to derive the program checksum baked into the generated project.
+ /// The persistent graph index whose poison set is serialized with the CFG reload dump.
/// The logger service implementation.
internal EmulationStateDataWriter(State state,
ExecutionAddressesExtractor executionAddressesExtractor,
@@ -62,6 +65,7 @@ internal EmulationStateDataWriter(State state,
EmulatorStateSerializationFolder emulatorStateSerializationFolder,
ISerializableBreakpointsSource serializableBreakpointsSource,
Configuration configuration,
+ CfgNodeIndex nodeIndex,
ILoggerService loggerService) : base(emulatorStateSerializationFolder, loggerService) {
_executionAddressesExtractor = executionAddressesExtractor;
_state = state;
@@ -74,6 +78,7 @@ internal EmulationStateDataWriter(State state,
_functionCatalogue = functionCatalogue;
_serializableBreakpointsSource = serializableBreakpointsSource;
_configuration = configuration;
+ _nodeIndex = nodeIndex;
}
///
@@ -150,7 +155,7 @@ private void WriteToFile(string path, Action writeAction) {
}
private void WriteCfgReload(string filePath) {
- CfgReloadDump dump = new CfgReloadExporter().Export(_executionContextManager);
+ CfgReloadDump dump = new CfgReloadExporter().Export(_executionContextManager, _nodeIndex.PoisonSet);
string jsonString = JsonSerializer.Serialize(dump, CfgReloadSerialization.Options);
File.WriteAllText(filePath, jsonString);
}
diff --git a/src/Spice86.Shared/Utils/DictionaryUtils.cs b/src/Spice86.Shared/Utils/DictionaryUtils.cs
index 9a3ae4d796..cdd3ae5601 100644
--- a/src/Spice86.Shared/Utils/DictionaryUtils.cs
+++ b/src/Spice86.Shared/Utils/DictionaryUtils.cs
@@ -30,4 +30,23 @@ public static List GetOrAddList(Dictionary
+ /// Removes from the collection stored at and drops
+ /// the key entirely when that collection becomes empty. No-op when the key is absent or the value
+ /// was not present.
+ ///
+ /// true when the value was removed, false otherwise.
+ public static bool RemoveFromCollection(
+ IDictionary dictionary, TKey key, TValue value)
+ where TKey : notnull
+ where TCollection : class, ICollection {
+ if (!dictionary.TryGetValue(key, out TCollection? collection) || !collection.Remove(value)) {
+ return false;
+ }
+ if (collection.Count == 0) {
+ dictionary.Remove(key);
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/Spice86/Spice86DependencyInjection.cs b/src/Spice86/Spice86DependencyInjection.cs
index 71fee4cce8..d18c8f7040 100644
--- a/src/Spice86/Spice86DependencyInjection.cs
+++ b/src/Spice86/Spice86DependencyInjection.cs
@@ -12,9 +12,11 @@ namespace Spice86;
using Spice86.Core.Emulator.CPU;
using Spice86.Core.Emulator.CPU.CfgCpu;
using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.CPU.CfgCpu.InstructionExecutor.Expressions;
using Spice86.Core.Emulator.CPU.CfgCpu.InstructionRenderer;
using Spice86.Core.Emulator.CPU.CfgCpu.Logging;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
using Spice86.Core.Emulator.Devices.Cmos;
using Spice86.Core.Emulator.Devices.DirectMemoryAccess;
using Spice86.Core.Emulator.Devices.ExternalInput;
@@ -67,6 +69,7 @@ namespace Spice86;
using Spice86.Views;
using System.Net.Sockets;
+using System.Linq;
using System.Reflection;
///
@@ -312,7 +315,7 @@ internal Spice86DependencyInjection(Configuration configuration, MainWindow? mai
CfgIdAllocator = cfgIdAllocator;
CfgCpu cfgCpu = new(memory, state, ioPortDispatcher, callbackHandler,
dualPic, emulatorBreakpointsManager, pauseHandler, functionCatalogue,
- configuration.UseCodeOverrideOption, configuration.FailOnInvalidOpcode, configuration.AllowIvtAddress0, loggerService, cfgNodeExecutionCompiler, cfgIdAllocator, cpuHeavyLogger);
+ configuration.UseCodeOverrideOption, configuration.FailOnInvalidOpcode, configuration.AllowIvtAddress0, configuration.EnableSpeculativeCfgExploration, loggerService, cfgNodeExecutionCompiler, cfgIdAllocator, cpuHeavyLogger);
if (loggerService.IsEnabled(LogEventLevel.Information)) {
loggerService.Information("CfgCpu created...");
@@ -460,7 +463,7 @@ internal Spice86DependencyInjection(Configuration configuration, MainWindow? mai
CfgCSharpDumper cfgCSharpDumper = new(new CfgCSharpGenerator(), loggerService);
EmulationStateDataWriter emulationStateDataWriter = new(state, executionAddressesExtractor, memoryDataExporter,
listingExporter, cfgCpuSnapshotBuilder, cfgBlocksJsonExporter, cfgCSharpDumper, cfgCpu.ExecutionContextManager, functionCatalogue, emulatorStateSerializationFolder, emulatorBreakpointsManager,
- configuration, loggerService);
+ configuration, cfgCpu.CfgNodeFeeder.NodeIndex, loggerService);
EmulatorStateSerializer emulatorStateSerializer = new(emulatorStateSerializationFolder,
emulationStateDataReader, emulationStateDataWriter);
@@ -584,7 +587,7 @@ internal Spice86DependencyInjection(Configuration configuration, MainWindow? mai
}
InterruptInstaller interruptInstaller =
- new InterruptInstaller(interruptVectorTable, memoryAsmWriter, functionCatalogue);
+ new InterruptInstaller(interruptVectorTable, memoryAsmWriter, functionCatalogue, dualPic);
AssemblyRoutineInstaller assemblyRoutineInstaller =
new AssemblyRoutineInstaller(memoryAsmWriter, functionCatalogue);
@@ -728,17 +731,29 @@ internal Spice86DependencyInjection(Configuration configuration, MainWindow? mai
ReloadCfgGraphIfRequested(configuration, emulationStateDataReader, cfgCpu, state,
cfgNodeExecutionCompiler, loggerService, cfgIdAllocator);
+ // Seed emulator-installed hardware interrupt (external event) handlers for speculative
+ // exploration and register them as CFG generation roots. The addresses were captured at
+ // install time (see InterruptInstaller), so this only needs to run after CFG reload (so any
+ // previously indexed nodes are present). External-event handlers fire with nondeterministic
+ // timing and may never be reached from the program's observed entry points during discovery,
+ // so without this they would be absent from generated code and generated execution would fail
+ // when the PIC delivers their vector.
+ if (configuration.InitializeDOS is not false) {
+ cfgCpu.ExecutionContextManager.SeedKnownSafeHandlersAndRegisterEntryPoints(
+ interruptInstaller.InstalledHardwareInterruptHandlerAddresses);
+ }
+
McpHttpHost? mcpHttpTransport = null;
- if (configuration.McpHttpPort != 0) {
- // Collect additional MCP tool assemblies and services from override supplier
- IEnumerable? additionalToolAssemblies = null;
- IEnumerable? additionalMcpServices = null;
- if (configuration.OverrideSupplier is IMcpToolSupplier mcpToolSupplier) {
- additionalToolAssemblies = mcpToolSupplier.GetMcpToolAssemblies();
- additionalMcpServices = mcpToolSupplier.GetMcpServices();
- }
+ // Collect additional MCP tool assemblies and services from override supplier
+ IEnumerable? additionalToolAssemblies = null;
+ IEnumerable? additionalMcpServices = null;
+ if (configuration.OverrideSupplier is IMcpToolSupplier mcpToolSupplier) {
+ additionalToolAssemblies = mcpToolSupplier.GetMcpToolAssemblies();
+ additionalMcpServices = mcpToolSupplier.GetMcpServices();
+ }
+ if (configuration.McpHttpPort != 0) {
mcpHttpTransport = new McpHttpHost(loggerService);
try {
mcpHttpTransport.Start(emulatorMcpServices, configuration.McpHttpPort, additionalToolAssemblies, additionalMcpServices);
diff --git a/tests/Spice86.Tests/CfgCpu/CfgGraphReloadTest.cs b/tests/Spice86.Tests/CfgCpu/CfgGraphReloadTest.cs
index 60c5b2d43d..fa94c84dda 100644
--- a/tests/Spice86.Tests/CfgCpu/CfgGraphReloadTest.cs
+++ b/tests/Spice86.Tests/CfgCpu/CfgGraphReloadTest.cs
@@ -14,6 +14,7 @@ namespace Spice86.Tests.CfgCpu;
using Spice86.Core.Emulator.StateSerialization;
using Spice86.Core.Emulator.StateSerialization.CfgReload;
using Spice86.Core.Emulator.VM;
+using Spice86.Logging;
using Spice86.Shared.Emulator.Memory;
using Spice86.Shared.Interfaces;
using Spice86.Tests.Utility;
@@ -134,6 +135,36 @@ public void ReloadedGraphReExportsToSameDump(string binName) {
Assert.Equal(SerializeDump(original), SerializeDump(reExported));
}
+ [Theory]
+ [MemberData(nameof(RoundTripBins))]
+ public void ReloadRegistersEveryInstructionInTheNodeIndex(string binName) {
+ // The CfgNodeIndex spans ALL nodes, observed and speculative, mirroring the live feeder path
+ // where ParseAndSetAsCurrent calls NodeIndex.Insert for every parsed node (not just
+ // speculative ones). If reload registered only one provenance, the index would be missing
+ // entries; the explorer's convergence check (HasAddress) and the cold-path promote/discard
+ // probe (GetAtAddress) would then mint duplicate nodes or fail to converge onto reloaded code.
+ // Assert the invariant directly: every reconstructed instruction is findable in the index by id.
+ CfgReloadDump original = CaptureDump(binName);
+
+ using LoggerService loggerService = new();
+ using Spice86Creator creator = CreateCreator(binName);
+ using Spice86DependencyInjection di = creator.Create();
+ using CfgNodeExecutionCompiler compiler = NewCompiler(loggerService);
+ CfgGraphReloader reloader = new(di.Machine.CfgCpu, di.Machine.CpuState, compiler, di.CfgIdAllocator);
+ reloader.Reload(original);
+
+ CfgNodeIndex index = di.Machine.CfgCpu.CfgNodeFeeder.NodeIndex;
+ foreach (CfgReloadNodeInfo nodeInfo in original.Nodes) {
+ if (nodeInfo.Type != CfgReloadNodeType.Instruction) {
+ continue;
+ }
+ SegmentedAddress address = ParseAddress(nodeInfo.Addr);
+ bool indexed = index.GetAtAddress(address).Any(node => node.Id == nodeInfo.Id);
+ Assert.True(indexed,
+ $"reloaded instruction id {nodeInfo.Id} at {nodeInfo.Addr} must be registered in the node index");
+ }
+ }
+
[Theory]
[MemberData(nameof(RoundTripBins))]
public void ReloadPreservesIdsAndSeedsAllocator(string binName) {
diff --git a/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs b/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs
index 18d8d25c71..794abb5228 100644
--- a/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs
+++ b/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs
@@ -50,7 +50,7 @@ public class CfgNodeFeederTest : IDisposable {
_compiler?.Dispose();
_compiler = new CfgNodeExecutionCompiler(new CfgNodeExecutionCompilerMonitor(loggerService), loggerService, JitMode.InterpretedOnly);
CfgNodeFeeder cfgNodeFeeder = new(_memory, _state, emulatorBreakpointsManager, replacerRegistry,
- _compiler, new SequentialIdAllocator());
+ _compiler, new SequentialIdAllocator(), enableSpeculativeExploration: false);
ExecutionContextManager executionContextManager = new(_memory, _state, cfgNodeFeeder, replacerRegistry, new(), false, loggerService, null);
ExecutionContext executionContext = executionContextManager.CurrentExecutionContext;
return (cfgNodeFeeder, executionContext);
diff --git a/tests/Spice86.Tests/CfgCpu/CfgNodeIndexTest.cs b/tests/Spice86.Tests/CfgCpu/CfgNodeIndexTest.cs
new file mode 100644
index 0000000000..bb87ac9cbf
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/CfgNodeIndexTest.cs
@@ -0,0 +1,94 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+
+using Xunit;
+
+///
+/// Dedicated unit tests for :
+/// the final-signature lookup tiebreak and the identity-based replacement used by signature-reducer
+/// fan-out.
+///
+public sealed class CfgNodeIndexTest : SpeculativeTestBase {
+ ///
+ /// When an observed and a speculative node share an address and final signature, the lookup must
+ /// return the observed one even if the speculative node was indexed first.
+ ///
+ [Fact]
+ public void GetAtAddressMatchingFinalSignaturePrefersObservedOverSpeculative() {
+ SegmentedAddress address = new(0, 0x100);
+ // Index the speculative variant first so a naive "first match wins" would pick it.
+ CreateSpeculativeNode(address);
+ CfgInstruction observed = CreateObservedNode(address);
+
+ CfgInstruction? match = NodeIndex.GetAtAddressMatchingFinalSignature(address, observed.SignatureFinal);
+
+ match.Should().BeSameAs(observed, "an observed node must win the tiebreak over a speculative one");
+ }
+
+ ///
+ /// With only a speculative node at the address, the lookup returns it.
+ ///
+ [Fact]
+ public void GetAtAddressMatchingFinalSignatureReturnsSpeculativeWhenNoObservedMatches() {
+ SegmentedAddress address = new(0, 0x200);
+ CfgInstruction speculative = CreateSpeculativeNode(address);
+
+ CfgInstruction? match = NodeIndex.GetAtAddressMatchingFinalSignature(address, speculative.SignatureFinal);
+
+ match.Should().BeSameAs(speculative);
+ }
+
+ ///
+ /// A different opcode at the address has a different final signature and must not match: this is
+ /// what keeps distinct self-modified variants separate.
+ ///
+ [Fact]
+ public void GetAtAddressMatchingFinalSignatureReturnsNullWhenNoFinalSignatureMatches() {
+ SegmentedAddress nopAddress = new(0, 0x300);
+ CreateObservedNode(nopAddress); // a NOP
+
+ // Parse a RET elsewhere just to obtain a different final signature to query with.
+ SegmentedAddress retAddress = new(0, 0x310);
+ WriteRet(retAddress);
+ CfgInstruction ret = Parser.ParseInstructionAt(retAddress);
+
+ CfgInstruction? match = NodeIndex.GetAtAddressMatchingFinalSignature(nopAddress, ret.SignatureFinal);
+
+ match.Should().BeNull("a RET final signature must not match the NOP indexed at that address");
+ }
+
+ ///
+ /// ReplaceInstruction swaps the indexed node identity at the address: the old node is gone and the
+ /// replacement is findable, mirroring signature-reducer fan-out.
+ ///
+ [Fact]
+ public void ReplaceInstructionSwapsIndexedNodeIdentityAtAddress() {
+ SegmentedAddress address = new(0, 0x400);
+ CfgInstruction original = CreateObservedNode(address);
+ // A distinct instance at the same address (re-parsed, not inserted).
+ CfgInstruction replacement = WriteNopAndParse(address);
+
+ NodeIndex.ReplaceInstruction(original, replacement);
+
+ NodeIndex.GetAtAddress(address).Should().Contain(replacement);
+ NodeIndex.GetAtAddress(address).Should().NotContain(original);
+ }
+
+ ///
+ /// ReplaceInstruction is a no-op when the old node was never indexed.
+ ///
+ [Fact]
+ public void ReplaceInstructionIsNoOpWhenOldNodeNotIndexed() {
+ SegmentedAddress address = new(0, 0x500);
+ CfgInstruction notIndexed = WriteNopAndParse(address);
+ CfgInstruction replacement = WriteNopAndParse(address);
+
+ NodeIndex.ReplaceInstruction(notIndexed, replacement);
+
+ NodeIndex.HasAddress(address).Should().BeFalse("replacing an unindexed node must not insert anything");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs b/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs
index 77977ba32a..2caed4fd5f 100644
--- a/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs
+++ b/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs
@@ -52,7 +52,7 @@ private InstructionsFeeder CreateInstructionsFeeder(IMmu mmu) {
_compiler = new CfgNodeExecutionCompiler(new CfgNodeExecutionCompilerMonitor(loggerService), loggerService, JitMode.InterpretedOnly);
return new InstructionsFeeder(emulatorBreakpointsManager, _memory, state, _instructionReplacer,
- _compiler, new SequentialIdAllocator());
+ _compiler, new SequentialIdAllocator(), nodeLinker: null);
}
private void WriteJumpNear(SegmentedAddress address) {
diff --git a/tests/Spice86.Tests/CfgCpu/NodeLinkerSpeculativeConflictTest.cs b/tests/Spice86.Tests/CfgCpu/NodeLinkerSpeculativeConflictTest.cs
new file mode 100644
index 0000000000..711de998cd
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/NodeLinkerSpeculativeConflictTest.cs
@@ -0,0 +1,53 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+
+using Xunit;
+
+///
+/// Tests that resolving a successor conflict against a stale speculative node fully discards it
+/// instead of only dropping the incoming edge, so the orphaned node and its exclusively-reachable
+/// speculative subgraph do not linger in the index or the graph.
+///
+public sealed class NodeLinkerSpeculativeConflictTest : SpeculativeTestBase {
+ ///
+ /// current -> speculative S is wired, S owns an exclusively-speculative child S2. When an observed
+ /// node at the same address S is linked from current, the speculative node and its child must be
+ /// swept from the index and fully detached, and current must point at the observed node.
+ ///
+ [Fact]
+ public void LinkingObservedOverStaleSpeculativeSuccessorSweepsIt() {
+ // Arrange
+ SegmentedAddress currentAddress = new(0, 0x100);
+ SegmentedAddress conflictAddress = new(0, 0x200);
+ SegmentedAddress childAddress = new(0, 0x201);
+ CfgInstruction current = CreateObservedNode(currentAddress);
+ CfgInstruction speculativeSuccessor = CreateSpeculativeNode(conflictAddress);
+ CfgInstruction speculativeChild = CreateSpeculativeNode(childAddress);
+ WireEdge(speculativeSuccessor, speculativeChild);
+ NodeLinker.Link(InstructionSuccessorType.Normal, current, speculativeSuccessor);
+
+ CfgInstruction observedSuccessor = CreateObservedNode(conflictAddress);
+
+ // Act
+ NodeLinker.Link(InstructionSuccessorType.Normal, current, observedSuccessor);
+
+ // Assert: the stale speculative node and its exclusively-reachable child are gone.
+ NodeIndex.Contains(speculativeSuccessor).Should().BeFalse("the stale speculative successor must be swept, not just unlinked");
+ NodeIndex.Contains(speculativeChild).Should().BeFalse("the child reachable only through the swept node must go too");
+ speculativeSuccessor.Successors.Should().BeEmpty();
+ speculativeSuccessor.Predecessors.Should().BeEmpty();
+ speculativeChild.Successors.Should().BeEmpty();
+ speculativeChild.Predecessors.Should().BeEmpty();
+
+ // Assert: current now points at the observed node instead of the swept speculative one.
+ current.Successors.Should().Contain(observedSuccessor);
+ current.Successors.Should().NotContain(speculativeSuccessor);
+ current.SuccessorsPerAddress.Should().ContainKey(conflictAddress);
+ current.SuccessorsPerAddress[conflictAddress].Should().Be(observedSuccessor);
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeBlockIntegrityTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeBlockIntegrityTest.cs
new file mode 100644
index 0000000000..f368e4d2fb
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeBlockIntegrityTest.cs
@@ -0,0 +1,150 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+
+using Xunit;
+
+///
+/// Tests for block integrity after speculative reachability sweep.
+/// Validates fully-speculative blocks are removed wholesale, mixed blocks are truncated,
+/// and observed blocks losing a successor edge keep their structure.
+///
+public sealed class SpeculativeBlockIntegrityTest : SpeculativeTestBase {
+ private readonly SpeculativeReachabilityPruner _pruner;
+
+ public SpeculativeBlockIntegrityTest() {
+ _pruner = new SpeculativeReachabilityPruner(ReplacerRegistry);
+ }
+
+ ///
+ /// Fully-speculative block removed wholesale after sweep.
+ /// Arrange: speculative block [A, B, C] (A=entry, C=terminator). All speculative.
+ /// Act: sweep removes A, B, C.
+ /// Assert: ContainingBlock is null for all. The block is not referenced by surviving nodes.
+ ///
+ [Fact]
+ public void FullySpeculativeBlockRemovedWholesaleAfterSweep() {
+ // Arrange: create 3 speculative NOP nodes and wire them into a block
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x100));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x101));
+ CfgInstruction c = CreateSpeculativeNode(new(0, 0x102));
+
+ // Wire edges: A→B→C (straight-line)
+ WireEdge(a, b);
+ WireEdge(b, c);
+
+ // Build the block manually (simulating what the linker would do)
+ CfgBlock block = new(IdAllocator.AllocateId(), a);
+ a.ContainingBlock = block;
+ block.Append(b);
+ b.ContainingBlock = block;
+ block.Append(c);
+ c.ContainingBlock = block;
+
+ block.IsSpeculative.Should().BeTrue("all nodes are speculative");
+
+ // Act
+ _pruner.Sweep(a);
+
+ // Assert
+ a.ContainingBlock.Should().BeNull("swept node should have no block");
+ b.ContainingBlock.Should().BeNull("swept node should have no block");
+ c.ContainingBlock.Should().BeNull("swept node should have no block");
+ }
+
+ ///
+ /// Mixed block truncated to observed prefix after sweep.
+ /// Arrange: block [O1, O2, S1, S2] where O1, O2 observed and S1, S2 speculative.
+ /// Act: sweep removes S1 and S2.
+ /// Assert: block contains only [O1, O2]. Block is not speculative.
+ ///
+ [Fact]
+ public void MixedBlockTruncatedToObservedPrefixAfterSweep() {
+ // Arrange: observed prefix + speculative tail in one block
+ CfgInstruction o1 = CreateObservedNode(new(0, 0x200));
+ CfgInstruction o2 = CreateObservedNode(new(0, 0x201));
+ CfgInstruction s1 = CreateSpeculativeNode(new(0, 0x202));
+ CfgInstruction s2 = CreateSpeculativeNode(new(0, 0x203));
+
+ // Wire chain: O1→O2→S1→S2
+ WireEdge(o1, o2);
+ WireEdge(o2, s1);
+ WireEdge(s1, s2);
+
+ // Build block containing all four
+ CfgBlock block = new(IdAllocator.AllocateId(), o1);
+ o1.ContainingBlock = block;
+ block.Append(o2);
+ o2.ContainingBlock = block;
+ block.Append(s1);
+ s1.ContainingBlock = block;
+ block.Append(s2);
+ s2.ContainingBlock = block;
+
+ block.IsSpeculative.Should().BeTrue("block has speculative nodes");
+ block.Instructions.Count.Should().Be(4);
+ s1.ContainingBlock.Should().Be(block, "sanity: s1 is in block");
+
+ // Act: sweep from S1
+ HashSet removed = _pruner.Sweep(s1);
+
+ // Assert
+ removed.Should().Contain(s1, "S1 should be in the removed set");
+ removed.Should().Contain(s2, "S2 should be in the removed set");
+ block.Instructions.Count.Should().Be(2, "only observed prefix remains");
+ block.Instructions[0].Should().Be(o1);
+ block.Instructions[1].Should().Be(o2);
+ block.Terminator.Should().Be(o2);
+ block.IsSpeculative.Should().BeFalse("no speculative nodes remain");
+ s1.ContainingBlock.Should().BeNull("swept nodes have no block");
+ s2.ContainingBlock.Should().BeNull("swept nodes have no block");
+ }
+
+ ///
+ /// Observed block losing a terminator successor edge keeps structure.
+ /// Arrange: observed block [O1, O2, O3]. O3 has speculative successor S.
+ /// Act: sweep removes S (calls RemoveEdge(O3, S)).
+ /// Assert: block [O1, O2, O3] unchanged. O3.MaxSuccessorsCount unchanged.
+ /// O3.CanHaveMoreSuccessors flipped back to true.
+ ///
+ [Fact]
+ public void ObservedBlockLosingTerminatorSuccessorEdgeKeepsStructure() {
+ // Arrange
+ CfgInstruction o1 = CreateObservedNode(new(0, 0x300));
+ CfgInstruction o2 = CreateObservedNode(new(0, 0x301));
+ CfgInstruction o3 = CreateObservedNode(new(0, 0x302));
+ CfgInstruction s = CreateSpeculativeNode(new(0, 0x310));
+
+ // Build observed block
+ CfgBlock block = new(IdAllocator.AllocateId(), o1);
+ o1.ContainingBlock = block;
+ block.Append(o2);
+ o2.ContainingBlock = block;
+ block.Append(o3);
+ o3.ContainingBlock = block;
+
+ // O3 is terminator with one speculative successor
+ o3.MaxSuccessorsCount = 1;
+ WireEdge(o3, s);
+ o3.CanHaveMoreSuccessors.Should().BeFalse("cap is 1, one successor exists");
+
+ // Act: sweep removes S
+ _pruner.Sweep(s);
+
+ // Assert: block unchanged
+ block.Instructions.Count.Should().Be(3);
+ block.Entry.Should().Be(o1);
+ block.Terminator.Should().Be(o3);
+ block.IsSpeculative.Should().BeFalse();
+
+ // O3 had its successor removed
+ o3.Successors.Should().NotContain(s);
+ o3.MaxSuccessorsCount.Should().Be(1, "cap should not change");
+ o3.CanHaveMoreSuccessors.Should().BeTrue("slot reopened after removal");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeExplorerEdgeWiringTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeExplorerEdgeWiringTest.cs
new file mode 100644
index 0000000000..f3cc899fe8
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeExplorerEdgeWiringTest.cs
@@ -0,0 +1,640 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.Linker;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Utils;
+
+using System.Linq;
+
+using Xunit;
+
+///
+/// Tests for Speculative CFG Explorer edge wiring.
+/// These validate that the explorer correctly creates successor/predecessor edges between speculative
+/// nodes and their predecessors during exploration.
+///
+public sealed class SpeculativeExplorerEdgeWiringTest : SpeculativeTestBase {
+ private readonly SpeculativeExplorer _explorer;
+
+ public SpeculativeExplorerEdgeWiringTest() {
+ _explorer = new SpeculativeExplorer(Parser, NodeIndex, NodeLinker);
+ }
+
+ ///
+ /// Explorer wires successor edge from seed to speculative node.
+ /// Arrange: JNZ at address A with static successor at address B.
+ /// Act: ExploreFrom(A).
+ /// Assert: A speculative node exists at B, A.Successors contains the node at B,
+ /// and nodeAtB.Predecessors contains A.
+ ///
+ [Fact]
+ public void ExplorerWiresSuccessorEdgeFromSeedToSpeculativeNode() {
+ // Arrange: JNZ at 0:0x100 with rel8=+4 -> target is 0:0x106 (after 2-byte JNZ instruction at 0x100, +4)
+ // JNZ is 2 bytes, so next instruction is at 0x102, and target = 0x102 + 4 = 0x106
+ SegmentedAddress addrA = new(0, 0x100);
+ // Write a NOP at the fall-through (0x102) so exploration can decode it
+ WriteNop(new SegmentedAddress(0, 0x102));
+ // Write a NOP at the branch target (0x106) so exploration can decode it
+ WriteNop(new SegmentedAddress(0, 0x106));
+ // Write RETs after so exploration terminates
+ WriteRet(new SegmentedAddress(0, 0x103));
+ WriteRet(new SegmentedAddress(0, 0x107));
+
+ CfgInstruction observedJnz = WriteConditionalJnzAndParse(addrA, 4);
+ // Insert the observed node into the index (simulating first-parse)
+ NodeIndex.Insert(observedJnz);
+
+ // Act
+ _explorer.ExploreFrom(observedJnz);
+
+ // Assert: speculative nodes exist at the static successors
+ SegmentedAddress addrFallthrough = new(0, 0x102); // JNZ is 2 bytes
+ SegmentedAddress addrTarget = new(0, 0x106); // 0x102 + 4
+
+ NodeIndex.HasAddress(addrFallthrough).Should().BeTrue("fall-through should have been explored");
+ NodeIndex.HasAddress(addrTarget).Should().BeTrue("branch target should have been explored");
+
+ // The speculative nodes should have successor edges from the observed JNZ
+ CfgInstruction speculativeFallthrough = NodeIndex.GetAtAddress(addrFallthrough).First();
+ CfgInstruction speculativeTarget = NodeIndex.GetAtAddress(addrTarget).First();
+
+ speculativeFallthrough.IsSpeculative.Should().BeTrue();
+ speculativeTarget.IsSpeculative.Should().BeTrue();
+
+ // Key assertion: full bidirectional edges are wired via NodeLinker
+ speculativeFallthrough.Predecessors.Should().Contain(observedJnz,
+ "speculative fall-through should have predecessor edge from observed JNZ");
+ speculativeTarget.Predecessors.Should().Contain(observedJnz,
+ "speculative branch target should have predecessor edge from observed JNZ");
+ observedJnz.Successors.Should().Contain(speculativeFallthrough,
+ "observed JNZ should have successor edge to speculative fall-through");
+ observedJnz.Successors.Should().Contain(speculativeTarget,
+ "observed JNZ should have successor edge to speculative branch target");
+ }
+
+ ///
+ /// Explorer wires edges along a speculative chain.
+ /// Arrange: observed node at A with StaticSuccessorAddresses = [B]. B is a NOP falling through to C. C is RET.
+ /// Act: ExploreFrom(A).
+ /// Assert: node at B has successor edge to node at C. Node at C has predecessor edge from B.
+ ///
+ [Fact]
+ public void ExplorerWiresEdgesAlongSpeculativeChain() {
+ // Arrange: JMP short at A targeting B. B is NOP falling through to C. C is RET.
+ SegmentedAddress addrA = new(0, 0x200);
+ SegmentedAddress addrB = new(0, 0x204); // target of JMP from A: 0x202 + 2 = 0x204
+ SegmentedAddress addrC = new(0, 0x205); // B is NOP (1 byte), so C = B + 1
+
+ // Write JMP short at A targeting B (rel8 = +2, since JMP is 2 bytes, next is 0x202, target is 0x204)
+ WriteJmpShort(addrA, 2);
+ WriteNop(addrB);
+ WriteRet(addrC);
+
+ CfgInstruction observedJmp = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedJmp);
+
+ // Act
+ _explorer.ExploreFrom(observedJmp);
+
+ // Assert
+ CfgInstruction nodeB = NodeIndex.GetAtAddress(addrB).First();
+ CfgInstruction nodeC = NodeIndex.GetAtAddress(addrC).First();
+ nodeB.IsSpeculative.Should().BeTrue();
+ nodeC.IsSpeculative.Should().BeTrue();
+
+ // Edge from observed A to speculative B (full bidirectional)
+ nodeB.Predecessors.Should().Contain(observedJmp, "B should have predecessor edge from A");
+ observedJmp.Successors.Should().Contain(nodeB, "A should have successor edge to B");
+
+ // Edge from speculative B to speculative C (full bidirectional)
+ nodeC.Predecessors.Should().Contain(nodeB, "C should have predecessor edge from B");
+ nodeB.Successors.Should().Contain(nodeC, "B should have successor edge to C");
+ }
+
+ ///
+ /// Explorer creates convergence edge to existing index node.
+ /// Arrange: observed node at A with static successor B. Index already contains an observed node at B.
+ /// Act: ExploreFrom(A).
+ /// Assert: A.Successors contains the existing node at B. No new node minted at B.
+ ///
+ [Fact]
+ public void ExplorerCreatesConvergenceEdgeToExistingObservedNode() {
+ // Arrange: JMP short at A targeting B. B already in index (observed).
+ SegmentedAddress addrA = new(0, 0x300);
+ SegmentedAddress addrB = new(0, 0x304); // JMP is 2 bytes, rel8=+2, target = 0x302 + 2 = 0x304
+
+ WriteJmpShort(addrA, 2);
+ WriteNop(addrB);
+
+ CfgInstruction observedJmp = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedJmp);
+
+ // Pre-insert an observed node at B
+ CfgInstruction existingObservedB = Parser.ParseInstructionAt(addrB);
+ NodeIndex.Insert(existingObservedB);
+ int nodeCountBefore = NodeIndex.GetAtAddress(addrB).Count();
+
+ // Act
+ _explorer.ExploreFrom(observedJmp);
+
+ // Assert: A has predecessor wired to existing B (convergence), no duplicate
+ int nodeCountAfter = NodeIndex.GetAtAddress(addrB).Count();
+ nodeCountAfter.Should().Be(nodeCountBefore, "no new node should be minted at B");
+
+ existingObservedB.Predecessors.Should().Contain(observedJmp,
+ "existing B should have predecessor edge from A");
+ }
+
+ ///
+ /// Explorer creates convergence edge to existing speculative node.
+ /// Arrange: two observed nodes X and Y both have static successor Z.
+ /// ExploreFrom(X) creates speculative Z.
+ /// Act: ExploreFrom(Y).
+ /// Assert: Y.Successors contains the same speculative node at Z (not a second copy).
+ /// Z has both X and Y in Predecessors.
+ ///
+ [Fact]
+ public void ExplorerCreatesConvergenceEdgeToExistingSpeculativeNode() {
+ // Arrange: JMP short at X targeting Z, JMP short at Y targeting Z.
+ SegmentedAddress addrX = new(0, 0x400);
+ SegmentedAddress addrY = new(0, 0x410);
+ SegmentedAddress addrZ = new(0, 0x420);
+
+ // JMP at X: 2 bytes, next=0x402, target=0x420 => rel8 = 0x420 - 0x402 = 0x1E (30)
+ WriteJmpShort(addrX, 0x1E);
+ // JMP at Y: 2 bytes, next=0x412, target=0x420 => rel8 = 0x420 - 0x412 = 0x0E (14)
+ WriteJmpShort(addrY, 0x0E);
+ // Write a RET at Z so exploration terminates
+ WriteRet(addrZ);
+
+ CfgInstruction observedX = Parser.ParseInstructionAt(addrX);
+ NodeIndex.Insert(observedX);
+ CfgInstruction observedY = Parser.ParseInstructionAt(addrY);
+ NodeIndex.Insert(observedY);
+
+ // First exploration from X creates speculative Z
+ _explorer.ExploreFrom(observedX);
+ CfgInstruction? speculativeZ = NodeIndex.GetAtAddress(addrZ).FirstOrDefault();
+ speculativeZ.Should().NotBeNull();
+
+ // Act: explore from Y
+ _explorer.ExploreFrom(observedY);
+
+ // Assert: Y has edge to the SAME speculative Z, no duplicate
+ int nodeCountAtZ = NodeIndex.GetAtAddress(addrZ).Count();
+ nodeCountAtZ.Should().Be(1, "only one node should exist at Z");
+
+ CfgInstruction speculativeZNode = NodeIndex.GetAtAddress(addrZ).First();
+ speculativeZNode.IsSpeculative.Should().BeTrue();
+
+ observedY.Successors.Should().Contain(speculativeZNode,
+ "explorer now wires full successor edges via NodeLinker");
+ speculativeZNode.Predecessors.Should().Contain(observedX, "Z should have predecessor from X");
+ speculativeZNode.Predecessors.Should().Contain(observedY, "Z should have predecessor from Y");
+ }
+
+ ///
+ /// Explorer stops at poisoned address (no edge, no node).
+ ///
+ [Fact]
+ public void ExplorerStopsAtPoisonedAddress() {
+ // Arrange: JMP short at A targeting P. P is poisoned.
+ SegmentedAddress addrA = new(0, 0x500);
+ SegmentedAddress addrP = new(0, 0x504); // JMP is 2 bytes, rel8=+2
+
+ WriteJmpShort(addrA, 2);
+ WriteNop(addrP); // Write something valid at P (but it's poisoned)
+
+ CfgInstruction observedA = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedA);
+ NodeIndex.PoisonSet.Add(addrP);
+
+ // Act
+ _explorer.ExploreFrom(observedA);
+
+ // Assert: no node at P, no edge from A to P
+ NodeIndex.HasAddress(addrP).Should().BeFalse("poisoned address should not get a node");
+ }
+
+ ///
+ /// Explorer stops at invalid opcode and poisons the address.
+ ///
+ [Fact]
+ public void ExplorerStopsAtInvalidOpcodeAndPoisonsAddress() {
+ // Arrange: JMP short at A targeting I. I contains an invalid opcode (0xFF 0xFF).
+ SegmentedAddress addrA = new(0, 0x600);
+ SegmentedAddress addrI = new(0, 0x604); // JMP is 2 bytes, rel8=+2
+
+ WriteJmpShort(addrA, 2);
+ // Write invalid opcode at I: 0x0F 0xFF is invalid in real mode
+ uint physI = MemoryUtils.ToPhysicalAddress(addrI.Segment, addrI.Offset);
+ Memory.UInt8[physI] = 0x0F;
+ Memory.UInt8[physI + 1] = 0xFF;
+
+ CfgInstruction observedA = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedA);
+
+ // Act
+ _explorer.ExploreFrom(observedA);
+
+ // Assert
+ NodeIndex.PoisonSet.Should().Contain(addrI, "invalid opcode address should be poisoned");
+ observedA.Successors.Should().BeEmpty("A should have no successor edge to invalid I");
+ }
+
+ ///
+ /// Explorer does not speculate call continuations.
+ /// Arrange: observed CALL near at A targeting callee C. Instruction after the call is A+N.
+ /// Act: ExploreFrom(A).
+ /// Assert: speculative node at C (callee IS explored). No speculative node at A+N.
+ ///
+ [Fact]
+ public void ExplorerDoesNotSpeculateCallContinuations() {
+ // Arrange: CALL near at address A (opcode E8 + rel16).
+ // CALL near rel16 is 3 bytes: E8 lo hi.
+ SegmentedAddress addrA = new(0, 0x700);
+ SegmentedAddress addrCallee = new(0, 0x720); // target
+ SegmentedAddress addrContinuation = new(0, 0x703); // A + 3 (size of CALL near rel16)
+
+ // Write CALL near at A targeting 0x720: rel16 = 0x720 - 0x703 = 0x1D
+ uint physA = MemoryUtils.ToPhysicalAddress(addrA.Segment, addrA.Offset);
+ Memory.UInt8[physA] = 0xE8; // CALL rel16
+ Memory.UInt16[physA + 1] = 0x001D; // rel16 = 0x1D -> target = 0x703 + 0x1D = 0x720
+
+ // Write a RET at the callee so exploration terminates there
+ WriteRet(addrCallee);
+ // Write a NOP at the continuation
+ WriteNop(addrContinuation);
+
+ CfgInstruction observedCall = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedCall);
+
+ // Act
+ _explorer.ExploreFrom(observedCall);
+
+ // Assert: callee IS explored
+ NodeIndex.HasAddress(addrCallee).Should().BeTrue("callee entry should be explored");
+ CfgInstruction calleeNode = NodeIndex.GetAtAddress(addrCallee).First();
+ calleeNode.IsSpeculative.Should().BeTrue();
+
+ // Call continuation is NOT explored
+ bool hasContinuation = NodeIndex.GetAtAddress(addrContinuation)
+ .Any(n => n.IsSpeculative);
+ hasContinuation.Should().BeFalse("call continuation should NOT be speculated");
+ }
+
+ ///
+ /// Explorer must not converge across a different opcode at the same address.
+ /// Two predecessors X and Y both target Z. Between explorations the byte at Z is rewritten with
+ /// a different opcode (self-modifying code). The explorer must NOT wire Y onto X's stale variant;
+ /// it must mint a distinct variant matching the current memory and wire Y to it. This is the
+ /// regression guard for the cross-variant convergence leak that forced a spurious SelectorNode.
+ ///
+ [Fact]
+ public void ExplorerDoesNotConvergeAcrossDifferentOpcodeAtSameAddress() {
+ SegmentedAddress addrX = new(0, 0x100);
+ SegmentedAddress addrY = new(0, 0x110);
+ SegmentedAddress addrZ = new(0, 0x120);
+
+ // JMP at X: next=0x102, target=0x120 => rel8=0x1E
+ WriteJmpShort(addrX, 0x1E);
+ // JMP at Y: next=0x112, target=0x120 => rel8=0x0E
+ WriteJmpShort(addrY, 0x0E);
+
+ // First era: Z is RET.
+ WriteRet(addrZ);
+ CfgInstruction observedX = Parser.ParseInstructionAt(addrX);
+ NodeIndex.Insert(observedX);
+ _explorer.ExploreFrom(observedX);
+ CfgInstruction variantRet = NodeIndex.GetAtAddress(addrZ).Single();
+ variantRet.IsSpeculative.Should().BeTrue();
+
+ // Second era: Z now decodes to HLT (different opcode => different SignatureFinal).
+ uint physZ = MemoryUtils.ToPhysicalAddress(addrZ.Segment, addrZ.Offset);
+ Memory.UInt8[physZ] = 0xF4; // HLT
+ CfgInstruction observedY = Parser.ParseInstructionAt(addrY);
+ NodeIndex.Insert(observedY);
+ _explorer.ExploreFrom(observedY);
+
+ // Two distinct variants now live at Z, each wired to its own predecessor.
+ NodeIndex.GetAtAddress(addrZ).Should().HaveCount(2,
+ "a different opcode at the same address must mint a distinct variant, not converge");
+ CfgInstruction variantHlt = NodeIndex.GetAtAddress(addrZ).Single(n => !ReferenceEquals(n, variantRet));
+
+ observedX.Successors.Should().Contain(variantRet);
+ observedY.Successors.Should().Contain(variantHlt);
+ observedY.Successors.Should().NotContain(variantRet,
+ "Y must not be wired to X's stale-opcode variant (the cross-variant convergence leak)");
+ }
+
+ ///
+ /// Explorer terminates on cyclic backward branch.
+ /// Arrange: observed JNZ at A with static successors B and C.
+ /// B is a JMP short targeting A (backward branch forming a cycle).
+ /// Act: ExploreFrom(A).
+ /// Assert: Exploration terminates (no infinite loop). Node at B with edge back to A.
+ ///
+ [Fact]
+ public void ExplorerTerminatesOnCyclicBackwardBranch() {
+ // Arrange: JNZ at A (0x800) with fall-through at B (0x802) and target at C (0x806).
+ // B is JMP short back to A: rel8 = A - (B+2) = 0x800 - 0x804 = -4
+ SegmentedAddress addrA = new(0, 0x800);
+ SegmentedAddress addrB = new(0, 0x802); // fall-through (JNZ is 2 bytes)
+ SegmentedAddress addrC = new(0, 0x806); // target: 0x802 + 4 = 0x806
+
+ // JNZ at A with rel8=+4 (target = 0x802 + 4 = 0x806)
+ uint physA = MemoryUtils.ToPhysicalAddress(addrA.Segment, addrA.Offset);
+ Memory.UInt8[physA] = 0x75;
+ Memory.Int8[physA + 1] = 4;
+
+ // JMP short at B back to A: JMP is 2 bytes, next=0x804, target=0x800, rel8 = 0x800 - 0x804 = -4
+ WriteJmpShort(addrB, -4);
+ // RET at C
+ WriteRet(addrC);
+
+ CfgInstruction observedA = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedA);
+
+ // Act: should terminate (no infinite loop)
+ _explorer.ExploreFrom(observedA);
+
+ // Assert
+ NodeIndex.HasAddress(addrB).Should().BeTrue("B should be explored");
+ NodeIndex.HasAddress(addrC).Should().BeTrue("C should be explored");
+
+ CfgInstruction? nodeB = NodeIndex.GetAtAddress(addrB).FirstOrDefault();
+ nodeB.Should().NotBeNull();
+
+ // B should have a predecessor edge wired to A (convergence edge via backward branch)
+ observedA.Predecessors.Should().Contain(nodeB,
+ "A should have predecessor edge from B (backward branch)");
+ }
+
+ // -----------------------------------------------------------------------
+ // Known-Safe Handler Seeding Tests
+ // -----------------------------------------------------------------------
+
+ ///
+ /// SeedKnownSafe with a simple callback + iret body produces the expected speculative
+ /// chain. This is the common handler shape (most interrupt handlers).
+ ///
+ [Fact]
+ public void SeedKnownSafeCallbackPlusIretProducesFullChain() {
+ // Arrange: at address 0:0x900, write a callback (FE 38 xx xx) + IRET (CF)
+ SegmentedAddress entry = new(0, 0x900);
+ uint physAddr = MemoryUtils.ToPhysicalAddress(entry.Segment, entry.Offset);
+ // Callback: FE 38 00 00 (4 bytes)
+ Memory.UInt8[physAddr] = 0xFE;
+ Memory.UInt8[physAddr + 1] = 0x38;
+ Memory.UInt8[physAddr + 2] = 0x00;
+ Memory.UInt8[physAddr + 3] = 0x00;
+ // IRET: CF (1 byte)
+ Memory.UInt8[physAddr + 4] = 0xCF;
+
+ // Act
+ _explorer.SeedKnownSafe(entry);
+
+ // Assert: both instructions exist in the index as speculative
+ NodeIndex.HasAddress(entry).Should().BeTrue("entry callback should be indexed");
+ SegmentedAddress iretAddr = new(0, 0x904);
+ NodeIndex.HasAddress(iretAddr).Should().BeTrue("iret should be indexed");
+
+ CfgInstruction callback = NodeIndex.GetAtAddress(entry).First();
+ CfgInstruction iret = NodeIndex.GetAtAddress(iretAddr).First();
+ callback.IsSpeculative.Should().BeTrue();
+ iret.IsSpeculative.Should().BeTrue();
+
+ // Chain: callback -> iret (successor edge)
+ callback.Successors.Should().Contain(iret);
+ iret.Predecessors.Should().Contain(callback);
+ }
+
+ ///
+ /// SeedKnownSafe is a no-op when the explorer is disabled (no node created).
+ /// We test this by verifying that when no explorer exists in the feeder, the seed does nothing.
+ /// Since we test the explorer directly here, we verify it's a no-op when address is already indexed.
+ ///
+ [Fact]
+ public void SeedKnownSafeAddressAlreadyIndexedIsNoOp() {
+ // Arrange: insert an observed node at the entry
+ SegmentedAddress entry = new(0, 0xA00);
+ uint physAddr = MemoryUtils.ToPhysicalAddress(entry.Segment, entry.Offset);
+ Memory.UInt8[physAddr] = 0x90; // NOP
+
+ CfgInstruction observed = Parser.ParseInstructionAt(entry);
+ NodeIndex.Insert(observed);
+ int countBefore = NodeIndex.GetAtAddress(entry).Count();
+
+ // Act
+ _explorer.SeedKnownSafe(entry);
+
+ // Assert: no duplicate
+ int countAfter = NodeIndex.GetAtAddress(entry).Count();
+ countAfter.Should().Be(countBefore);
+ }
+
+ ///
+ /// SeedKnownSafe with INT N + continuation (timer shape) wires CallToReturn edge
+ /// and follows the continuation.
+ /// Handler shape: callback + INT 0x1C + callback + IRET
+ ///
+ [Fact]
+ public void SeedKnownSafeIntPlusContinuationWiresCallToReturnEdge() {
+ // Arrange: at 0:0xB00 write: callback(4) + INT 0x1C(2) + callback(4) + IRET(1)
+ SegmentedAddress entry = new(0, 0xB00);
+ uint phys = MemoryUtils.ToPhysicalAddress(entry.Segment, entry.Offset);
+ // Callback: FE 38 08 00
+ Memory.UInt8[phys] = 0xFE;
+ Memory.UInt8[phys + 1] = 0x38;
+ Memory.UInt8[phys + 2] = 0x08;
+ Memory.UInt8[phys + 3] = 0x00;
+ // INT 0x1C: CD 1C
+ Memory.UInt8[phys + 4] = 0xCD;
+ Memory.UInt8[phys + 5] = 0x1C;
+ // Callback: FE 38 09 00
+ Memory.UInt8[phys + 6] = 0xFE;
+ Memory.UInt8[phys + 7] = 0x38;
+ Memory.UInt8[phys + 8] = 0x09;
+ Memory.UInt8[phys + 9] = 0x00;
+ // IRET: CF
+ Memory.UInt8[phys + 10] = 0xCF;
+
+ // Act
+ _explorer.SeedKnownSafe(entry);
+
+ // Assert: all four instructions indexed
+ SegmentedAddress callbackAddr = entry;
+ SegmentedAddress intAddr = new(0, 0xB04);
+ SegmentedAddress afterIntAddr = new(0, 0xB06);
+ SegmentedAddress iretAddr = new(0, 0xB0A);
+
+ NodeIndex.HasAddress(callbackAddr).Should().BeTrue();
+ NodeIndex.HasAddress(intAddr).Should().BeTrue();
+ NodeIndex.HasAddress(afterIntAddr).Should().BeTrue("continuation after INT should be explored under trust");
+ NodeIndex.HasAddress(iretAddr).Should().BeTrue();
+
+ CfgInstruction intNode = NodeIndex.GetAtAddress(intAddr).First();
+ CfgInstruction afterInt = NodeIndex.GetAtAddress(afterIntAddr).First();
+
+ // The INT -> continuation edge should be CallToReturn
+ intNode.SuccessorsPerType.Should().ContainKey(InstructionSuccessorType.CallToReturn);
+ intNode.SuccessorsPerType[InstructionSuccessorType.CallToReturn].Should().Contain(afterInt);
+ afterInt.Predecessors.Should().Contain(intNode);
+ }
+
+ ///
+ /// SeedKnownSafe with far call (0x9A) to RETF stub + callback + IRET (mouse shape).
+ /// The far call's callee is NOT explored (far call imm does not register a static successor for
+ /// the callee - its operand is switchable via InMemoryAddressSwitcher). Only the continuation
+ /// after the far call is explored under trust with a CallToReturn edge.
+ ///
+ [Fact]
+ public void SeedKnownSafeFarCallToStubWiresCallToReturnForContinuation() {
+ // Arrange: at 0:0xC00 write RETF (CB), then at 0:0xC01 write far call 0:0xC00 (9A 00 0C 00 00)
+ // then callback(4) + IRET(1)
+ SegmentedAddress retfStubAddr = new(0, 0xC00);
+ uint physRetf = MemoryUtils.ToPhysicalAddress(retfStubAddr.Segment, retfStubAddr.Offset);
+ Memory.UInt8[physRetf] = 0xCB; // RETF
+
+ SegmentedAddress handlerEntry = new(0, 0xC01);
+ uint physEntry = MemoryUtils.ToPhysicalAddress(handlerEntry.Segment, handlerEntry.Offset);
+ // Far CALL: 9A offset_lo offset_hi seg_lo seg_hi -> CALL FAR 0000:0C00
+ Memory.UInt8[physEntry] = 0x9A;
+ Memory.UInt8[physEntry + 1] = 0x00; // offset low
+ Memory.UInt8[physEntry + 2] = 0x0C; // offset high
+ Memory.UInt8[physEntry + 3] = 0x00; // segment low
+ Memory.UInt8[physEntry + 4] = 0x00; // segment high
+ // Continuation at entry+5 = 0xC06
+ // Callback: FE 38 74 00
+ Memory.UInt8[physEntry + 5] = 0xFE;
+ Memory.UInt8[physEntry + 6] = 0x38;
+ Memory.UInt8[physEntry + 7] = 0x74;
+ Memory.UInt8[physEntry + 8] = 0x00;
+ // IRET: CF
+ Memory.UInt8[physEntry + 9] = 0xCF;
+
+ // Act
+ _explorer.SeedKnownSafe(handlerEntry);
+
+ // Assert: handler entry, callee, and continuation are all explored.
+ // Far call imm now registers its target as a static successor (same as JMP FAR imm).
+ // Self-modifying code cases use selectors which block speculative discovery separately.
+ SegmentedAddress continuationAddr = new(0, 0xC06);
+ SegmentedAddress iretAddr = new(0, 0xC0A);
+
+ NodeIndex.HasAddress(handlerEntry).Should().BeTrue("far call instruction should be indexed");
+ NodeIndex.HasAddress(continuationAddr).Should().BeTrue("continuation after far call should be explored under trust");
+ NodeIndex.HasAddress(iretAddr).Should().BeTrue("IRET terminator should be explored");
+
+ // The callee (RETF stub) at 0:0xC00 IS explored: far call imm registers target as static successor
+ NodeIndex.HasAddress(retfStubAddr).Should().BeTrue(
+ "far call imm callee is explored via its static successor (self-modifying code uses selectors to block this)");
+
+ CfgInstruction farCallNode = NodeIndex.GetAtAddress(handlerEntry).First();
+ CfgInstruction continuationNode = NodeIndex.GetAtAddress(continuationAddr).First();
+
+ // Far call -> continuation should be CallToReturn edge
+ farCallNode.SuccessorsPerType.Should().ContainKey(InstructionSuccessorType.CallToReturn);
+ farCallNode.SuccessorsPerType[InstructionSuccessorType.CallToReturn].Should().Contain(continuationNode);
+ }
+
+ ///
+ /// Trust does NOT cross the call boundary into the callee.
+ /// A callee that itself contains a CALL should NOT have its own call-continuation explored.
+ ///
+ [Fact]
+ public void SeedKnownSafeTrustDoesNotCrossCallBoundary() {
+ // Arrange: Handler at 0:0xD00 = CALL NEAR 0xD10(3) + callback(4) + IRET(1)
+ // Callee at 0:0xD10 = CALL NEAR 0xD20(3) + RET(1)
+ // Nested callee at 0:0xD20 = RET(1)
+ SegmentedAddress handlerEntry = new(0, 0xD00);
+ uint physHandler = MemoryUtils.ToPhysicalAddress(handlerEntry.Segment, handlerEntry.Offset);
+
+ // CALL NEAR 0xD10: E8 rel16 where rel16 = 0xD10 - 0xD03 = 0x0D
+ Memory.UInt8[physHandler] = 0xE8;
+ Memory.UInt16[physHandler + 1] = 0x000D; // target = 0xD03 + 0x0D = 0xD10
+
+ // Callback at 0xD03: FE 38 00 00
+ Memory.UInt8[physHandler + 3] = 0xFE;
+ Memory.UInt8[physHandler + 4] = 0x38;
+ Memory.UInt8[physHandler + 5] = 0x00;
+ Memory.UInt8[physHandler + 6] = 0x00;
+ // IRET at 0xD07: CF
+ Memory.UInt8[physHandler + 7] = 0xCF;
+
+ // Callee at 0xD10: CALL NEAR 0xD20, E8 rel16 where rel16 = 0xD20 - 0xD13 = 0x0D
+ uint physCallee = MemoryUtils.ToPhysicalAddress(0, 0xD10);
+ Memory.UInt8[physCallee] = 0xE8;
+ Memory.UInt16[physCallee + 1] = 0x000D; // target = 0xD13 + 0x0D = 0xD20
+ // RET at 0xD13
+ Memory.UInt8[physCallee + 3] = 0xC3;
+
+ // Nested callee at 0xD20: RET
+ uint physNested = MemoryUtils.ToPhysicalAddress(0, 0xD20);
+ Memory.UInt8[physNested] = 0xC3;
+
+ // Act
+ _explorer.SeedKnownSafe(handlerEntry);
+
+ // Assert: handler's call continuation at 0xD03 IS explored (trust follows continuation of handler's call)
+ SegmentedAddress handlerContinuation = new(0, 0xD03);
+ NodeIndex.HasAddress(handlerContinuation).Should().BeTrue(
+ "handler's own call continuation should be explored under trust");
+
+ // The callee at 0xD10 IS explored (callee entry is always explored, just untrusted)
+ SegmentedAddress calleeEntry = new(0, 0xD10);
+ NodeIndex.HasAddress(calleeEntry).Should().BeTrue("callee entry should be explored");
+
+ // The nested callee at 0xD20 IS explored (it's the callee's callee entry)
+ SegmentedAddress nestedCalleeEntry = new(0, 0xD20);
+ NodeIndex.HasAddress(nestedCalleeEntry).Should().BeTrue("nested callee entry is explored");
+
+ // But the callee's OWN continuation at 0xD13 should NOT be explored
+ // because trust was dropped when entering the callee.
+ SegmentedAddress calleeContinuation = new(0, 0xD13);
+ bool hasCalleeContinuation = NodeIndex.GetAtAddress(calleeContinuation)
+ .Any(n => n.IsSpeculative);
+ hasCalleeContinuation.Should().BeFalse(
+ "callee's call continuation should NOT be explored because trust does not cross call boundary");
+ }
+
+ ///
+ /// Non-seeded exploration of the same code does NOT produce continuation edges.
+ ///
+ [Fact]
+ public void ExploreFromDoesNotProduceContinuationEdges() {
+ // Arrange: at 0:0xE00, observed CALL NEAR targeting 0xE10, with a NOP at the continuation 0xE03
+ SegmentedAddress addrA = new(0, 0xE00);
+ uint physA = MemoryUtils.ToPhysicalAddress(addrA.Segment, addrA.Offset);
+ // CALL NEAR 0xE10: E8 rel16 where rel16 = 0xE10 - 0xE03 = 0x0D
+ Memory.UInt8[physA] = 0xE8;
+ Memory.UInt16[physA + 1] = 0x000D;
+ // RET at callee 0xE10
+ WriteRet(new SegmentedAddress(0, 0xE10));
+ // NOP at continuation 0xE03
+ WriteNop(new SegmentedAddress(0, 0xE03));
+
+ CfgInstruction observedCall = Parser.ParseInstructionAt(addrA);
+ NodeIndex.Insert(observedCall);
+
+ // Act: normal (non-seeded) exploration
+ _explorer.ExploreFrom(observedCall);
+
+ // Assert: continuation NOT explored
+ bool hasContinuation = NodeIndex.GetAtAddress(new SegmentedAddress(0, 0xE03))
+ .Any(n => n.IsSpeculative);
+ hasContinuation.Should().BeFalse(
+ "non-seeded exploration must NOT speculate call continuations");
+
+ // But the callee IS explored
+ NodeIndex.HasAddress(new SegmentedAddress(0, 0xE10)).Should().BeTrue("callee should be explored");
+
+ // And no CallToReturn edge exists
+ observedCall.SuccessorsPerType.Should().NotContainKey(InstructionSuccessorType.CallToReturn);
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeLinkerAndPromotionTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeLinkerAndPromotionTest.cs
new file mode 100644
index 0000000000..311479d66d
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeLinkerAndPromotionTest.cs
@@ -0,0 +1,270 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using NSubstitute;
+
+using Spice86.Core.CLI;
+using Spice86.Core.Emulator.CPU;
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Exceptions;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.InstructionExecutor.Expressions;
+using Spice86.Core.Emulator.CPU.CfgCpu.Linker;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.Parser;
+using Spice86.Core.Emulator.Memory;
+using Spice86.Core.Emulator.Memory.Mmu;
+using Spice86.Core.Emulator.VM;
+using Spice86.Core.Emulator.VM.Breakpoint;
+using Spice86.Logging;
+using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Interfaces;
+using Spice86.Shared.Utils;
+
+using System;
+
+using Xunit;
+
+///
+/// Tests for provenance-aware linker/selector safety and speculative promotion correctness.
+///
+public sealed class SpeculativeLinkerAndPromotionTest : SpeculativeTestBase {
+ private readonly SpeculativePromoter _promoter;
+ private readonly CurrentInstructions _currentInstructions;
+ private readonly PreviousInstructions _previousInstructions;
+
+ public SpeculativeLinkerAndPromotionTest() {
+ AddressReadWriteBreakpoints memoryBreakpoints = new();
+ AddressReadWriteBreakpoints ioBreakpoints = new();
+ EmulatorBreakpointsManager breakpointsManager = new(new PauseHandler(LoggerService), State, Memory, memoryBreakpoints, ioBreakpoints);
+ _currentInstructions = new CurrentInstructions(Memory, breakpointsManager, ReplacerRegistry);
+ _previousInstructions = new PreviousInstructions(Memory, ReplacerRegistry);
+ _promoter = new SpeculativePromoter(Compiler, _currentInstructions, _previousInstructions);
+ }
+
+ ///
+ /// ResolveSuccessorConflict with existing speculative node discards it, no selector.
+ /// Arrange: observed node P has a speculative successor S at address X.
+ /// A fresh observed node N arrives at address X.
+ /// Act: Link(Normal, P, N) triggers conflict resolution.
+ /// Assert: S removed from P's successors. N linked. No SelectorNode created.
+ ///
+ [Fact]
+ public void ResolveSuccessorConflictWithExistingSpeculativeDiscardsNoSelector() {
+ // Arrange
+ SegmentedAddress addrP = new(0, 0x100);
+ SegmentedAddress addrX = new(0, 0x110);
+
+ CfgInstruction p = CreateObservedNode(addrP);
+ CfgInstruction s = CreateSpeculativeNode(addrX);
+ p.MaxSuccessorsCount = 2;
+ WireEdge(p, s);
+
+ // Create a fresh observed node at the same address X
+ CfgInstruction n = CreateObservedNode(addrX);
+
+ // Act: link P -> N should resolve conflict by discarding S
+ ICfgNode resolved = NodeLinker.Link(InstructionSuccessorType.Normal, p, n);
+
+ // Assert
+ resolved.Should().Be(n, "the fresh observed node should win");
+ p.Successors.Should().Contain(n);
+ p.Successors.Should().NotContain(s, "existing speculative node should be discarded");
+ // No selector should exist in successors
+ p.Successors.OfType()
+ .Should().BeEmpty("no selector should be created for speculative conflict");
+ }
+
+ ///
+ /// ResolveSuccessorConflict with an existing speculative node must preserve the caller's
+ /// successor type instead of hardcoding .
+ ///
+ /// Models a seeded known-safe handler whose CALL eagerly wired a speculative
+ /// continuation edge. When the real
+ /// continuation node later replaces that speculative node, the replacement edge must stay
+ /// classified as CallToReturn so the partitioner/generator see the same call-return metadata as
+ /// the non-speculative graph. Hardcoding Normal silently diverges the two graphs.
+ ///
+ [Fact]
+ public void ResolveSuccessorConflictWithExistingSpeculativePreservesCallToReturnType() {
+ // Arrange: observed call-site P with a speculative CallToReturn continuation S at address X.
+ SegmentedAddress addrP = new(0, 0x120);
+ SegmentedAddress addrX = new(0, 0x130);
+
+ CfgInstruction p = CreateObservedNode(addrP);
+ CfgInstruction s = CreateSpeculativeNode(addrX);
+ p.MaxSuccessorsCount = 2;
+ WireEdge(p, s);
+
+ // A fresh observed continuation node arrives at the same address X.
+ CfgInstruction n = CreateObservedNode(addrX);
+
+ // Act: link the real CallToReturn continuation, replacing the existing speculative node.
+ ICfgNode resolved = NodeLinker.Link(InstructionSuccessorType.CallToReturn, p, n);
+
+ // Assert: the replacement edge keeps its CallToReturn classification, not Normal.
+ resolved.Should().Be(n, "the fresh observed node should win");
+ p.Successors.Should().Contain(n);
+ p.Successors.Should().NotContain(s, "the existing speculative node should be discarded");
+ p.SuccessorsPerType.Should().ContainKey(InstructionSuccessorType.CallToReturn,
+ "the replacement edge must keep the caller's successor type, not be reclassified as Normal");
+ p.SuccessorsPerType[InstructionSuccessorType.CallToReturn].Should().Contain(n);
+ p.SuccessorsPerType.Should().NotContainKey(InstructionSuccessorType.Normal,
+ "the replacement must not be reclassified as Normal");
+ }
+
+ ///
+ /// CreateSelectorNodeBetween throws if either operand is speculative.
+ ///
+ [Fact]
+ public void CreateSelectorNodeBetweenThrowsIfEitherOperandIsSpeculative() {
+ // Arrange
+ CfgInstruction observed = CreateObservedNode(new(0, 0x200));
+ CfgInstruction speculative = CreateSpeculativeNode(new(0, 0x200));
+
+ // Act & Assert
+ Action act = () => NodeLinker.CreateSelectorNodeBetween(speculative, observed);
+ act.Should().Throw();
+
+ Action act2 = () => NodeLinker.CreateSelectorNodeBetween(observed, speculative);
+ act2.Should().Throw();
+ }
+
+ ///
+ /// Promote-on-match flips node to observed and installs in caches.
+ /// Arrange: speculative node S at address A matching current memory bytes.
+ /// Act: Promote(S).
+ /// Assert: S.IsSpeculative == false. S.IsLive == true. Caches contain S.
+ ///
+ [Fact]
+ public void PromoteOnMatchFlipsNodeToObservedAndInstallsInCaches() {
+ // Arrange
+ SegmentedAddress addr = new(0, 0x300);
+ CfgInstruction s = CreateSpeculativeNode(addr);
+ s.IsSpeculative.Should().BeTrue();
+ s.IsLive.Should().BeFalse("speculative nodes are non-live");
+
+ // Act
+ _promoter.Promote(s);
+
+ // Assert
+ s.IsSpeculative.Should().BeFalse("promotion clears speculative flag");
+ s.IsLive.Should().BeTrue("promoted node should be live");
+ CfgInstruction? fromCurrent = _currentInstructions.GetAtAddress(addr);
+ fromCurrent.Should().Be(s, "promoted node should be in CurrentInstructions");
+ }
+
+ ///
+ /// Cold-path promotion: first execution at a speculated address promotes in-place.
+ /// Uses the InstructionsFeeder flow.
+ ///
+ [Fact]
+ public void ColdPathPromotionFirstExecutionAtSpeculatedAddressPromotes() {
+ InstructionsFeeder feeder = CreateSpeculativeFeeder();
+
+ // NOP at A falling through to a RET at B (so exploration terminates).
+ SegmentedAddress addrA = new(0, 0x400);
+ SegmentedAddress addrB = new(0, 0x401);
+ WriteNop(addrA);
+ WriteRet(addrB);
+
+ // First: parse A as observed (triggers exploration of B as speculative)
+ CfgInstruction observedA = feeder.GetInstructionFromMemory(addrA);
+ observedA.IsSpeculative.Should().BeFalse();
+
+ // Verify B is speculative in the index
+ CfgInstruction speculativeB = feeder.NodeIndex.GetAtAddress(addrB).FirstOrDefault()
+ ?? throw new InvalidOperationException("address B should hold a speculative node after exploration");
+ speculativeB.IsSpeculative.Should().BeTrue();
+
+ // Act: now "execute" at address B (cold-path promotion)
+ CfgInstruction result = feeder.GetInstructionFromMemory(addrB);
+
+ // Assert: the speculative node was promoted (same instance, now observed)
+ result.Should().Be(speculativeB, "promoted node should be returned");
+ result.IsSpeculative.Should().BeFalse("node should be promoted to observed");
+ }
+
+ ///
+ /// Cold-path mismatch: discard + poison.
+ /// When memory at a speculative address differs from what was decoded,
+ /// the speculative node is removed, the address is poisoned, and a fresh observed node is returned.
+ ///
+ [Fact]
+ public void ColdPathMismatchDiscardsAndPoisons() {
+ InstructionsFeeder feeder = CreateSpeculativeFeeder();
+
+ // NOP at A falling through to a NOP at B (exploration decodes the NOP at B).
+ SegmentedAddress addrA = new(0, 0x500);
+ SegmentedAddress addrB = new(0, 0x501);
+ WriteNop(addrA);
+ WriteNop(addrB);
+
+ // Parse A (triggers exploration -> B decoded as speculative NOP)
+ feeder.GetInstructionFromMemory(addrA);
+ CfgInstruction speculativeB = feeder.NodeIndex.GetAtAddress(addrB).FirstOrDefault()
+ ?? throw new InvalidOperationException("address B should hold a speculative node after exploration");
+ speculativeB.IsSpeculative.Should().BeTrue();
+
+ // Now change memory at B (simulating SMC or decode-into-data)
+ WriteRet(addrB); // change to RET
+
+ // Act: cold-path at B should detect mismatch, discard, poison
+ CfgInstruction result = feeder.GetInstructionFromMemory(addrB);
+
+ // Assert
+ result.Should().NotBe(speculativeB, "mismatch should return a fresh node");
+ result.IsSpeculative.Should().BeFalse("fresh node should be observed");
+ feeder.NodeIndex.PoisonSet.Should().Contain(addrB, "mismatched address should be poisoned");
+ }
+
+ ///
+ /// Cold-path with multiple self-modified speculative variants at one address: reconciliation must
+ /// promote the variant whose final signature matches live memory, not an arbitrary sibling.
+ /// Self-modifying code lets the explorer mint distinct variants (different opcodes) at the same
+ /// address. When execution reaches that address, picking a non-matching sibling would sweep it and
+ /// permanently poison the address even though a sibling matched memory.
+ ///
+ [Fact]
+ public void ColdPathPromotesSpeculativeVariantMatchingLiveMemoryAmongSiblings() {
+ InstructionsFeeder feeder = CreateSpeculativeFeeder();
+
+ // Two jump sites both target B. B initially decodes to a NOP falling through to a RET.
+ SegmentedAddress jmpToNopEra = new(0, 0x600);
+ SegmentedAddress jmpToRetEra = new(0, 0x610);
+ SegmentedAddress b = new(0, 0x620);
+ SegmentedAddress bNext = new(0, 0x621);
+ WriteJmpShort(jmpToNopEra, 0x1E); // 0x602 + 0x1E = 0x620
+ WriteJmpShort(jmpToRetEra, 0x0E); // 0x612 + 0x0E = 0x620
+ WriteNop(b);
+ WriteRet(bNext);
+
+ // Observe the first jump: explores B, minting a speculative NOP variant at B.
+ feeder.GetInstructionFromMemory(jmpToNopEra);
+ CfgInstruction nopVariant = feeder.NodeIndex.GetAtAddress(b).Single();
+ nopVariant.IsSpeculative.Should().BeTrue();
+
+ // Self-modify B into a RET, then observe the second jump: explores B again and mints a second
+ // speculative variant (a RET) because its final signature differs from the NOP variant.
+ WriteRet(b);
+ feeder.GetInstructionFromMemory(jmpToRetEra);
+ feeder.NodeIndex.GetAtAddress(b).Should().HaveCount(2,
+ "two self-modified speculative variants now share address B");
+ CfgInstruction liveRet = Parser.ParseInstructionAt(b);
+ CfgInstruction retVariant = feeder.NodeIndex.GetAtAddressMatchingFinalSignature(b, liveRet.SignatureFinal)
+ ?? throw new InvalidOperationException("expected a speculative RET variant at B");
+ retVariant.IsSpeculative.Should().BeTrue();
+ retVariant.Should().NotBeSameAs(nopVariant);
+
+ // Act: execute at B. Live memory (RET) matches the RET variant, not the first-indexed NOP variant.
+ CfgInstruction result = feeder.GetInstructionFromMemory(b);
+
+ // Assert: the matching sibling is promoted in place; the address is not poisoned.
+ result.Should().BeSameAs(retVariant,
+ "the speculative variant matching live memory must be promoted, not an arbitrary sibling");
+ result.IsSpeculative.Should().BeFalse("the matching variant is promoted to observed");
+ feeder.NodeIndex.PoisonSet.Should().NotContain(b,
+ "a matching sibling variant must not cause the address to be poisoned");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeReachabilityPrunerTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeReachabilityPrunerTest.cs
new file mode 100644
index 0000000000..1becbbf19c
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeReachabilityPrunerTest.cs
@@ -0,0 +1,215 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Shared.Emulator.Memory;
+
+using System.Collections.Generic;
+
+using Xunit;
+
+///
+/// Tests for the Speculative Reachability Pruner.
+/// These validate that the sweep correctly removes speculative nodes and maintains graph integrity.
+///
+public sealed class SpeculativeReachabilityPrunerTest : SpeculativeTestBase {
+ private readonly SpeculativeReachabilityPruner _pruner;
+
+ public SpeculativeReachabilityPrunerTest() {
+ _pruner = new SpeculativeReachabilityPruner(ReplacerRegistry);
+ }
+
+ ///
+ /// Sweep removes full speculative chain.
+ /// Arrange: speculative chain A->B->C (with proper edges). No observed predecessors.
+ /// Act: Sweep(A).
+ /// Assert: all three removed from the index. All edges detached.
+ ///
+ [Fact]
+ public void SweepRemovesFullSpeculativeChain() {
+ // Arrange
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x100));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x101));
+ CfgInstruction c = CreateSpeculativeNode(new(0, 0x102));
+ WireEdge(a, b);
+ WireEdge(b, c);
+
+ // Act
+ HashSet removed = _pruner.Sweep(a);
+
+ // Assert
+ removed.Should().Contain(a);
+ removed.Should().Contain(b);
+ removed.Should().Contain(c);
+ NodeIndex.HasAddress(new(0, 0x100)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x101)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x102)).Should().BeFalse();
+ a.Successors.Should().BeEmpty();
+ a.Predecessors.Should().BeEmpty();
+ b.Successors.Should().BeEmpty();
+ b.Predecessors.Should().BeEmpty();
+ c.Successors.Should().BeEmpty();
+ c.Predecessors.Should().BeEmpty();
+ }
+
+ ///
+ /// Sweep preserves survivors reachable from another observed root.
+ /// Arrange: speculative chain A->B->C. Observed node O also has a successor edge to B.
+ /// Act: Sweep(A).
+ /// Assert: A removed. B and C survive (reachable from O).
+ ///
+ [Fact]
+ public void SweepPreservesSurvivorsReachableFromObservedRoot() {
+ // Arrange
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x200));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x201));
+ CfgInstruction c = CreateSpeculativeNode(new(0, 0x202));
+ CfgInstruction o = CreateObservedNode(new(0, 0x210));
+ WireEdge(a, b);
+ WireEdge(b, c);
+ WireEdge(o, b);
+
+ // Act
+ HashSet removed = _pruner.Sweep(a);
+
+ // Assert
+ removed.Should().Contain(a);
+ removed.Should().NotContain(b, "B is reachable from observed O");
+ removed.Should().NotContain(c, "C is reachable from observed O via B");
+ NodeIndex.HasAddress(new(0, 0x200)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x201)).Should().BeTrue("B survives");
+ NodeIndex.HasAddress(new(0, 0x202)).Should().BeTrue("C survives");
+ b.Predecessors.Should().Contain(o, "B still has predecessor from O");
+ }
+
+ ///
+ /// Sweep handles a cycle in the speculative region.
+ /// Arrange: speculative nodes A->B->C->B (cycle). No external observed predecessor.
+ /// Act: Sweep(A).
+ /// Assert: A, B, C all removed.
+ ///
+ [Fact]
+ public void SweepHandlesCycleInSpeculativeRegion() {
+ // Arrange
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x300));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x301));
+ CfgInstruction c = CreateSpeculativeNode(new(0, 0x302));
+ WireEdge(a, b);
+ WireEdge(b, c);
+ WireEdge(c, b); // cycle
+
+ // Act
+ HashSet removed = _pruner.Sweep(a);
+
+ // Assert
+ removed.Should().Contain(a);
+ removed.Should().Contain(b);
+ removed.Should().Contain(c);
+ NodeIndex.HasAddress(new(0, 0x300)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x301)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x302)).Should().BeFalse();
+ }
+
+ ///
+ /// Sweep handles a diamond (join) in the speculative region.
+ /// Arrange: A->B, A->C, B->D, C->D (diamond). All speculative.
+ /// Act: Sweep(A).
+ /// Assert: A, B, C, D all removed.
+ ///
+ [Fact]
+ public void SweepHandlesDiamondInSpeculativeRegion() {
+ // Arrange
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x400));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x401));
+ CfgInstruction c = CreateSpeculativeNode(new(0, 0x402));
+ CfgInstruction d = CreateSpeculativeNode(new(0, 0x403));
+ WireEdge(a, b);
+ WireEdge(a, c);
+ WireEdge(b, d);
+ WireEdge(c, d);
+
+ // Act
+ HashSet removed = _pruner.Sweep(a);
+
+ // Assert: all four nodes are in the removed set
+ removed.Should().Contain(a);
+ removed.Should().Contain(b);
+ removed.Should().Contain(c);
+ removed.Should().Contain(d);
+
+ // ...gone from the index
+ NodeIndex.HasAddress(new(0, 0x400)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x401)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x402)).Should().BeFalse();
+ NodeIndex.HasAddress(new(0, 0x403)).Should().BeFalse();
+
+ // ...and every edge detached, including the diamond's join node D which had two predecessors.
+ a.Successors.Should().BeEmpty();
+ a.Predecessors.Should().BeEmpty();
+ b.Successors.Should().BeEmpty();
+ b.Predecessors.Should().BeEmpty();
+ c.Successors.Should().BeEmpty();
+ c.Predecessors.Should().BeEmpty();
+ d.Successors.Should().BeEmpty();
+ d.Predecessors.Should().BeEmpty();
+ }
+
+ ///
+ /// RemoveEdge refreshes SuccessorInvariant on predecessor.
+ /// Arrange: observed conditional P with MaxSuccessorsCount=2, two successors A and B.
+ /// Act: RemoveEdge(P, A).
+ /// Assert: P.CanHaveMoreSuccessors == true. SuccessorsPerAddress no longer references A.
+ ///
+ [Fact]
+ public void RemoveEdgeRefreshesSuccessorInvariantOnPredecessor() {
+ // Arrange: observed P with two successors
+ CfgInstruction p = CreateObservedNode(new(0, 0x500));
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x510));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x520));
+ p.MaxSuccessorsCount = 2;
+ WireEdge(p, a);
+ WireEdge(p, b);
+ p.CanHaveMoreSuccessors.Should().BeFalse("cap is 2 and 2 successors exist");
+
+ // Act
+ NodeLinker.RemoveEdge(p, a);
+
+ // Assert
+ p.CanHaveMoreSuccessors.Should().BeTrue("removed one successor, reopened slot");
+ p.Successors.Should().NotContain(a);
+ p.Successors.Should().Contain(b);
+ p.SuccessorsPerAddress.Should().NotContainKey(a.Address);
+ p.SuccessorsPerAddress.Should().ContainKey(b.Address);
+ }
+
+ ///
+ /// After RemoveEdge, a new link through the reopened slot succeeds.
+ ///
+ [Fact]
+ public void AfterRemoveEdgeNewLinkThroughReopenedSlotSucceeds() {
+ // Arrange
+ CfgInstruction p = CreateObservedNode(new(0, 0x600));
+ CfgInstruction a = CreateSpeculativeNode(new(0, 0x610));
+ CfgInstruction b = CreateSpeculativeNode(new(0, 0x620));
+ p.MaxSuccessorsCount = 2;
+ WireEdge(p, a);
+ WireEdge(p, b);
+
+ // Remove A to reopen a slot
+ NodeLinker.RemoveEdge(p, a);
+ p.CanHaveMoreSuccessors.Should().BeTrue();
+
+ // Act: link a new node through the reopened slot
+ CfgInstruction newNode = CreateObservedNode(new(0, 0x630));
+ WireEdge(p, newNode);
+
+ // Assert
+ p.Successors.Should().Contain(b);
+ p.Successors.Should().Contain(newNode);
+ p.Successors.Count.Should().Be(2);
+ p.CanHaveMoreSuccessors.Should().BeFalse("cap is 2 and 2 successors exist again");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeReconcilerTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeReconcilerTest.cs
new file mode 100644
index 0000000000..e54806447f
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeReconcilerTest.cs
@@ -0,0 +1,133 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.VM;
+using Spice86.Core.Emulator.VM.Breakpoint;
+using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Utils;
+
+using Xunit;
+
+///
+/// Dedicated unit tests for : the promote-or-sweep decision taken
+/// when execution reaches a pre-existing speculative node and compares it against live memory.
+///
+public sealed class SpeculativeReconcilerTest : SpeculativeTestBase {
+ private readonly CurrentInstructions _currentInstructions;
+ private readonly SpeculativeReconciler _reconciler;
+
+ public SpeculativeReconcilerTest() {
+ AddressReadWriteBreakpoints ioBreakpoints = new();
+ EmulatorBreakpointsManager breakpointsManager =
+ new(new PauseHandler(LoggerService), State, Memory, MemoryBreakpoints, ioBreakpoints);
+ _currentInstructions = new CurrentInstructions(Memory, breakpointsManager, ReplacerRegistry);
+ PreviousInstructions previousInstructions = new(Memory, ReplacerRegistry);
+ SpeculativePromoter promoter = new(Compiler, _currentInstructions, previousInstructions);
+ SpeculativeReachabilityPruner pruner = new(ReplacerRegistry);
+ SignatureReducer signatureReducer = new(ReplacerRegistry);
+ _reconciler = new SpeculativeReconciler(promoter, pruner, signatureReducer, NodeIndex);
+ }
+
+ ///
+ /// When the speculative node still matches live memory, reconciliation promotes it in place and
+ /// reports success; the address is not poisoned.
+ ///
+ [Fact]
+ public void ReconcileMatchingSignaturePromotesInPlace() {
+ SegmentedAddress address = new(0, 0x100);
+ CfgInstruction speculative = CreateSpeculativeNode(address);
+ // Live memory still holds the same NOP, so the live signature is equivalent.
+ CfgInstruction live = Parser.ParseInstructionAt(address);
+
+ bool promoted = _reconciler.Reconcile(speculative, live, address);
+
+ promoted.Should().BeTrue("a matching speculative node is promoted, not swept");
+ speculative.IsSpeculative.Should().BeFalse("promotion clears the speculative flag");
+ _currentInstructions.GetAtAddress(address).Should().BeSameAs(speculative,
+ "the promoted node is installed in the current cache");
+ NodeIndex.PoisonSet.Should().NotContain(address, "a matching address must not be poisoned");
+ }
+
+ ///
+ /// When live memory diverges from the speculative decode, reconciliation sweeps the node, poisons
+ /// the address, and reports failure so the caller falls back to a fresh observed node.
+ ///
+ [Fact]
+ public void ReconcileMismatchingSignatureSweepsAndPoisons() {
+ SegmentedAddress address = new(0, 0x200);
+ CfgInstruction speculative = CreateSpeculativeNode(address);
+ // Rewrite memory so the live instruction is a RET, diverging from the speculative NOP.
+ WriteRet(address);
+ CfgInstruction live = Parser.ParseInstructionAt(address);
+
+ bool promoted = _reconciler.Reconcile(speculative, live, address);
+
+ promoted.Should().BeFalse("a diverged speculative node is swept, not promoted");
+ NodeIndex.PoisonSet.Should().Contain(address, "a diverged address is poisoned to stop future speculation");
+ NodeIndex.HasAddress(address).Should().BeFalse("the swept speculative node is removed from the index");
+ _currentInstructions.GetAtAddress(address).Should().BeNull("a swept node must not be installed as current");
+ }
+
+ ///
+ /// When live memory differs from the speculative decode only in a non-final field (a self-modified
+ /// immediate), the opcode is unchanged, so the difference is reducible rather than a real conflict.
+ /// Reconciliation merges the speculative node with the live instruction and promotes the survivor
+ /// in place: the node stays in the graph, the address is not poisoned, and no sweep occurs.
+ ///
+ [Fact]
+ public void ReconcileReducibleImmediateDiffReducesAndPromotes() {
+ SegmentedAddress address = new(0, 0x300);
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ // Speculative decode of MOV AL, 0x11 (0xB0 0x11); the immediate is a non-final field.
+ Memory.UInt8[physAddr] = 0xB0;
+ Memory.UInt8[physAddr + 1] = 0x11;
+ CfgInstruction speculative = Parser.ParseInstructionAt(address);
+ speculative.SetSpeculative(true);
+ NodeIndex.Insert(speculative);
+ // Self-modify only the immediate: same opcode, so the final-field signatures still match.
+ Memory.UInt8[physAddr + 1] = 0x22;
+ CfgInstruction live = Parser.ParseInstructionAt(address);
+
+ bool promoted = _reconciler.Reconcile(speculative, live, address);
+
+ promoted.Should().BeTrue("a reducible same-opcode variant is merged and promoted, not swept");
+ speculative.IsSpeculative.Should().BeFalse("the surviving reduced node is promoted to observed");
+ NodeIndex.PoisonSet.Should().NotContain(address,
+ "a reducible same-opcode variant must not poison the address");
+ NodeIndex.HasAddress(address).Should().BeTrue("the reduced node stays in the graph");
+ _currentInstructions.GetAtAddress(address).Should().BeSameAs(speculative,
+ "the promoted survivor is installed in the current cache");
+ }
+
+ ///
+ /// Reducing a self-modified immediate must preserve the speculatively-explored subgraph: a
+ /// downstream speculative successor of the reconciled node stays wired and indexed rather than
+ /// being discarded by a reachability sweep.
+ ///
+ [Fact]
+ public void ReconcileReducibleImmediateDiffKeepsDownstreamSpeculativeSubgraph() {
+ SegmentedAddress address = new(0, 0x400);
+ SegmentedAddress successorAddress = new(0, 0x402);
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = 0xB0;
+ Memory.UInt8[physAddr + 1] = 0x11;
+ CfgInstruction speculative = Parser.ParseInstructionAt(address);
+ speculative.SetSpeculative(true);
+ NodeIndex.Insert(speculative);
+ CfgInstruction downstream = CreateSpeculativeNode(successorAddress);
+ WireEdge(speculative, downstream);
+ // Self-modify only the immediate: same opcode, reducible.
+ Memory.UInt8[physAddr + 1] = 0x22;
+ CfgInstruction live = Parser.ParseInstructionAt(address);
+
+ bool promoted = _reconciler.Reconcile(speculative, live, address);
+
+ promoted.Should().BeTrue();
+ NodeIndex.HasAddress(successorAddress).Should().BeTrue(
+ "the explored downstream speculative node must survive reduction, not be swept away");
+ downstream.IsSpeculative.Should().BeTrue("downstream nodes stay speculative until visited");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeSweepEntryPointTest.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeSweepEntryPointTest.cs
new file mode 100644
index 0000000000..ef3af7c0a7
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeSweepEntryPointTest.cs
@@ -0,0 +1,62 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.Function;
+using Spice86.Core.Emulator.VM;
+using Spice86.Core.Emulator.VM.Breakpoint;
+using Spice86.Shared.Emulator.Memory;
+
+using Xunit;
+
+///
+/// Graph-level backstop for the dangling-generation-root fix: a seeded generation-root entry point
+/// that is swept while still speculative must be dropped from
+/// and from the node index, leaving
+/// no detached, de-indexed ghost lingering as a dead generation root. This pins the fix at the unit
+/// boundary independent of emulator handler layout.
+///
+public sealed class SpeculativeSweepEntryPointTest : SpeculativeTestBase {
+ private readonly SpeculativeReachabilityPruner _pruner;
+ private readonly ExecutionContextManager _contextManager;
+
+ public SpeculativeSweepEntryPointTest() {
+ _pruner = new SpeculativeReachabilityPruner(ReplacerRegistry);
+ _contextManager = new ExecutionContextManager(Memory, State, BuildIsolatedFeeder(), ReplacerRegistry,
+ new FunctionCatalogue(), false, LoggerService, null);
+ }
+
+ // The feeder is built on its own throwaway registry so the only subscriber joining the shared
+ // ReplacerRegistry the pruner fans out through is the ExecutionContextManager under test (plus the
+ // base index/linker). Otherwise the feeder's own caches would double-register.
+ private CfgNodeFeeder BuildIsolatedFeeder() {
+ InstructionReplacerRegistry isolatedRegistry = new();
+ EmulatorBreakpointsManager breakpointsManager = new(new PauseHandler(LoggerService), State, Memory,
+ MemoryBreakpoints, new AddressReadWriteBreakpoints());
+ return new CfgNodeFeeder(Memory, State, breakpointsManager, isolatedRegistry, Compiler, IdAllocator,
+ enableSpeculativeExploration: false);
+ }
+
+ ///
+ /// A seeded speculative entry point swept before it fires must not survive as a dead root.
+ /// Arrange: a speculative node registered as a CFG generation root.
+ /// Act: Sweep the node.
+ /// Assert: the entry-point map no longer references it and the index no longer contains it.
+ ///
+ [Fact]
+ public void SweepingSeededSpeculativeEntryPointDropsItAsGenerationRoot() {
+ SegmentedAddress address = new(0, 0x100);
+ CfgInstruction seeded = CreateSpeculativeNode(address);
+ _contextManager.RegisterEntryPoint(seeded);
+ _contextManager.ExecutionContextEntryPoints.Should().ContainKey(address, "sanity: the root was seeded");
+
+ _pruner.Sweep(seeded);
+
+ _contextManager.ExecutionContextEntryPoints.Should().NotContainKey(address,
+ "the swept seeded root must not linger as a detached, de-indexed dead generation root");
+ NodeIndex.HasAddress(address).Should().BeFalse("the swept node must be de-indexed");
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SpeculativeTestBase.cs b/tests/Spice86.Tests/CfgCpu/SpeculativeTestBase.cs
new file mode 100644
index 0000000000..3602977fb6
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SpeculativeTestBase.cs
@@ -0,0 +1,128 @@
+namespace Spice86.Tests.CfgCpu;
+
+using NSubstitute;
+
+using Spice86.Core.CLI;
+using Spice86.Core.Emulator.CPU;
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.InstructionExecutor.Expressions;
+using Spice86.Core.Emulator.CPU.CfgCpu.Linker;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.Parser;
+using Spice86.Core.Emulator.Memory;
+using Spice86.Core.Emulator.Memory.Mmu;
+using Spice86.Core.Emulator.VM;
+using Spice86.Core.Emulator.VM.Breakpoint;
+using Spice86.Logging;
+using Spice86.Shared.Emulator.Memory;
+using Spice86.Shared.Interfaces;
+using Spice86.Shared.Utils;
+
+using System;
+
+///
+/// Shared base class for speculative execution tests providing common infrastructure
+/// (memory, state, parser, node index, linker, compiler) and utility helpers.
+///
+public abstract class SpeculativeTestBase : IDisposable {
+ private const byte Nop = 0x90;
+
+ protected readonly Memory Memory;
+ protected readonly AddressReadWriteBreakpoints MemoryBreakpoints;
+ protected readonly State State;
+ protected readonly InstructionParser Parser;
+ protected readonly InstructionReplacerRegistry ReplacerRegistry;
+ protected readonly CfgNodeIndex NodeIndex;
+ protected readonly NodeLinker NodeLinker;
+ protected readonly SequentialIdAllocator IdAllocator;
+ protected readonly CfgNodeExecutionCompiler Compiler;
+ protected readonly ILoggerService LoggerService;
+
+ protected SpeculativeTestBase() {
+ IdAllocator = new SequentialIdAllocator();
+ MemoryBreakpoints = new AddressReadWriteBreakpoints();
+ Memory = new Memory(MemoryBreakpoints, new Ram(0x100000), new A20Gate(), new RealModeMmu8086(), false);
+ State = new State(CpuModel.INTEL_80286);
+ Parser = new InstructionParser(Memory, State, IdAllocator);
+ ReplacerRegistry = new InstructionReplacerRegistry();
+ NodeIndex = new CfgNodeIndex(ReplacerRegistry);
+ LoggerService = Substitute.For();
+ Compiler = new CfgNodeExecutionCompiler(new CfgNodeExecutionCompilerMonitor(LoggerService), LoggerService, JitMode.InterpretedOnly);
+ NodeLinker = new NodeLinker(ReplacerRegistry, Compiler, IdAllocator);
+ }
+
+ protected CfgInstruction CreateSpeculativeNode(SegmentedAddress address) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = Nop;
+ CfgInstruction node = Parser.ParseInstructionAt(address);
+ node.SetSpeculative(true);
+ NodeIndex.Insert(node);
+ return node;
+ }
+
+ protected CfgInstruction CreateObservedNode(SegmentedAddress address) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = Nop;
+ CfgInstruction node = Parser.ParseInstructionAt(address);
+ NodeIndex.Insert(node);
+ return node;
+ }
+
+ protected static void WireEdge(ICfgNode from, ICfgNode to) {
+ from.Successors.Add(to);
+ to.Predecessors.Add(from);
+ SuccessorInvariant.Refresh(from);
+ from.UpdateSuccessorCache();
+ }
+
+ protected void WriteNop(SegmentedAddress address) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = Nop;
+ }
+
+ protected void WriteRet(SegmentedAddress address) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = 0xC3;
+ }
+
+ protected void WriteJmpShort(SegmentedAddress address, sbyte relativeOffset) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = 0xEB;
+ Memory.Int8[physAddr + 1] = relativeOffset;
+ }
+
+ protected CfgInstruction WriteNopAndParse(SegmentedAddress address) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = Nop;
+ CfgInstruction instruction = Parser.ParseInstructionAt(address);
+ return instruction;
+ }
+
+ protected CfgInstruction WriteConditionalJnzAndParse(SegmentedAddress address, sbyte relativeOffset) {
+ uint physAddr = MemoryUtils.ToPhysicalAddress(address.Segment, address.Offset);
+ Memory.UInt8[physAddr] = 0x75;
+ Memory.Int8[physAddr + 1] = relativeOffset;
+ CfgInstruction instruction = Parser.ParseInstructionAt(address);
+ return instruction;
+ }
+
+ ///
+ /// Builds a fully-wired over the shared memory and state with
+ /// speculative exploration enabled and a reachability pruner installed, mirroring the production
+ /// wiring. Lets cold-path tests drive the real promote/discard flow without repeating the
+ /// breakpoint/feeder/pruner bootstrap.
+ ///
+ protected InstructionsFeeder CreateSpeculativeFeeder() {
+ AddressReadWriteBreakpoints ioBreakpoints = new();
+ EmulatorBreakpointsManager breakpointsManager =
+ new(new PauseHandler(LoggerService), State, Memory, MemoryBreakpoints, ioBreakpoints);
+ InstructionsFeeder feeder = new(breakpointsManager, Memory, State, ReplacerRegistry,
+ Compiler, IdAllocator, nodeLinker: NodeLinker);
+ return feeder;
+ }
+
+ public virtual void Dispose() {
+ Compiler.Dispose();
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SuccessorConsistency.cs b/tests/Spice86.Tests/CfgCpu/SuccessorConsistency.cs
new file mode 100644
index 0000000000..9939c2aa42
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SuccessorConsistency.cs
@@ -0,0 +1,118 @@
+namespace Spice86.Tests.CfgCpu;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying;
+
+using Xunit.Sdk;
+
+///
+/// Test-only invariant check over a node's successor bookkeeping. Every secondary successor collection
+/// must agree with the authoritative set, predecessor edges must be
+/// symmetric, and the derived scalars must match . Applied after each
+/// edge-mutation path, it turns accidental drift (one metadata set mutated without the others) into an
+/// explicit test failure.
+///
+internal static class SuccessorConsistency {
+ public static void AssertConsistent(ICfgNode node) {
+ if (node is CfgBlock) {
+ // A block delegates its successor semantics to its terminator; nothing to check here.
+ return;
+ }
+ AssertBidirectional(node);
+ AssertDerivedScalars(node);
+ switch (node) {
+ case CfgInstruction instruction:
+ AssertInstruction(instruction);
+ break;
+ case SelectorNode selector:
+ AssertSelector(selector);
+ break;
+ }
+ }
+
+ private static void AssertBidirectional(ICfgNode node) {
+ foreach (ICfgNode successor in node.Successors) {
+ if (!successor.Predecessors.Contains(node)) {
+ throw new XunitException(
+ $"Successor {successor.Id} of node {node.Id} does not list it as a predecessor.");
+ }
+ }
+ foreach (ICfgNode predecessor in node.Predecessors) {
+ if (!predecessor.Successors.Contains(node)) {
+ throw new XunitException(
+ $"Predecessor {predecessor.Id} of node {node.Id} does not list it as a successor.");
+ }
+ }
+ }
+
+ private static void AssertDerivedScalars(ICfgNode node) {
+ int? maxSuccessors = node.MaxSuccessorsCount;
+ bool expectedCanHaveMore = maxSuccessors is null || node.Successors.Count < maxSuccessors;
+ if (node.CanHaveMoreSuccessors != expectedCanHaveMore) {
+ throw new XunitException(
+ $"Node {node.Id} CanHaveMoreSuccessors={node.CanHaveMoreSuccessors} disagrees with successor count " +
+ $"{node.Successors.Count} and cap {maxSuccessors?.ToString() ?? "null"}.");
+ }
+ if (maxSuccessors == 1) {
+ if (node.Successors.Count == 0) {
+ if (node.UniqueSuccessor is not null) {
+ throw new XunitException(
+ $"Node {node.Id} has UniqueSuccessor set but no successors.");
+ }
+ } else if (node.UniqueSuccessor is null || !node.Successors.Contains(node.UniqueSuccessor)) {
+ throw new XunitException(
+ $"Node {node.Id} UniqueSuccessor is not among its successors.");
+ }
+ } else if (node.UniqueSuccessor is not null) {
+ throw new XunitException(
+ $"Node {node.Id} has UniqueSuccessor set but its successor cap is not 1.");
+ }
+ }
+
+ private static void AssertInstruction(CfgInstruction instruction) {
+ if (instruction.SuccessorsPerAddress.Count != instruction.Successors.Count) {
+ throw new XunitException(
+ $"Instruction {instruction.Id} SuccessorsPerAddress has {instruction.SuccessorsPerAddress.Count} " +
+ $"entries but {instruction.Successors.Count} successors.");
+ }
+ foreach (ICfgNode successor in instruction.Successors) {
+ if (!instruction.SuccessorsPerAddress.TryGetValue(successor.Address, out ICfgNode? byAddress)
+ || !byAddress.Equals(successor)) {
+ throw new XunitException(
+ $"Instruction {instruction.Id} SuccessorsPerAddress does not map {successor.Address} " +
+ $"to successor {successor.Id}.");
+ }
+ }
+ Dictionary typeBySuccessor = new();
+ foreach ((InstructionSuccessorType type, ISet bucket) in instruction.SuccessorsPerType) {
+ foreach (ICfgNode successor in bucket) {
+ if (!instruction.Successors.Contains(successor)) {
+ throw new XunitException(
+ $"Instruction {instruction.Id} lists {successor.Id} under type {type} but it is not a successor.");
+ }
+ if (typeBySuccessor.TryGetValue(successor, out InstructionSuccessorType existingType)) {
+ throw new XunitException(
+ $"Instruction {instruction.Id} lists successor {successor.Id} under two types " +
+ $"({existingType} and {type}).");
+ }
+ typeBySuccessor[successor] = type;
+ }
+ }
+ foreach (ICfgNode successor in instruction.SpeculativelyWiredSuccessors) {
+ if (!instruction.Successors.Contains(successor)) {
+ throw new XunitException(
+ $"Instruction {instruction.Id} marks {successor.Id} as speculatively wired but it is not a successor.");
+ }
+ }
+ }
+
+ private static void AssertSelector(SelectorNode selector) {
+ foreach (CfgInstruction successor in selector.SuccessorsPerSignature.Values) {
+ if (!selector.Successors.Contains(successor)) {
+ throw new XunitException(
+ $"Selector {selector.Id} lists successor {successor.Id} by signature but it is not in its successor set.");
+ }
+ }
+ }
+}
diff --git a/tests/Spice86.Tests/CfgCpu/SuccessorConsistencyTest.cs b/tests/Spice86.Tests/CfgCpu/SuccessorConsistencyTest.cs
new file mode 100644
index 0000000000..19b6e0a22e
--- /dev/null
+++ b/tests/Spice86.Tests/CfgCpu/SuccessorConsistencyTest.cs
@@ -0,0 +1,137 @@
+namespace Spice86.Tests.CfgCpu;
+
+using FluentAssertions;
+
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying;
+using Spice86.Shared.Emulator.Memory;
+
+using Xunit;
+
+///
+/// Exercises each successor-edge mutation path in
+/// and asserts the node's successor bookkeeping stays internally consistent afterwards (see
+/// ). These guard the drift surface where the per-type and
+/// speculative-provenance sets must move in lockstep with .
+///
+public sealed class SuccessorConsistencyTest : SpeculativeTestBase {
+ private static readonly SegmentedAddress A = new(0, 0x100);
+ private static readonly SegmentedAddress B = new(0, 0x200);
+ private static readonly SegmentedAddress C = new(0, 0x300);
+
+ [Fact]
+ public void NewObservedEdgeIsConsistent() {
+ CfgInstruction from = CreateObservedNode(A);
+ CfgInstruction to = CreateObservedNode(B);
+
+ NodeLinker.Link(InstructionSuccessorType.Normal, from, to);
+
+ from.Successors.Should().Contain(to);
+ SuccessorConsistency.AssertConsistent(from);
+ SuccessorConsistency.AssertConsistent(to);
+ }
+
+ [Fact]
+ public void EdgeToSpeculativeTargetRecordsProvenanceAndIsConsistent() {
+ CfgInstruction from = CreateObservedNode(A);
+ CfgInstruction speculativeTo = CreateSpeculativeNode(B);
+
+ NodeLinker.Link(InstructionSuccessorType.Normal, from, speculativeTo);
+
+ from.SpeculativelyWiredSuccessors.Should().Contain(speculativeTo);
+ SuccessorConsistency.AssertConsistent(from);
+ }
+
+ [Fact]
+ public void NewCallToReturnEdgeIsConsistent() {
+ CfgInstruction from = CreateObservedNode(A);
+ CfgInstruction to = CreateObservedNode(B);
+
+ NodeLinker.Link(InstructionSuccessorType.CallToReturn, from, to);
+
+ from.SuccessorsPerType[InstructionSuccessorType.CallToReturn].Should().Contain(to);
+ SuccessorConsistency.AssertConsistent(from);
+ }
+
+ [Fact]
+ public void ObservedRetraversalRetypesSpeculativeEdgeAndDropsProvenance() {
+ CfgInstruction from = CreateObservedNode(A);
+ CfgInstruction target = CreateSpeculativeNode(B);
+ // Explorer wires a speculative Normal guess.
+ NodeLinker.Link(InstructionSuccessorType.Normal, from, target);
+ from.SpeculativelyWiredSuccessors.Should().Contain(target);
+
+ // The target is later observed and re-reached via a different edge type.
+ target.SetSpeculative(false);
+ NodeLinker.Link(InstructionSuccessorType.CallToReturn, from, target);
+
+ from.SuccessorsPerType[InstructionSuccessorType.CallToReturn].Should().Contain(target);
+ from.SuccessorsPerType[InstructionSuccessorType.Normal].Should().NotContain(target);
+ from.SpeculativelyWiredSuccessors.Should().BeEmpty();
+ SuccessorConsistency.AssertConsistent(from);
+ }
+
+ [Fact]
+ public void RemoveEdgeClearsAllMetadataAndIsConsistent() {
+ CfgInstruction from = CreateObservedNode(A);
+ CfgInstruction to = CreateSpeculativeNode(B);
+ NodeLinker.Link(InstructionSuccessorType.Normal, from, to);
+
+ NodeLinker.RemoveEdge(from, to);
+
+ from.Successors.Should().NotContain(to);
+ from.SpeculativelyWiredSuccessors.Should().NotContain(to);
+ from.SuccessorsPerType.Values.SelectMany(bucket => bucket).Should().NotContain(to);
+ to.Predecessors.Should().NotContain(from);
+ SuccessorConsistency.AssertConsistent(from);
+ }
+
+ [Fact]
+ public void ReplacingSpeculativelyWiredSuccessorMigratesProvenanceToNewTarget() {
+ CfgInstruction predecessor = CreateObservedNode(A);
+ CfgInstruction speculativeTarget = CreateSpeculativeNode(B);
+ NodeLinker.Link(InstructionSuccessorType.Normal, predecessor, speculativeTarget);
+ predecessor.SpeculativelyWiredSuccessors.Should().Contain(speculativeTarget);
+
+ CfgInstruction observedTarget = CreateObservedNode(B);
+ NodeLinker.ReplaceInstruction(speculativeTarget, observedTarget);
+
+ predecessor.Successors.Should().Contain(observedTarget);
+ predecessor.SpeculativelyWiredSuccessors.Should().NotContain(speculativeTarget);
+ predecessor.SpeculativelyWiredSuccessors.Should().Contain(observedTarget);
+ SuccessorConsistency.AssertConsistent(predecessor);
+ SuccessorConsistency.AssertConsistent(observedTarget);
+ }
+
+ [Fact]
+ public void SelectorNodeBetweenVariantsIsConsistent() {
+ CfgInstruction predecessor = CreateObservedNode(A);
+ // Two distinct signatures parsed at the same address (NOP then RET), giving two variants.
+ WriteNop(B);
+ CfgInstruction variantA = WriteNopAndParse(B);
+ NodeIndex.Insert(variantA);
+ WriteRet(B);
+ CfgInstruction variantB = Parser.ParseInstructionAt(B);
+ NodeIndex.Insert(variantB);
+
+ NodeLinker.Link(InstructionSuccessorType.Normal, predecessor, variantA);
+ SelectorNode selector = NodeLinker.CreateSelectorNodeBetween(variantA, variantB);
+
+ selector.Successors.Should().Contain(variantA);
+ selector.Successors.Should().Contain(variantB);
+ SuccessorConsistency.AssertConsistent(selector);
+ SuccessorConsistency.AssertConsistent(predecessor);
+ }
+
+ [Fact]
+ public void ConsistencyCheckRejectsTypedSuccessorNotInSuccessorSet() {
+ CfgInstruction node = CreateObservedNode(A);
+ CfgInstruction orphan = CreateObservedNode(B);
+ node.SuccessorsPerType[InstructionSuccessorType.Normal] = new HashSet { orphan };
+
+ Action assert = () => SuccessorConsistency.AssertConsistent(node);
+
+ assert.Should().Throw();
+ }
+}
diff --git a/tests/Spice86.Tests/CpuTests/SingleStepTests/SingleStepTestMinimalMachine.cs b/tests/Spice86.Tests/CpuTests/SingleStepTests/SingleStepTestMinimalMachine.cs
index 5ec168aa97..fb51539289 100644
--- a/tests/Spice86.Tests/CpuTests/SingleStepTests/SingleStepTestMinimalMachine.cs
+++ b/tests/Spice86.Tests/CpuTests/SingleStepTests/SingleStepTestMinimalMachine.cs
@@ -49,7 +49,7 @@ public SingleStepTestMinimalMachine(CpuModel cpuModel) {
CfgNodeExecutionCompiler executionCompiler = new CfgNodeExecutionCompiler(new CfgNodeExecutionCompilerMonitor(loggerService), loggerService, JitMode.InterpretedOnly);
_cfgNodeExecutionCompiler = executionCompiler;
Cpu = new CfgCpu(memory, state, ioPortDispatcher, callbackHandler, dualPic,
- emulatorBreakpointsManager, pauseHandler, functionCatalogue, false, false, true, loggerService,
+ emulatorBreakpointsManager, pauseHandler, functionCatalogue, false, false, true, false, loggerService,
executionCompiler, new SequentialIdAllocator());
}
diff --git a/tests/Spice86.Tests/Fixtures/ExecutionContextManagerFactory.cs b/tests/Spice86.Tests/Fixtures/ExecutionContextManagerFactory.cs
index c91c4725b6..c8f743a643 100644
--- a/tests/Spice86.Tests/Fixtures/ExecutionContextManagerFactory.cs
+++ b/tests/Spice86.Tests/Fixtures/ExecutionContextManagerFactory.cs
@@ -38,7 +38,7 @@ public ExecutionContextManagerFactory(FunctionCatalogue functionCatalogue) {
Spice86.Core.Emulator.VM.PauseHandler pauseHandler = new(loggerService);
CfgNodeFeeder feeder = new(Memory, State, new EmulatorBreakpointsManager(
pauseHandler, State, Memory,
- memoryBreakpoints, new AddressReadWriteBreakpoints()), replacerRegistry, _compiler, new SequentialIdAllocator());
+ memoryBreakpoints, new AddressReadWriteBreakpoints()), replacerRegistry, _compiler, new SequentialIdAllocator(), enableSpeculativeExploration: false);
ContextManager = new ExecutionContextManager(Memory, State, feeder, replacerRegistry,
functionCatalogue, false, loggerService, null);
}
diff --git a/tests/Spice86.Tests/GeneratedCodeMachineTest.cs b/tests/Spice86.Tests/GeneratedCodeMachineTest.cs
index 05f97dd15c..4e19b92be5 100644
--- a/tests/Spice86.Tests/GeneratedCodeMachineTest.cs
+++ b/tests/Spice86.Tests/GeneratedCodeMachineTest.cs
@@ -76,11 +76,16 @@ public void Jump1GeneratedOverrideCompilesAndMatchesMachineTestOracle() {
[Fact]
public void AlwaysTakenConditionalJumpGuardsUnobservedFallthroughWithIf() {
// jump1 contains conditional jumps that were always taken during discovery (e.g. `clc; ja j01`),
- // so their fallthrough (next-in-memory) edge was never observed. Each such jump must lower to an
- // `if` that guards the unobserved fallthrough with FailAsUntested, not silently collapse to an
- // unconditional transfer to the taken target.
+ // so their fallthrough (next-in-memory) edge was never observed. With speculation off, each such
+ // jump must lower to an `if` that guards the unobserved fallthrough with FailAsUntested, not
+ // silently collapse to an unconditional transfer to the taken target. (The speculation-on
+ // resolution of the same fallthroughs is covered by SpeculationOnJump1ResolvesUnobservedFallthroughs.)
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = false
+ };
GeneratedCodeMachineTestRunner runner = new();
- (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("jump1", maxCycles: 1000);
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("jump1", options);
generatedProgram.SourceText.Should().Contain("Unobserved conditional fallthrough");
}
@@ -378,7 +383,7 @@ public void ExternalIntGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
byte[] expected = new byte[6];
expected[0x00] = 0x01;
new GeneratedCodeMachineTestRunner().TestGeneratedCode("externalint", expected,
- new GeneratedCodeRunOptions { MaxCycles = 0xFFFFFFF, EnablePit = true, InstallInterruptVectors = true });
+ new GeneratedCodeRunOptions { MaxCycles = 0xFFFFFFF, EnablePit = true });
}
[Fact]
@@ -399,6 +404,87 @@ public void StiCliGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
new GeneratedCodeMachineTestRunner().TestGeneratedCode("sticli", [], new GeneratedCodeRunOptions { MaxCycles = 1000 });
}
+ [Fact]
+ public void SpeculativeBranchGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_branch", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeClosureGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xBB;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_closure", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeConvergenceGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x404];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xAA;
+ expected[0x402] = 0xCC;
+ expected[0x403] = 0xFF;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_convergence", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeInvalidOpcodeGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xEE;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_invalid_opcode", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeCallEntryGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_call_entry", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeSmcGuardGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_smc_guard", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeDiscardGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_discard", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
+ [Fact]
+ public void SpeculativeMixedBlockGeneratedOverrideCompilesAndMatchesMachineTestOracle() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_mixed_block", expected,
+ new GeneratedCodeRunOptions { MaxCycles = 1000, EnableSpeculativeCfgExploration = true });
+ }
+
[Fact]
public void Test386ButNotProtectedModeGeneratedOverrideCompilesAndReachesPostFinished() {
Test386PostPortHandler? handler = null;
diff --git a/tests/Spice86.Tests/GeneratedCodeMachineTestRunner.cs b/tests/Spice86.Tests/GeneratedCodeMachineTestRunner.cs
index 5b4f806d1f..c7d64b2149 100644
--- a/tests/Spice86.Tests/GeneratedCodeMachineTestRunner.cs
+++ b/tests/Spice86.Tests/GeneratedCodeMachineTestRunner.cs
@@ -31,7 +31,8 @@ public void TestGeneratedCode(string binName, byte[] expected, GeneratedCodeRunO
using Spice86Creator creator = new(binName: binName, maxCycles: options.MaxCycles, enablePit: options.EnablePit,
installInterruptVectors: options.InstallInterruptVectors, failOnUnhandledPort: options.FailOnUnhandledPort,
- enableA20Gate: options.EnableA20Gate, jitMode: JitMode.InterpretedOnly, overrideSupplier: compiledOverride.Supplier);
+ enableA20Gate: options.EnableA20Gate, jitMode: JitMode.InterpretedOnly, overrideSupplier: compiledOverride.Supplier,
+ enableSpeculativeCfgExploration: options.EnableSpeculativeCfgExploration);
using Spice86DependencyInjection spice86DependencyInjection = creator.Create();
options.ConfigureMachine?.Invoke(spice86DependencyInjection.Machine);
spice86DependencyInjection.FunctionCatalogue.FunctionInformations.Values
@@ -79,7 +80,8 @@ private static CompiledGeneratedOverride GenerateAndCompileSupplier(string binNa
private static CfgPartitionedProgram GenerateProgram(string binName, GeneratedCodeRunOptions options) {
using Spice86Creator creator = new(binName: binName, maxCycles: options.MaxCycles, enablePit: options.EnablePit,
installInterruptVectors: options.InstallInterruptVectors, failOnUnhandledPort: options.FailOnUnhandledPort,
- enableA20Gate: options.EnableA20Gate, jitMode: JitMode.InterpretedOnly);
+ enableA20Gate: options.EnableA20Gate, jitMode: JitMode.InterpretedOnly,
+ enableSpeculativeCfgExploration: options.EnableSpeculativeCfgExploration);
using Spice86DependencyInjection spice86DependencyInjection = creator.Create();
options.ConfigureMachine?.Invoke(spice86DependencyInjection.Machine);
spice86DependencyInjection.ProgramExecutor.Run();
diff --git a/tests/Spice86.Tests/GeneratedCodeRunOptions.cs b/tests/Spice86.Tests/GeneratedCodeRunOptions.cs
index fcaf528a8b..58ae2a3704 100644
--- a/tests/Spice86.Tests/GeneratedCodeRunOptions.cs
+++ b/tests/Spice86.Tests/GeneratedCodeRunOptions.cs
@@ -8,6 +8,7 @@ internal sealed class GeneratedCodeRunOptions {
public bool EnableA20Gate { get; init; }
public bool InstallInterruptVectors { get; init; }
public bool FailOnUnhandledPort { get; init; }
+ public bool EnableSpeculativeCfgExploration { get; init; } = true;
///
/// Optional hook invoked on the freshly created machine before the program runs, for both the discovery
/// run and the generated-code run. Used to install custom I/O port handlers (e.g. the test386 POST port).
diff --git a/tests/Spice86.Tests/MachineTest.cs b/tests/Spice86.Tests/MachineTest.cs
index ea43dddcb8..48cdf621b8 100755
--- a/tests/Spice86.Tests/MachineTest.cs
+++ b/tests/Spice86.Tests/MachineTest.cs
@@ -622,6 +622,87 @@ public void TestLinearAddressSameButSegmentedDifferent(JitMode jitMode)
TestOneBin("linearsamesegmenteddifferent", expected, jitMode, enableA20Gate:true);
}
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeBranch(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ TestOneBin("speculative_branch", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeClosure(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xBB;
+ TestOneBin("speculative_closure", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeConvergence(JitMode jitMode) {
+ byte[] expected = new byte[0x404];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xAA;
+ expected[0x402] = 0xCC;
+ expected[0x403] = 0xFF;
+ TestOneBin("speculative_convergence", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeInvalidOpcode(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xEE;
+ TestOneBin("speculative_invalid_opcode", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeCallEntry(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ TestOneBin("speculative_call_entry", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeSmcGuard(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ TestOneBin("speculative_smc_guard", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeDiscard(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ TestOneBin("speculative_discard", expected, jitMode, maxCycles: 1000);
+ }
+
+ [Theory]
+ [MemberData(nameof(JitModes))]
+ public void TestSpeculativeMixedBlock(JitMode jitMode) {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xDD;
+ expected[0x402] = 0xAA;
+ TestOneBin("speculative_mixed_block", expected, jitMode, maxCycles: 1000);
+ }
+
[Theory]
[MemberData(nameof(CfgPartitioningGraphFixtures))]
public void TestCfgPartitioningGraphFixture(string binName) {
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_branch.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_branch.asm
new file mode 100644
index 0000000000..80c1ad55e5
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_branch.asm
@@ -0,0 +1,38 @@
+; Speculative CFG test fixture T1: simple conditional branch.
+;
+; Selector byte at memory address 0x0500 (above IVT and BDA) determines branch direction.
+; Discovery run: byte at [0x0500] = 0x00 -> AL=0, test sets ZF=1, JNZ not taken -> fallthrough
+; Speculative path: byte at [0x0500] = 0x01 -> AL=1, test sets ZF=0, JNZ taken -> alt_path
+;
+; Expected memory layout after execution:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = branch result: 0xDD (discovery/fallthrough) or 0xEE (speculative/alt_path)
+; [0x0402] = 0xAA (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ ; Read selector byte from well-above IVT/BDA area
+ mov al, byte [0x0500]
+ test al, al
+ jnz alt_path
+
+ ; Discovery path: selector=0 -> JNZ not taken, falls through here
+ mov byte [0x0401], 0xDD
+ jmp done
+
+alt_path:
+ ; Speculative path: selector=1 -> JNZ taken
+ mov byte [0x0401], 0xEE
+ jmp done
+
+done:
+ mov byte [0x0402], 0xAA
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_call_entry.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_call_entry.asm
new file mode 100644
index 0000000000..6530d604eb
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_call_entry.asm
@@ -0,0 +1,48 @@
+; Speculative CFG test fixture T5: direct call entry on speculative path.
+;
+; Discovery takes path A (fallthrough). Speculative path B contains a call near
+; to a subroutine that was never called during discovery. The subroutine is pure
+; code (no SMC) and returns normally.
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: JNZ not taken -> fallthrough writes 0xDD
+; 0x01 = speculative path: JNZ taken -> calls subroutine -> writes return value
+;
+; Expected memory layout:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery) or 0x42 (speculative: subroutine result)
+; [0x0402] = 0xAA (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz call_path
+
+ ; Discovery path: selector=0 -> JNZ not taken
+ mov byte [0x0401], 0xDD
+ jmp done
+
+call_path:
+ ; Speculative path: selector=1 -> calls subroutine
+ call subroutine
+ mov byte [0x0401], al
+ jmp done
+
+subroutine:
+ ; Pure code subroutine, never called during discovery.
+ ; Returns 0x42 in AL.
+ mov al, 0x42
+ ret
+
+done:
+ mov byte [0x0402], 0xAA
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_closure.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_closure.asm
new file mode 100644
index 0000000000..1aed32b36c
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_closure.asm
@@ -0,0 +1,43 @@
+; Speculative CFG test fixture T4: recursive closure (multi-block unobserved arm).
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: JNZ not taken -> fallthrough writes 0xDD
+; 0x01 = speculative path: JNZ taken -> enters a 3-iteration loop
+;
+; Expected memory layout:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery) or 0x03 (loop counter after 3 iterations)
+; [0x0402] = 0xBB (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz loop_path
+
+ ; Discovery path: selector=0 -> JNZ not taken
+ mov byte [0x0401], 0xDD
+ jmp done
+
+loop_path:
+ ; Speculative path: multi-block loop, 3 iterations
+ mov cx, 3
+ mov byte [0x0401], 0x00
+
+loop_body:
+ inc byte [0x0401]
+ dec cx
+ jnz loop_body
+ jmp done
+
+done:
+ mov byte [0x0402], 0xBB
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_convergence.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_convergence.asm
new file mode 100644
index 0000000000..f79acd39c3
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_convergence.asm
@@ -0,0 +1,44 @@
+; Speculative CFG test fixture T6: convergence onto observed code (no duplicate).
+;
+; Two paths (A and B) both jump to the same target C. Discovery observes path A -> C.
+; Path B is speculative. The generated code for path B must emit a goto to C's label.
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: first JNZ not taken -> path A taken (second JNZ taken) -> mergepoint
+; 0x01 = speculative path: first JNZ taken -> path B -> mergepoint
+;
+; Expected memory layout:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xAA (path A marker) or 0xBB (path B marker)
+; [0x0402] = 0xCC (mergepoint marker - always reached)
+; [0x0403] = 0xFF (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz path_b
+
+ ; Discovery path: selector=0 -> first JNZ not taken -> path A
+path_a:
+ mov byte [0x0401], 0xAA
+ jmp mergepoint
+
+path_b:
+ ; Speculative path: selector=1 -> first JNZ taken
+ mov byte [0x0401], 0xBB
+ jmp mergepoint
+
+mergepoint:
+ ; Both paths converge here. Discovery observed this block.
+ mov byte [0x0402], 0xCC
+ mov byte [0x0403], 0xFF
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_discard.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_discard.asm
new file mode 100644
index 0000000000..f405b8e851
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_discard.asm
@@ -0,0 +1,47 @@
+; Speculative CFG test fixture T9: discard-on-divergence (live reconciliation).
+;
+; Discovery explores a speculative path B. At runtime, path B is taken but the
+; memory has been modified (via ConfigureMachine). The VerifySpeculativeEntryOrFail
+; guard fires (memory changed), and the program falls back to FailAsUntested behavior.
+; The interpreter then takes over and completes the program.
+;
+; This tests the live reconciliation path where a speculative decode is proven wrong.
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: JNZ not taken -> writes 0xDD
+; 0x01 = speculative path: JNZ taken -> writes 0xEE
+;
+; For the discard scenario, the test modifies memory at the speculative target
+; between discovery and the generated code run so the guard fires.
+;
+; Expected memory layout (discovery path):
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery path result)
+; [0x0402] = 0xAA (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz alt_path
+
+ ; Discovery path: selector=0 -> JNZ not taken
+ mov byte [0x0401], 0xDD
+ jmp done
+
+alt_path:
+ ; Speculative path: selector=1 -> JNZ taken
+ mov byte [0x0401], 0xEE
+ jmp done
+
+done:
+ mov byte [0x0402], 0xAA
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_invalid_opcode.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_invalid_opcode.asm
new file mode 100644
index 0000000000..11e241f032
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_invalid_opcode.asm
@@ -0,0 +1,41 @@
+; Speculative CFG test fixture T8: decode-into-data / invalid opcode hard-stop.
+;
+; A conditional branch where one arm would decode into invalid opcodes (data region).
+; The explorer must hard-stop at the invalid opcode and NOT speculate that path.
+; The generated source must still have FailAsUntested for that arm.
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: JNZ not taken -> fallthrough writes 0xDD
+; 0x01 = data path: JNZ taken -> jumps to address containing invalid opcodes (0xFF 0xFF)
+;
+; Expected memory layout:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery path result)
+; [0x0402] = 0xEE (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz data_region
+
+ ; Discovery path: selector=0 -> JNZ not taken
+ mov byte [0x0401], 0xDD
+ jmp done
+
+data_region:
+ ; This is a data region that happens to be the JNZ target.
+ ; The explorer will decode 0xFF 0xFF as an invalid opcode and hard-stop.
+ db 0xFF, 0xFF, 0xFF, 0xFF
+
+done:
+ mov byte [0x0402], 0xEE
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_mixed_block.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_mixed_block.asm
new file mode 100644
index 0000000000..a84eaaa2d4
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_mixed_block.asm
@@ -0,0 +1,45 @@
+; Speculative CFG test fixture T12: mixed-block guard placement (mid-block).
+;
+; A block where discovery executes the first 2 instructions, then takes a branch
+; out. The remaining instructions in the straight-line block are speculative.
+; The generated source must show the guard AFTER the 2 observed instructions
+; and BEFORE the speculative tail, within the same block label.
+;
+; Layout:
+; start -> observed_prefix (2 instructions executed) -> branch out
+; When branch not taken: continues into speculative_tail (never executed during discovery)
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: CMP makes JE taken -> branch to done_early
+; 0x01 = speculative path: CMP makes JE not taken -> fallthrough to speculative tail
+;
+; Expected memory layout:
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery: done_early) or 0xEE (speculative: speculative tail)
+; [0x0402] = 0xAA (done marker)
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ cmp al, 0x00
+ je done_early
+
+ ; Speculative tail: JE not taken -> these instructions are never executed during discovery
+ mov byte [0x0401], 0xEE
+ jmp done
+
+done_early:
+ ; Discovery path: JE taken
+ mov byte [0x0401], 0xDD
+
+done:
+ mov byte [0x0402], 0xAA
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_seeded_timer_sweep.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_seeded_timer_sweep.asm
new file mode 100644
index 0000000000..102f9377f9
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_seeded_timer_sweep.asm
@@ -0,0 +1,80 @@
+; Speculative CFG fixture: sweep of a seeded hardware-interrupt generation root.
+;
+; With interrupt vectors installed and speculation on, the emulator seeds the INT 8
+; (IRQ0 / 8254 PIT) handler as a speculative CFG generation root at construction time,
+; decoding the emulator callback + IRET stub sitting at IVT[8].
+;
+; This BIOS-style image loads into F000 AFTER that seeding, so the bytes at the handler
+; address are replaced. The program copies its own minimal ISR over the seeded handler
+; address and lets the timer fire. On the first INT 8, live memory no longer matches the
+; seeded callback decode, so the reconciler SWEEPS the seeded speculative node - which is
+; still registered as a generation root. This is the exact scenario the RemoveInstruction
+; fan-out fix must handle: the swept root must not linger as a detached, de-indexed ghost.
+;
+; Real code lives high (F000:8000) so the low handler region and the ISR copy never collide
+; with executing code.
+;
+; assembled with: fasm speculative_seeded_timer_sweep.asm ../speculative_seeded_timer_sweep.bin
+
+use16
+
+rb 0x8000 - $ ; leave the low F000 handler region free for the ISR copy
+
+start:
+ xor ax, ax
+ mov ds, ax ; DS=0 to read the IVT
+ mov ss, ax
+ mov sp, 0x7000
+ mov byte [cs:marker], 0
+
+ ; destination = emulator-seeded INT 8 handler H, read from IVT[8] (0000:0020)
+ mov di, [0x20] ; H offset
+ mov ax, [0x22] ; H segment (0xF000)
+ mov es, ax
+
+ ; source = our ISR template, assembled below in this same segment (CS == 0xF000)
+ push cs
+ pop ds
+ mov si, isr
+ mov cx, isr_end - isr
+ cld
+ rep movsb ; overwrite the seeded handler bytes with our ISR
+
+ ; configure PIT channel 0 (mode 3, ~1ms), same sequence as externalint.asm
+ mov al, 00110110b
+ out 43h, al
+ mov al, 51h
+ out 40h, al
+ mov al, 22h
+ out 40h, al
+
+ ; unmask the master PIC so IRQ0 (INT 8) can fire
+ mov al, 0
+ out 21h, al
+
+ sti
+waitloop:
+ cmp byte [cs:marker], 0
+ jz waitloop
+
+ cli
+ hlt
+
+; Minimal ISR copied over the seeded handler. Its bytes differ from the seeded
+; callback+IRET decode, so the first INT 8 reconciles to a mismatch and sweeps the seeded
+; root. Never executed from here (it sits past the HLT); it only runs once copied to H,
+; where CS is also 0xF000, so [cs:marker] resolves to the same byte the wait loop polls.
+isr:
+ mov al, 0x20
+ out 0x20, al ; EOI to the master PIC
+ inc byte [cs:marker]
+ iret
+isr_end:
+
+marker:
+ db 0
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_smc_guard.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_smc_guard.asm
new file mode 100644
index 0000000000..82740153eb
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_smc_guard.asm
@@ -0,0 +1,57 @@
+; Speculative CFG test fixture T2: mid-block SMC inside a speculative run triggers the guard.
+;
+; Discovery takes path A (fallthrough). Speculative path B contains two speculative
+; instructions in the same block: the first rewrites a byte of the SECOND instruction's
+; encoding (self-modifying code), the second is the instruction that gets modified.
+;
+; The write uses a CS segment override so it targets the code segment (CS=F000) rather
+; than the data segment (DS=0): the program writes its data markers via DS:[0x04xx] which
+; land in low RAM, so the SMC write must explicitly address CS to hit the code bytes.
+;
+; A single block-entry guard cannot catch this: at block entry the bytes still match what
+; the explorer decoded, so the guard passes; the first instruction then mutates the second
+; one, and an entry-only guard never re-checks. Only a guard emitted before each speculative
+; instruction detects the divergence before the modified instruction executes.
+;
+; Selector byte at [0x0500]:
+; 0x00 = discovery path: JNZ not taken -> fallthrough writes 0xDD
+; 0x01 = smc path: JNZ taken -> rewrites the next instruction's ModRM byte -> guard fires
+;
+; Expected memory layout (discovery path, selector=0):
+; [0x0400] = 0x01 (init marker)
+; [0x0401] = 0xDD (discovery path result)
+; [0x0402] = 0xAA (done marker)
+;
+; When run with selector=1 via the generated override, the per-instruction guard fires and
+; the program falls back to FailAsUntested behavior.
+
+use16
+
+start:
+ mov byte [0x0400], 0x01
+
+ mov al, byte [0x0500]
+ test al, al
+ jnz smc_path
+
+ ; Discovery path: selector=0 -> JNZ not taken
+ mov byte [0x0401], 0xDD
+ jmp done
+
+smc_path:
+ ; Self-modifying code: write over the ModRM byte (smc_target + 1) of the next instruction
+ ; in this same block. The CS override makes the write target the code segment (CS=F000),
+ ; changing the bytes the speculative explorer decoded at explore time.
+ mov byte [cs:smc_target + 1], 0xEE
+smc_target:
+ mov byte [0x0401], 0x00 ; ModRM byte at smc_target+1 is rewritten to 0xEE at runtime
+ jmp done
+
+done:
+ mov byte [0x0402], 0xAA
+ hlt
+
+rb 65520-$
+jmp start
+rb 65535-$
+db 0ffh
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/strings.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/strings.asm
index d9afcb7b82..bb9a13a807 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/strings.asm
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/strings.asm
@@ -53,6 +53,11 @@ mov es,dx
scasw ; (8)
jz stor
+; Unobserved fallthrough of the jz above runs into the rb zero-padding below.
+; A hlt here bounds speculative decoding so the explorer stops at a terminator
+; instead of decoding the padding as a long run of bogus instructions.
+hlt
+
rb 01350h-$
stor:
mov al,80h
diff --git a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/test386.asm b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/test386.asm
index 3395f8da6b..ce7789ba10 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/test386.asm
+++ b/tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/test386.asm
@@ -1510,14 +1510,18 @@ postFF:
;
DefaultExcHandler:
error:
- ; CLI and HLT are privileged instructions, don't use them in ring3
+ ; Failure handler: spin forever instead of halting. A HLT here would stop the
+ ; machine the same way the successful postFF terminator does, making failure
+ ; indistinguishable from success (and, under speculative CFG exploration, a HLT
+ ; baked into this never-taken arm masks divergence). The self-loop keeps a failed
+ ; run observably stuck instead.
+ ; CLI is privileged, so it must not run in ring3.
mov ax, cs
; when in real mode, the jnz will be decoded together with test as
; "test eax,0xfe750007" (66A9070075FE)
test ax, 7 ; 66 A9 07 00
.ring3: jnz .ring3 ; 75 FE
cli
- hlt
jmp error
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bcdcnv.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bcdcnv.json
index 1844f1ba8d..242d88286b 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bcdcnv.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bcdcnv.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0121",
- "lastExecutedBlockId": 122,
+ "lastExecutedBlockId": 219,
"blocks": [
{
- "id": 122,
+ "id": 219,
"entry": "F000:00C1",
"term": "F000:0121",
"pred": [
- 120
+ 211
],
"succ": [],
"asm": [
@@ -75,14 +75,14 @@
]
},
{
- "id": 120,
+ "id": 211,
"entry": "F000:00C0",
"term": "F000:00C0",
"pred": [
- 114
+ 206
],
"succ": [
- 122
+ 219
],
"asm": [
"9D|popf"
@@ -114,14 +114,14 @@
]
},
{
- "id": 114,
+ "id": 206,
"entry": "F000:00B7",
"term": "F000:00BF",
"pred": [
- 112
+ 198
],
"succ": [
- 120
+ 211
],
"asm": [
"B88BFF|mov AX,0xFF8B",
@@ -139,35 +139,35 @@
3
],
"succ": [
- 18
+ 32
],
"asm": [
"9D|popf"
]
},
{
- "id": 112,
+ "id": 198,
"entry": "F000:00B6",
"term": "F000:00B6",
"pred": [
- 106
+ 193
],
"succ": [
- 114
+ 206
],
"asm": [
"9D|popf"
]
},
{
- "id": 18,
+ "id": 32,
"entry": "F000:001B",
"term": "F000:0043",
"pred": [
16
],
"succ": [
- 40
+ 53
],
"asm": [
"B8F9FF|mov AX,0xFFF9",
@@ -194,14 +194,14 @@
]
},
{
- "id": 106,
+ "id": 193,
"entry": "F000:00AD",
"term": "F000:00B5",
"pred": [
- 104
+ 169
],
"succ": [
- 112
+ 198
],
"asm": [
"B8F8FF|mov AX,0xFFF8",
@@ -212,42 +212,42 @@
]
},
{
- "id": 40,
+ "id": 53,
"entry": "F000:0044",
"term": "F000:0044",
"pred": [
- 18
+ 32
],
"succ": [
- 42
+ 77
],
"asm": [
"9D|popf"
]
},
{
- "id": 104,
+ "id": 169,
"entry": "F000:00AC",
"term": "F000:00AC",
"pred": [
- 82
+ 148
],
"succ": [
- 106
+ 193
],
"asm": [
"9D|popf"
]
},
{
- "id": 42,
+ "id": 77,
"entry": "F000:0045",
"term": "F000:006D",
"pred": [
- 40
+ 53
],
"succ": [
- 64
+ 98
],
"asm": [
"B8F9FF|mov AX,0xFFF9",
@@ -274,14 +274,14 @@
]
},
{
- "id": 82,
+ "id": 148,
"entry": "F000:0083",
"term": "F000:00AB",
"pred": [
- 80
+ 140
],
"succ": [
- 104
+ 169
],
"asm": [
"B88200|mov AX,0x0082",
@@ -308,42 +308,42 @@
]
},
{
- "id": 64,
+ "id": 98,
"entry": "F000:006E",
"term": "F000:006E",
"pred": [
- 42
+ 77
],
"succ": [
- 66
+ 122
],
"asm": [
"9D|popf"
]
},
{
- "id": 80,
+ "id": 140,
"entry": "F000:0082",
"term": "F000:0082",
"pred": [
- 74
+ 135
],
"succ": [
- 82
+ 148
],
"asm": [
"9D|popf"
]
},
{
- "id": 66,
+ "id": 122,
"entry": "F000:006F",
"term": "F000:0077",
"pred": [
- 64
+ 98
],
"succ": [
- 72
+ 127
],
"asm": [
"B8F8FF|mov AX,0xFFF8",
@@ -354,14 +354,14 @@
]
},
{
- "id": 74,
+ "id": 135,
"entry": "F000:0079",
"term": "F000:0081",
"pred": [
- 72
+ 127
],
"succ": [
- 80
+ 140
],
"asm": [
"B88BFF|mov AX,0xFF8B",
@@ -372,14 +372,14 @@
]
},
{
- "id": 72,
+ "id": 127,
"entry": "F000:0078",
"term": "F000:0078",
"pred": [
- 66
+ 122
],
"succ": [
- 74
+ 135
],
"asm": [
"9D|popf"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bitwise.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bitwise.json
index d784c89ec3..305a0455e7 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bitwise.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/bitwise.json
@@ -6,10 +6,10 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:01F2",
- "lastExecutedBlockId": 166,
+ "lastExecutedBlockId": 328,
"blocks": [
{
- "id": 166,
+ "id": 328,
"entry": "F000:01DA",
"term": "F000:01F2",
"pred": [
@@ -50,7 +50,7 @@
3
],
"succ": [
- 166
+ 328
],
"asm": [
"9D|popf"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/control.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/control.json
index 99bf3dd552..d42c40c4c5 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/control.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/control.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:002A",
- "lastExecutedBlockId": 28,
+ "lastExecutedBlockId": 46,
"blocks": [
{
- "id": 28,
+ "id": 46,
"entry": "F000:001C",
"term": "F000:002A",
"pred": [
- 23
+ 38
],
"succ": [],
"asm": [
@@ -39,14 +39,14 @@
]
},
{
- "id": 23,
+ "id": 38,
"entry": "F000:0018",
"term": "F000:001B",
"pred": [
- 21
+ 27
],
"succ": [
- 28
+ 46
],
"asm": [
"F5|cmc",
@@ -74,14 +74,14 @@
]
},
{
- "id": 21,
+ "id": 27,
"entry": "F000:0017",
"term": "F000:0017",
"pred": [
- 14
+ 20
],
"succ": [
- 23
+ 38
],
"asm": [
"9D|popf"
@@ -95,21 +95,21 @@
3
],
"succ": [
- 11
+ 18
],
"asm": [
"9D|popf"
]
},
{
- "id": 14,
+ "id": 20,
"entry": "F000:000F",
"term": "F000:0016",
"pred": [
- 11
+ 18
],
"succ": [
- 21
+ 27
],
"asm": [
"FA|cli",
@@ -121,14 +121,14 @@
]
},
{
- "id": 11,
+ "id": 18,
"entry": "F000:000D",
"term": "F000:000E",
"pred": [
9
],
"succ": [
- 14
+ 20
],
"asm": [
"F8|clc",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/datatrnf.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/datatrnf.json
index 0b9f88630a..120936caaf 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/datatrnf.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/datatrnf.json
@@ -6,10 +6,10 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:00C5",
- "lastExecutedBlockId": 41,
+ "lastExecutedBlockId": 78,
"blocks": [
{
- "id": 41,
+ "id": 78,
"entry": "F000:0054",
"term": "F000:00C5",
"pred": [
@@ -85,7 +85,7 @@
3
],
"succ": [
- 41
+ 78
],
"asm": [
"9D|popf"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/div.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/div.json
index 36685fe377..57a6bab939 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/div.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/div.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:031C",
- "lastExecutedBlockId": 265,
+ "lastExecutedBlockId": 528,
"blocks": [
{
- "id": 265,
+ "id": 528,
"entry": "F000:02F6",
"term": "F000:031C",
"pred": [
- 32,
- 247
+ 284,
+ 510
],
"succ": [],
"asm": [
@@ -48,63 +48,16 @@
]
},
{
- "id": 32,
- "entry": "F000:1000",
- "term": "F000:101A",
- "pred": [
- 3,
- 47,
- 58,
- 68,
- 104,
- 113,
- 149,
- 187,
- 206,
- 239,
- 247
- ],
- "succ": [
- 47,
- 58,
- 68,
- 104,
- 113,
- 149,
- 187,
- 206,
- 239,
- 247,
- 265
- ],
- "asm": [
- "50|push AX",
- "57|push DI",
- "8B4600|mov AX,word ptr SS:[BP]",
- "89E6|mov SI,SP",
- "83C604|add SI,4",
- "8B34|mov SI,word ptr DS:[SI]",
- "897600|mov word ptr SS:[BP],SI",
- "01C6|add SI,AX",
- "89E7|mov DI,SP",
- "83C704|add DI,4",
- "8935|mov word ptr DS:[DI],SI",
- "5F|pop DI",
- "58|pop AX",
- "CF|iret"
- ]
- },
- {
- "id": 247,
+ "id": 510,
"entry": "F000:02C6",
"term": "F000:02F4",
"pred": [
- 32,
- 239
+ 284,
+ 502
],
"succ": [
- 32,
- 265
+ 284,
+ 528
],
"asm": [
"83C502|add BP,2",
@@ -126,6 +79,53 @@
"D400|aam"
]
},
+ {
+ "id": 284,
+ "entry": "F000:1000",
+ "term": "F000:101A",
+ "pred": [
+ 3,
+ 297,
+ 321,
+ 331,
+ 367,
+ 376,
+ 412,
+ 450,
+ 469,
+ 502,
+ 510
+ ],
+ "succ": [
+ 297,
+ 321,
+ 331,
+ 367,
+ 376,
+ 412,
+ 450,
+ 469,
+ 502,
+ 510,
+ 528
+ ],
+ "asm": [
+ "50|push AX",
+ "57|push DI",
+ "8B4600|mov AX,word ptr SS:[BP]",
+ "89E6|mov SI,SP",
+ "83C604|add SI,4",
+ "8B34|mov SI,word ptr DS:[SI]",
+ "897600|mov word ptr SS:[BP],SI",
+ "01C6|add SI,AX",
+ "89E7|mov DI,SP",
+ "83C704|add DI,4",
+ "8935|mov word ptr DS:[DI],SI",
+ "5F|pop DI",
+ "58|pop AX",
+ "CF|iret"
+ ]
+ },
{
"id": 3,
"entry": "F000:0000",
@@ -134,8 +134,8 @@
2
],
"succ": [
- 32,
- 47
+ 284,
+ 297
],
"asm": [
"BCD000|mov SP,0x00D0",
@@ -169,16 +169,38 @@
]
},
{
- "id": 47,
+ "id": 502,
+ "entry": "F000:02B1",
+ "term": "F000:02C4",
+ "pred": [
+ 284,
+ 469
+ ],
+ "succ": [
+ 284,
+ 510
+ ],
+ "asm": [
+ "83C502|add BP,2",
+ "A37C00|mov word ptr DS:[0x007C],AX",
+ "89167E00|mov word ptr DS:[0x007E],DX",
+ "9C|pushf",
+ "B8FFFF|mov AX,0xFFFF",
+ "C746000200|mov word ptr SS:[BP],2",
+ "D400|aam"
+ ]
+ },
+ {
+ "id": 297,
"entry": "F000:0061",
"term": "F000:0081",
"pred": [
3,
- 32
+ 284
],
"succ": [
- 32,
- 58
+ 284,
+ 321
],
"asm": [
"83C502|add BP,2",
@@ -194,16 +216,16 @@
]
},
{
- "id": 58,
+ "id": 321,
"entry": "F000:0085",
"term": "F000:009E",
"pred": [
- 32,
- 47
+ 284,
+ 297
],
"succ": [
- 32,
- 68
+ 284,
+ 331
],
"asm": [
"83C502|add BP,2",
@@ -218,16 +240,16 @@
]
},
{
- "id": 68,
+ "id": 331,
"entry": "F000:00A0",
"term": "F000:010D",
"pred": [
- 32,
- 58
+ 284,
+ 321
],
"succ": [
- 32,
- 104
+ 284,
+ 367
],
"asm": [
"83C502|add BP,2",
@@ -268,16 +290,16 @@
]
},
{
- "id": 104,
+ "id": 367,
"entry": "F000:010F",
"term": "F000:0128",
"pred": [
- 32,
- 68
+ 284,
+ 331
],
"succ": [
- 32,
- 113
+ 284,
+ 376
],
"asm": [
"83C502|add BP,2",
@@ -291,16 +313,16 @@
]
},
{
- "id": 113,
+ "id": 376,
"entry": "F000:012C",
"term": "F000:0197",
"pred": [
- 32,
- 104
+ 284,
+ 367
],
"succ": [
- 32,
- 149
+ 284,
+ 412
],
"asm": [
"83C502|add BP,2",
@@ -341,16 +363,16 @@
]
},
{
- "id": 149,
+ "id": 412,
"entry": "F000:019B",
"term": "F000:020F",
"pred": [
- 32,
- 113
+ 284,
+ 376
],
"succ": [
- 32,
- 187
+ 284,
+ 450
],
"asm": [
"83C502|add BP,2",
@@ -393,16 +415,16 @@
]
},
{
- "id": 187,
+ "id": 450,
"entry": "F000:0211",
"term": "F000:0249",
"pred": [
- 32,
- 149
+ 284,
+ 412
],
"succ": [
- 32,
- 206
+ 284,
+ 469
],
"asm": [
"83C502|add BP,2",
@@ -426,16 +448,16 @@
]
},
{
- "id": 206,
+ "id": 469,
"entry": "F000:024D",
"term": "F000:02AF",
"pred": [
- 32,
- 187
+ 284,
+ 450
],
"succ": [
- 32,
- 239
+ 284,
+ 502
],
"asm": [
"83C502|add BP,2",
@@ -471,28 +493,6 @@
"C746000200|mov word ptr SS:[BP],2",
"F6F8|idiv AL"
]
- },
- {
- "id": 239,
- "entry": "F000:02B1",
- "term": "F000:02C4",
- "pred": [
- 32,
- 206
- ],
- "succ": [
- 32,
- 247
- ],
- "asm": [
- "83C502|add BP,2",
- "A37C00|mov word ptr DS:[0x007C],AX",
- "89167E00|mov word ptr DS:[0x007E],DX",
- "9C|pushf",
- "B8FFFF|mov AX,0xFFFF",
- "C746000200|mov word ptr SS:[BP],2",
- "D400|aam"
- ]
}
],
"truncated": false
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/divfaultloop.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/divfaultloop.json
index 4da69bab46..7981580093 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/divfaultloop.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/divfaultloop.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0036",
- "lastExecutedBlockId": 31,
+ "lastExecutedBlockId": 44,
"blocks": [
{
- "id": 31,
+ "id": 44,
"entry": "F000:002A",
"term": "F000:0036",
"pred": [
- 27
+ 56
],
"succ": [],
"asm": [
@@ -36,18 +36,18 @@
]
},
{
- "id": 27,
+ "id": 56,
"entry": "F000:0020",
"term": "F000:0028",
"pred": [
3,
- 23,
- 27
+ 37,
+ 56
],
"succ": [
- 14,
- 27,
- 31
+ 29,
+ 44,
+ 56
],
"asm": [
"B80A00|mov AX,0x000A",
@@ -63,7 +63,7 @@
2
],
"succ": [
- 27
+ 56
],
"asm": [
"B80000|mov AX,0",
@@ -76,15 +76,15 @@
]
},
{
- "id": 14,
+ "id": 29,
"entry": "F000:0037",
"term": "F000:0049",
"pred": [
- 27
+ 56
],
"succ": [
- 23,
- 29
+ 37,
+ 39
],
"asm": [
"55|push BP",
@@ -98,15 +98,15 @@
]
},
{
- "id": 23,
+ "id": 37,
"entry": "F000:0052",
"term": "F000:0059",
"pred": [
- 14,
- 29
+ 29,
+ 39
],
"succ": [
- 27
+ 56
],
"asm": [
"C746022000|mov word ptr SS:[BP\u002B2],0x0020",
@@ -116,14 +116,14 @@
]
},
{
- "id": 29,
+ "id": 39,
"entry": "F000:004B",
"term": "F000:004B",
"pred": [
- 14
+ 29
],
"succ": [
- 23
+ 37
],
"asm": [
"2EC70602010500|mov word ptr CS:[0x0102],5"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/externalint.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/externalint.json
index a8c466f1ae..2fe299a1e3 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/externalint.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/externalint.json
@@ -7,14 +7,14 @@
"F000:003F"
],
"lastExecutedAddress": "F000:003E",
- "lastExecutedBlockId": 33,
+ "lastExecutedBlockId": 42,
"blocks": [
{
- "id": 33,
+ "id": 42,
"entry": "F000:0033",
"term": "F000:003E",
"pred": [
- 24
+ 40
],
"succ": [],
"asm": [
@@ -26,7 +26,7 @@
]
},
{
- "id": 27,
+ "id": 53,
"entry": "F000:003F",
"term": "F000:0048",
"pred": [],
@@ -53,16 +53,16 @@
]
},
{
- "id": 24,
+ "id": 40,
"entry": "F000:002C",
"term": "F000:0031",
"pred": [
- 19,
- 24
+ 35,
+ 40
],
"succ": [
- 24,
- 33
+ 40,
+ 42
],
"asm": [
"40|inc AX",
@@ -79,7 +79,7 @@
2
],
"succ": [
- 19
+ 35
],
"asm": [
"B80000|mov AX,0",
@@ -100,14 +100,14 @@
]
},
{
- "id": 19,
+ "id": 35,
"entry": "F000:0026",
"term": "F000:0026",
"pred": [
3
],
"succ": [
- 24
+ 40
],
"asm": [
"66B9FFFFFF00|mov ECX,0x00FFFFFF"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/interrupt.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/interrupt.json
index 4388bb3731..b68851eda5 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/interrupt.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/interrupt.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:4026",
- "lastExecutedBlockId": 53,
+ "lastExecutedBlockId": 85,
"blocks": [
{
- "id": 53,
+ "id": 85,
"entry": "F000:4013",
"term": "F000:4026",
"pred": [
- 50
+ 81
],
"succ": [],
"asm": [
@@ -38,14 +38,14 @@
]
},
{
- "id": 50,
+ "id": 81,
"entry": "F000:400C",
"term": "F000:4012",
"pred": [
- 48
+ 75
],
"succ": [
- 53
+ 85
],
"asm": [
"C70610000050|mov word ptr DS:[0x0010],0x5000",
@@ -74,14 +74,14 @@
]
},
{
- "id": 48,
+ "id": 75,
"entry": "F000:400B",
"term": "F000:400B",
"pred": [
- 44
+ 72
],
"succ": [
- 50
+ 81
],
"asm": [
"9D|popf"
@@ -95,22 +95,22 @@
3
],
"succ": [
- 14
+ 24
],
"asm": [
"9D|popf"
]
},
{
- "id": 44,
+ "id": 72,
"entry": "F000:4002",
"term": "F000:400A",
"pred": [
- 33,
- 38
+ 54,
+ 62
],
"succ": [
- 48
+ 75
],
"asm": [
"C606060006|mov byte ptr DS:[6],6",
@@ -119,15 +119,15 @@
]
},
{
- "id": 14,
+ "id": 24,
"entry": "F000:001B",
"term": "F000:0020",
"pred": [
12
],
"succ": [
- 17,
- 23
+ 28,
+ 38
],
"asm": [
"C606000000|mov byte ptr DS:[0],0",
@@ -135,14 +135,14 @@
]
},
{
- "id": 38,
+ "id": 62,
"entry": "F000:3001",
"term": "F000:300B",
"pred": [
- 33
+ 54
],
"succ": [
- 44
+ 72
],
"asm": [
"C606050005|mov byte ptr DS:[5],5",
@@ -153,16 +153,16 @@
]
},
{
- "id": 33,
+ "id": 54,
"entry": "F000:0CEC",
"term": "F000:0CFD",
"pred": [
- 17,
- 26
+ 28,
+ 42
],
"succ": [
- 38,
- 44
+ 62,
+ 72
],
"asm": [
"C606040004|mov byte ptr DS:[4],4",
@@ -172,16 +172,16 @@
]
},
{
- "id": 17,
+ "id": 28,
"entry": "E342:EBE0",
"term": "E342:EBE8",
"pred": [
- 14,
- 26
+ 24,
+ 42
],
"succ": [
- 23,
- 33
+ 38,
+ 54
],
"asm": [
"C606010001|mov byte ptr DS:[1],1",
@@ -192,15 +192,15 @@
]
},
{
- "id": 23,
+ "id": 38,
"entry": "F000:0022",
"term": "F000:0027",
"pred": [
- 14,
- 17
+ 24,
+ 28
],
"succ": [
- 26
+ 42
],
"asm": [
"C606020002|mov byte ptr DS:[2],2",
@@ -208,15 +208,15 @@
]
},
{
- "id": 26,
+ "id": 42,
"entry": "F000:0CD7",
"term": "F000:0CEA",
"pred": [
- 23
+ 38
],
"succ": [
- 17,
- 33
+ 28,
+ 54
],
"asm": [
"C606030003|mov byte ptr DS:[3],3",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jmpmov.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jmpmov.json
index 767fc4aae3..ec04f7b639 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jmpmov.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jmpmov.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:400C",
- "lastExecutedBlockId": 42,
+ "lastExecutedBlockId": 75,
"blocks": [
{
- "id": 42,
+ "id": 75,
"entry": "F000:4001",
"term": "F000:400C",
"pred": [
- 26
+ 45
],
"succ": [],
"asm": [
@@ -37,14 +37,14 @@
]
},
{
- "id": 26,
+ "id": 45,
"entry": "E342:FBE1",
"term": "E342:FC06",
"pred": [
- 12
+ 17
],
"succ": [
- 42
+ 75
],
"asm": [
"BA00F1|mov DX,0xF100",
@@ -79,14 +79,14 @@
]
},
{
- "id": 12,
+ "id": 17,
"entry": "E342:EBE0",
"term": "E342:EC05",
"pred": [
- 10
+ 16
],
"succ": [
- 26
+ 45
],
"asm": [
"BB0010|mov BX,0x1000",
@@ -112,7 +112,7 @@
3
],
"succ": [
- 10
+ 16
],
"asm": [
"BB00F0|mov BX,0xF000",
@@ -122,14 +122,14 @@
]
},
{
- "id": 10,
+ "id": 16,
"entry": "F000:1290",
"term": "F000:1290",
"pred": [
5
],
"succ": [
- 12
+ 17
],
"asm": [
"EAE0EB42E3|jmp far E342:EBE0"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump1.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump1.json
index e5ba7d6d8e..6f4151a51f 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump1.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump1.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:00CE",
- "lastExecutedBlockId": 170,
+ "lastExecutedBlockId": 309,
"blocks": [
{
- "id": 170,
+ "id": 309,
"entry": "F000:00C8",
"term": "F000:00CE",
"pred": [
- 168
+ 308
],
"succ": [],
"asm": [
@@ -34,14 +34,15 @@
]
},
{
- "id": 168,
+ "id": 308,
"entry": "F000:00C3",
"term": "F000:00C3",
"pred": [
- 166
+ 301
],
"succ": [
- 170
+ 309,
+ 311
],
"asm": [
"7803|js short 0x00C8"
@@ -67,14 +68,29 @@
]
},
{
- "id": 166,
+ "id": 311,
+ "entry": "F000:00C5",
+ "dead": true,
+ "term": "F000:00C5",
+ "pred": [
+ 308
+ ],
+ "succ": [
+ 17
+ ],
+ "asm": [
+ "E92AFF|jmp near 0xFFF2"
+ ]
+ },
+ {
+ "id": 301,
"entry": "F000:00C2",
"term": "F000:00C2",
"pred": [
- 163
+ 297
],
"succ": [
- 168
+ 308
],
"asm": [
"9D|popf"
@@ -88,7 +104,8 @@
3
],
"succ": [
- 13
+ 13,
+ 15
],
"asm": [
"F9|stc",
@@ -96,14 +113,43 @@
]
},
{
- "id": 163,
+ "id": 17,
+ "entry": "F000:FFF2",
+ "dead": true,
+ "term": "F000:FFF2",
+ "pred": [
+ 13,
+ 22,
+ 32,
+ 41,
+ 72,
+ 92,
+ 116,
+ 138,
+ 155,
+ 175,
+ 192,
+ 216,
+ 238,
+ 260,
+ 277,
+ 294,
+ 311
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 297,
"entry": "F000:00BE",
"term": "F000:00C1",
"pred": [
- 161
+ 292
],
"succ": [
- 166
+ 301
],
"asm": [
"BA8408|mov DX,0x0884",
@@ -112,13 +158,29 @@
},
{
"id": 13,
+ "entry": "F000:000E",
+ "dead": true,
+ "term": "F000:000E",
+ "pred": [
+ 10
+ ],
+ "succ": [
+ 17
+ ],
+ "asm": [
+ "EBE2|jmp short 0xFFF2"
+ ]
+ },
+ {
+ "id": 15,
"entry": "F000:0013",
"term": "F000:0014",
"pred": [
10
],
"succ": [
- 16
+ 20,
+ 22
],
"asm": [
"F8|clc",
@@ -126,769 +188,878 @@
]
},
{
- "id": 161,
- "entry": "F000:00BC",
- "term": "F000:00BC",
+ "id": 22,
+ "entry": "F000:0016",
+ "dead": true,
+ "term": "F000:0016",
"pred": [
- 159
+ 15,
+ 20
],
"succ": [
- 163
+ 17
],
"asm": [
- "78FB|js short 0x00B9"
+ "EBDA|jmp short 0xFFF2"
]
},
{
- "id": 16,
- "entry": "F000:0018",
- "term": "F000:0019",
+ "id": 32,
+ "entry": "F000:001E",
+ "dead": true,
+ "term": "F000:001E",
"pred": [
- 13
+ 27,
+ 30
],
"succ": [
- 19
+ 17
],
"asm": [
- "F9|stc",
- "73FB|jae short 0x0016"
+ "EBD2|jmp short 0xFFF2"
]
},
{
- "id": 159,
- "entry": "F000:00B7",
- "term": "F000:00B7",
+ "id": 41,
+ "entry": "F000:0025",
+ "dead": true,
+ "term": "F000:0025",
"pred": [
- 157
+ 35,
+ 39
],
"succ": [
- 161
+ 17
],
"asm": [
- "7A03|jp short 0x00BC"
+ "EBCB|jmp short 0xFFF2"
]
},
{
- "id": 19,
- "entry": "F000:001B",
- "term": "F000:001C",
+ "id": 72,
+ "entry": "F000:002D",
+ "dead": true,
+ "term": "F000:002D",
"pred": [
- 16
+ 69,
+ 80
],
"succ": [
- 22
+ 17
],
"asm": [
- "F8|clc",
- "7302|jae short 0x0020"
+ "EBC3|jmp short 0xFFF2"
]
},
{
- "id": 157,
- "entry": "F000:00B6",
- "term": "F000:00B6",
+ "id": 92,
+ "entry": "F000:0037",
+ "dead": true,
+ "term": "F000:0037",
"pred": [
- 154
+ 89,
+ 102
],
"succ": [
- 159
+ 17
],
"asm": [
- "9D|popf"
+ "EBB9|jmp short 0xFFF2"
]
},
{
- "id": 22,
- "entry": "F000:0020",
- "term": "F000:0020",
+ "id": 116,
+ "entry": "F000:0047",
+ "dead": true,
+ "term": "F000:0047",
"pred": [
- 19
+ 113,
+ 126
],
"succ": [
- 24
+ 17
],
"asm": [
- "72FC|jb short 0x001E"
+ "EBA9|jmp short 0xFFF2"
]
},
{
- "id": 154,
- "entry": "F000:00B2",
- "term": "F000:00B5",
+ "id": 138,
+ "entry": "F000:0054",
+ "dead": true,
+ "term": "F000:0054",
"pred": [
- 152
+ 135,
+ 136
],
"succ": [
- 157
+ 17
],
"asm": [
- "BA0408|mov DX,0x0804",
- "52|push DX"
+ "EB9C|jmp short 0xFFF2"
]
},
{
- "id": 24,
- "entry": "F000:0022",
- "term": "F000:0023",
+ "id": 155,
+ "entry": "F000:005F",
+ "dead": true,
+ "term": "F000:005F",
"pred": [
- 22
+ 152,
+ 163
],
"succ": [
- 27
+ 17
],
"asm": [
- "F9|stc",
- "7202|jb short 0x0027"
+ "EB91|jmp short 0xFFF2"
]
},
{
- "id": 152,
- "entry": "F000:00B0",
- "term": "F000:00B0",
+ "id": 175,
+ "entry": "F000:0069",
+ "dead": true,
+ "term": "F000:0069",
"pred": [
- 150
+ 172,
+ 173
],
"succ": [
- 154
+ 17
],
"asm": [
- "7AFB|jp short 0x00AD"
+ "EB87|jmp short 0xFFF2"
]
},
{
- "id": 27,
- "entry": "F000:0027",
- "term": "F000:0028",
+ "id": 192,
+ "entry": "F000:0074",
+ "dead": true,
+ "term": "F000:0074",
"pred": [
- 24
+ 189,
+ 202
],
"succ": [
- 30
+ 17
],
"asm": [
- "F8|clc",
- "76FB|jbe short 0x0025"
+ "E97BFF|jmp near 0xFFF2"
]
},
{
- "id": 150,
- "entry": "F000:00AB",
- "term": "F000:00AB",
+ "id": 216,
+ "entry": "F000:0085",
+ "dead": true,
+ "term": "F000:0085",
"pred": [
- 148
+ 213,
+ 226
],
"succ": [
- 152
+ 17
],
"asm": [
- "7003|jo short 0x00B0"
+ "E96AFF|jmp near 0xFFF2"
]
},
{
- "id": 30,
- "entry": "F000:002A",
- "term": "F000:002A",
+ "id": 238,
+ "entry": "F000:0093",
+ "dead": true,
+ "term": "F000:0093",
"pred": [
- 27
+ 235,
+ 248
],
"succ": [
- 32
+ 17
],
"asm": [
- "9D|popf"
+ "E95CFF|jmp near 0xFFF2"
]
},
{
- "id": 148,
- "entry": "F000:00AA",
- "term": "F000:00AA",
+ "id": 260,
+ "entry": "F000:00A1",
+ "dead": true,
+ "term": "F000:00A1",
"pred": [
- 145
+ 257,
+ 258
],
"succ": [
- 150
+ 17
],
"asm": [
- "9D|popf"
+ "E94EFF|jmp near 0xFFF2"
]
},
{
- "id": 32,
- "entry": "F000:002B",
- "term": "F000:002B",
+ "id": 277,
+ "entry": "F000:00AD",
+ "dead": true,
+ "term": "F000:00AD",
"pred": [
- 30
+ 274,
+ 275
],
"succ": [
- 34
+ 17
],
"asm": [
- "7602|jbe short 0x002F"
+ "E942FF|jmp near 0xFFF2"
]
},
{
- "id": 145,
- "entry": "F000:00A6",
- "term": "F000:00A9",
+ "id": 294,
+ "entry": "F000:00B9",
+ "dead": true,
+ "term": "F000:00B9",
"pred": [
- 143
+ 291,
+ 292
],
"succ": [
- 148
+ 17
],
"asm": [
- "BA0008|mov DX,0x0800",
- "52|push DX"
+ "E936FF|jmp near 0xFFF2"
]
},
{
- "id": 34,
- "entry": "F000:002F",
- "term": "F000:002F",
+ "id": 292,
+ "entry": "F000:00BC",
+ "term": "F000:00BC",
"pred": [
- 32
+ 291
],
"succ": [
- 36
+ 294,
+ 297
],
"asm": [
- "51|push CX"
+ "78FB|js short 0x00B9"
]
},
{
- "id": 143,
- "entry": "F000:00A4",
- "term": "F000:00A4",
+ "id": 20,
+ "entry": "F000:0018",
+ "term": "F000:0019",
"pred": [
- 141
+ 15
],
"succ": [
- 145
+ 22,
+ 27
],
"asm": [
- "70FB|jo short 0x00A1"
+ "F9|stc",
+ "73FB|jae short 0x0016"
]
},
{
- "id": 36,
- "entry": "F000:0030",
- "term": "F000:0030",
+ "id": 27,
+ "entry": "F000:001B",
+ "term": "F000:001C",
"pred": [
- 34
+ 20
],
"succ": [
- 38
+ 30,
+ 32
],
"asm": [
- "9D|popf"
+ "F8|clc",
+ "7302|jae short 0x0020"
]
},
{
- "id": 141,
- "entry": "F000:009F",
- "term": "F000:009F",
+ "id": 30,
+ "entry": "F000:0020",
+ "term": "F000:0020",
"pred": [
- 139
+ 27
],
"succ": [
- 143
+ 32,
+ 35
],
"asm": [
- "7903|jns short 0x00A4"
+ "72FC|jb short 0x001E"
]
},
{
- "id": 38,
- "entry": "F000:0031",
- "term": "F000:0031",
+ "id": 35,
+ "entry": "F000:0022",
+ "term": "F000:0023",
"pred": [
- 36
+ 30
],
"succ": [
- 40
+ 39,
+ 41
],
"asm": [
- "74FA|je short 0x002D"
+ "F9|stc",
+ "7202|jb short 0x0027"
]
},
{
- "id": 139,
- "entry": "F000:009E",
- "term": "F000:009E",
+ "id": 39,
+ "entry": "F000:0027",
+ "term": "F000:0028",
"pred": [
- 137
+ 35
],
"succ": [
- 141
+ 41,
+ 46
],
"asm": [
- "9D|popf"
+ "F8|clc",
+ "76FB|jbe short 0x0025"
]
},
{
- "id": 40,
- "entry": "F000:0033",
- "term": "F000:0033",
+ "id": 69,
+ "entry": "F000:002B",
+ "term": "F000:002B",
"pred": [
- 38
+ 46
],
"succ": [
- 42
+ 70,
+ 72
],
"asm": [
- "53|push BX"
+ "7602|jbe short 0x002F"
]
},
{
- "id": 137,
- "entry": "F000:009D",
- "term": "F000:009D",
+ "id": 80,
+ "entry": "F000:0031",
+ "term": "F000:0031",
"pred": [
- 135
+ 74
],
"succ": [
- 139
+ 72,
+ 82
],
"asm": [
- "51|push CX"
+ "74FA|je short 0x002D"
]
},
{
- "id": 42,
- "entry": "F000:0034",
- "term": "F000:0034",
+ "id": 89,
+ "entry": "F000:0035",
+ "term": "F000:0035",
"pred": [
- 40
+ 84
],
"succ": [
- 44
+ 90,
+ 92
],
"asm": [
- "9D|popf"
+ "7402|je short 0x0039"
]
},
{
- "id": 135,
- "entry": "F000:009B",
- "term": "F000:009B",
+ "id": 102,
+ "entry": "F000:003E",
+ "term": "F000:003E",
"pred": [
- 133
+ 96
],
"succ": [
- 137
+ 92,
+ 104
],
"asm": [
- "79F6|jns short 0x0093"
+ "7FF7|jg short 0x0037"
]
},
{
- "id": 44,
- "entry": "F000:0035",
- "term": "F000:0035",
+ "id": 113,
+ "entry": "F000:0045",
+ "term": "F000:0045",
"pred": [
- 42
+ 107
],
"succ": [
- 46
+ 114,
+ 116
],
"asm": [
- "7402|je short 0x0039"
+ "7F02|jg short 0x0049"
]
},
{
- "id": 133,
- "entry": "F000:009A",
- "term": "F000:009A",
+ "id": 126,
+ "entry": "F000:004E",
+ "term": "F000:004E",
"pred": [
- 130
+ 120
],
"succ": [
- 135
+ 116,
+ 128
],
"asm": [
- "9D|popf"
+ "7DF7|jge short 0x0047"
]
},
{
- "id": 46,
- "entry": "F000:0039",
- "term": "F000:003C",
+ "id": 135,
+ "entry": "F000:0052",
+ "term": "F000:0052",
"pred": [
- 44
+ 130
],
"succ": [
- 49
+ 136,
+ 138
],
"asm": [
- "BAC008|mov DX,0x08C0",
- "52|push DX"
+ "7D02|jge short 0x0056"
]
},
{
- "id": 130,
- "entry": "F000:0096",
- "term": "F000:0099",
+ "id": 136,
+ "entry": "F000:0056",
+ "term": "F000:0056",
"pred": [
- 128
+ 135
],
"succ": [
- 133
+ 138,
+ 141
],
"asm": [
- "BAFF0E|mov DX,0x0EFF",
- "52|push DX"
+ "7CFC|jl short 0x0054"
]
},
{
- "id": 49,
- "entry": "F000:003D",
- "term": "F000:003D",
+ "id": 152,
+ "entry": "F000:005D",
+ "term": "F000:005D",
"pred": [
- 46
+ 145
],
"succ": [
- 51
+ 153,
+ 155
],
"asm": [
- "9D|popf"
+ "7C02|jl short 0x0061"
]
},
{
- "id": 128,
- "entry": "F000:0091",
- "term": "F000:0091",
+ "id": 163,
+ "entry": "F000:0063",
+ "term": "F000:0063",
"pred": [
- 126
+ 157
],
"succ": [
- 130
+ 155,
+ 165
],
"asm": [
- "7B03|jnp short 0x0096"
+ "7EFA|jle short 0x005F"
]
},
{
- "id": 51,
- "entry": "F000:003E",
- "term": "F000:003E",
+ "id": 172,
+ "entry": "F000:0067",
+ "term": "F000:0067",
"pred": [
- 49
+ 167
],
"succ": [
- 53
+ 173,
+ 175
],
"asm": [
- "7FF7|jg short 0x0037"
+ "7E02|jle short 0x006B"
]
},
{
- "id": 126,
- "entry": "F000:0090",
- "term": "F000:0090",
+ "id": 173,
+ "entry": "F000:006B",
+ "term": "F000:006B",
"pred": [
- 124
+ 172
],
"succ": [
- 128
+ 175,
+ 178
],
"asm": [
- "9D|popf"
+ "75FC|jne short 0x0069"
]
},
{
- "id": 53,
- "entry": "F000:0040",
- "term": "F000:0043",
+ "id": 189,
+ "entry": "F000:0072",
+ "term": "F000:0072",
"pred": [
- 51
+ 182
],
"succ": [
- 56
+ 190,
+ 192
],
"asm": [
- "BA8008|mov DX,0x0880",
- "52|push DX"
+ "7503|jne short 0x0077"
]
},
{
- "id": 124,
- "entry": "F000:008F",
- "term": "F000:008F",
+ "id": 202,
+ "entry": "F000:007C",
+ "term": "F000:007C",
"pred": [
- 122
+ 196
],
"succ": [
- 126
+ 192,
+ 204
],
"asm": [
- "51|push CX"
+ "71F6|jno short 0x0074"
]
},
{
- "id": 56,
- "entry": "F000:0044",
- "term": "F000:0044",
+ "id": 213,
+ "entry": "F000:0083",
+ "term": "F000:0083",
"pred": [
- 53
+ 207
],
"succ": [
- 58
+ 214,
+ 216
],
"asm": [
- "9D|popf"
+ "7103|jno short 0x0088"
]
},
{
- "id": 122,
+ "id": 226,
"entry": "F000:008D",
"term": "F000:008D",
"pred": [
- 120
+ 220
],
"succ": [
- 124
+ 216,
+ 228
],
"asm": [
"7BF6|jnp short 0x0085"
]
},
{
- "id": 58,
- "entry": "F000:0045",
- "term": "F000:0045",
+ "id": 235,
+ "entry": "F000:0091",
+ "term": "F000:0091",
"pred": [
- 56
+ 230
],
"succ": [
- 60
+ 236,
+ 238
],
"asm": [
- "7F02|jg short 0x0049"
+ "7B03|jnp short 0x0096"
]
},
{
- "id": 120,
- "entry": "F000:008C",
- "term": "F000:008C",
+ "id": 248,
+ "entry": "F000:009B",
+ "term": "F000:009B",
"pred": [
- 117
+ 242
],
"succ": [
- 122
+ 238,
+ 250
],
"asm": [
- "9D|popf"
+ "79F6|jns short 0x0093"
]
},
{
- "id": 60,
- "entry": "F000:0049",
- "term": "F000:004C",
+ "id": 257,
+ "entry": "F000:009F",
+ "term": "F000:009F",
"pred": [
- 58
+ 252
],
"succ": [
- 63
+ 258,
+ 260
],
"asm": [
- "BA8000|mov DX,0x0080",
- "52|push DX"
+ "7903|jns short 0x00A4"
]
},
{
- "id": 117,
- "entry": "F000:0088",
- "term": "F000:008B",
+ "id": 258,
+ "entry": "F000:00A4",
+ "term": "F000:00A4",
"pred": [
- 115
+ 257
],
"succ": [
- 120
+ 260,
+ 263
],
"asm": [
- "BA0400|mov DX,4",
- "52|push DX"
+ "70FB|jo short 0x00A1"
]
},
{
- "id": 63,
- "entry": "F000:004D",
- "term": "F000:004D",
+ "id": 274,
+ "entry": "F000:00AB",
+ "term": "F000:00AB",
"pred": [
- 60
+ 267
],
"succ": [
- 65
+ 275,
+ 277
],
"asm": [
- "9D|popf"
+ "7003|jo short 0x00B0"
]
},
{
- "id": 115,
- "entry": "F000:0083",
- "term": "F000:0083",
+ "id": 275,
+ "entry": "F000:00B0",
+ "term": "F000:00B0",
"pred": [
- 113
+ 274
],
"succ": [
- 117
+ 277,
+ 280
],
"asm": [
- "7103|jno short 0x0088"
+ "7AFB|jp short 0x00AD"
]
},
{
- "id": 65,
- "entry": "F000:004E",
- "term": "F000:004E",
+ "id": 291,
+ "entry": "F000:00B7",
+ "term": "F000:00B7",
"pred": [
- 63
+ 284
],
"succ": [
- 67
+ 292,
+ 294
],
"asm": [
- "7DF7|jge short 0x0047"
+ "7A03|jp short 0x00BC"
]
},
{
- "id": 113,
- "entry": "F000:0082",
- "term": "F000:0082",
+ "id": 46,
+ "entry": "F000:002A",
+ "term": "F000:002A",
"pred": [
- 110
+ 39
],
"succ": [
- 115
+ 69
],
"asm": [
"9D|popf"
]
},
{
- "id": 67,
- "entry": "F000:0050",
- "term": "F000:0050",
+ "id": 70,
+ "entry": "F000:002F",
+ "term": "F000:002F",
"pred": [
- 65
+ 69
],
"succ": [
- 69
+ 74
],
"asm": [
"51|push CX"
]
},
{
- "id": 110,
- "entry": "F000:007E",
- "term": "F000:0081",
+ "id": 82,
+ "entry": "F000:0033",
+ "term": "F000:0033",
"pred": [
- 108
+ 80
],
"succ": [
- 113
+ 84
],
"asm": [
- "BAFF06|mov DX,0x06FF",
+ "53|push BX"
+ ]
+ },
+ {
+ "id": 74,
+ "entry": "F000:0030",
+ "term": "F000:0030",
+ "pred": [
+ 70
+ ],
+ "succ": [
+ 80
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 90,
+ "entry": "F000:0039",
+ "term": "F000:003C",
+ "pred": [
+ 89
+ ],
+ "succ": [
+ 96
+ ],
+ "asm": [
+ "BAC008|mov DX,0x08C0",
"52|push DX"
]
},
{
- "id": 69,
- "entry": "F000:0051",
- "term": "F000:0051",
+ "id": 84,
+ "entry": "F000:0034",
+ "term": "F000:0034",
"pred": [
- 67
+ 82
],
"succ": [
- 71
+ 89
],
"asm": [
"9D|popf"
]
},
{
- "id": 108,
- "entry": "F000:007C",
- "term": "F000:007C",
+ "id": 104,
+ "entry": "F000:0040",
+ "term": "F000:0043",
"pred": [
- 106
+ 102
],
"succ": [
- 110
+ 107
],
"asm": [
- "71F6|jno short 0x0074"
+ "BA8008|mov DX,0x0880",
+ "52|push DX"
]
},
{
- "id": 71,
- "entry": "F000:0052",
- "term": "F000:0052",
+ "id": 96,
+ "entry": "F000:003D",
+ "term": "F000:003D",
"pred": [
- 69
+ 90
],
"succ": [
- 73
+ 102
],
"asm": [
- "7D02|jge short 0x0056"
+ "9D|popf"
]
},
{
- "id": 106,
- "entry": "F000:007B",
- "term": "F000:007B",
+ "id": 114,
+ "entry": "F000:0049",
+ "term": "F000:004C",
"pred": [
- 103
+ 113
],
"succ": [
- 108
+ 120
+ ],
+ "asm": [
+ "BA8000|mov DX,0x0080",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 107,
+ "entry": "F000:0044",
+ "term": "F000:0044",
+ "pred": [
+ 104
+ ],
+ "succ": [
+ 113
],
"asm": [
"9D|popf"
]
},
{
- "id": 73,
- "entry": "F000:0056",
- "term": "F000:0056",
+ "id": 128,
+ "entry": "F000:0050",
+ "term": "F000:0050",
"pred": [
- 71
+ 126
],
"succ": [
- 75
+ 130
],
"asm": [
- "7CFC|jl short 0x0054"
+ "51|push CX"
]
},
{
- "id": 103,
- "entry": "F000:0077",
- "term": "F000:007A",
+ "id": 120,
+ "entry": "F000:004D",
+ "term": "F000:004D",
"pred": [
- 101
+ 114
],
"succ": [
- 106
+ 126
],
"asm": [
- "BA0008|mov DX,0x0800",
- "52|push DX"
+ "9D|popf"
]
},
{
- "id": 75,
+ "id": 130,
+ "entry": "F000:0051",
+ "term": "F000:0051",
+ "pred": [
+ 128
+ ],
+ "succ": [
+ 135
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 141,
"entry": "F000:0058",
"term": "F000:005B",
"pred": [
- 73
+ 136
],
"succ": [
- 78
+ 145
],
"asm": [
"BA0008|mov DX,0x0800",
@@ -896,70 +1067,84 @@
]
},
{
- "id": 101,
- "entry": "F000:0072",
- "term": "F000:0072",
+ "id": 153,
+ "entry": "F000:0061",
+ "term": "F000:0061",
"pred": [
- 99
+ 152
],
"succ": [
- 103
+ 157
],
"asm": [
- "7503|jne short 0x0077"
+ "51|push CX"
]
},
{
- "id": 78,
+ "id": 145,
"entry": "F000:005C",
"term": "F000:005C",
"pred": [
- 75
+ 141
],
"succ": [
- 80
+ 152
],
"asm": [
"9D|popf"
]
},
{
- "id": 99,
- "entry": "F000:0071",
- "term": "F000:0071",
+ "id": 165,
+ "entry": "F000:0065",
+ "term": "F000:0065",
"pred": [
- 96
+ 163
],
"succ": [
- 101
+ 167
+ ],
+ "asm": [
+ "53|push BX"
+ ]
+ },
+ {
+ "id": 157,
+ "entry": "F000:0062",
+ "term": "F000:0062",
+ "pred": [
+ 153
+ ],
+ "succ": [
+ 163
],
"asm": [
"9D|popf"
]
},
{
- "id": 80,
- "entry": "F000:005D",
- "term": "F000:005D",
+ "id": 167,
+ "entry": "F000:0066",
+ "term": "F000:0066",
"pred": [
- 78
+ 165
],
"succ": [
- 82
+ 172
],
"asm": [
- "7C02|jl short 0x0061"
+ "9D|popf"
]
},
{
- "id": 96,
+ "id": 178,
"entry": "F000:006D",
"term": "F000:0070",
"pred": [
- 94
+ 173
],
"succ": [
- 99
+ 182
],
"asm": [
"BABF0C|mov DX,0x0CBF",
@@ -967,101 +1152,247 @@
]
},
{
- "id": 82,
- "entry": "F000:0061",
- "term": "F000:0061",
+ "id": 190,
+ "entry": "F000:0077",
+ "term": "F000:007A",
"pred": [
- 80
+ 189
],
"succ": [
- 84
+ 196
+ ],
+ "asm": [
+ "BA0008|mov DX,0x0800",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 182,
+ "entry": "F000:0071",
+ "term": "F000:0071",
+ "pred": [
+ 178
+ ],
+ "succ": [
+ 189
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 204,
+ "entry": "F000:007E",
+ "term": "F000:0081",
+ "pred": [
+ 202
+ ],
+ "succ": [
+ 207
+ ],
+ "asm": [
+ "BAFF06|mov DX,0x06FF",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 196,
+ "entry": "F000:007B",
+ "term": "F000:007B",
+ "pred": [
+ 190
+ ],
+ "succ": [
+ 202
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 214,
+ "entry": "F000:0088",
+ "term": "F000:008B",
+ "pred": [
+ 213
+ ],
+ "succ": [
+ 220
+ ],
+ "asm": [
+ "BA0400|mov DX,4",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 207,
+ "entry": "F000:0082",
+ "term": "F000:0082",
+ "pred": [
+ 204
+ ],
+ "succ": [
+ 213
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 228,
+ "entry": "F000:008F",
+ "term": "F000:008F",
+ "pred": [
+ 226
+ ],
+ "succ": [
+ 230
],
"asm": [
"51|push CX"
]
},
{
- "id": 94,
- "entry": "F000:006B",
- "term": "F000:006B",
+ "id": 220,
+ "entry": "F000:008C",
+ "term": "F000:008C",
"pred": [
- 92
+ 214
],
"succ": [
- 96
+ 226
],
"asm": [
- "75FC|jne short 0x0069"
+ "9D|popf"
]
},
{
- "id": 84,
- "entry": "F000:0062",
- "term": "F000:0062",
+ "id": 236,
+ "entry": "F000:0096",
+ "term": "F000:0099",
"pred": [
- 82
+ 235
],
"succ": [
- 86
+ 242
+ ],
+ "asm": [
+ "BAFF0E|mov DX,0x0EFF",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 230,
+ "entry": "F000:0090",
+ "term": "F000:0090",
+ "pred": [
+ 228
+ ],
+ "succ": [
+ 235
],
"asm": [
"9D|popf"
]
},
{
- "id": 92,
- "entry": "F000:0067",
- "term": "F000:0067",
+ "id": 250,
+ "entry": "F000:009D",
+ "term": "F000:009D",
"pred": [
- 90
+ 248
],
"succ": [
- 94
+ 252
],
"asm": [
- "7E02|jle short 0x006B"
+ "51|push CX"
]
},
{
- "id": 86,
- "entry": "F000:0063",
- "term": "F000:0063",
+ "id": 242,
+ "entry": "F000:009A",
+ "term": "F000:009A",
"pred": [
- 84
+ 236
],
"succ": [
- 88
+ 248
],
"asm": [
- "7EFA|jle short 0x005F"
+ "9D|popf"
]
},
{
- "id": 90,
- "entry": "F000:0066",
- "term": "F000:0066",
+ "id": 252,
+ "entry": "F000:009E",
+ "term": "F000:009E",
"pred": [
- 88
+ 250
],
"succ": [
- 92
+ 257
],
"asm": [
"9D|popf"
]
},
{
- "id": 88,
- "entry": "F000:0065",
- "term": "F000:0065",
+ "id": 263,
+ "entry": "F000:00A6",
+ "term": "F000:00A9",
"pred": [
- 86
+ 258
],
"succ": [
- 90
+ 267
],
"asm": [
- "53|push BX"
+ "BA0008|mov DX,0x0800",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 267,
+ "entry": "F000:00AA",
+ "term": "F000:00AA",
+ "pred": [
+ 263
+ ],
+ "succ": [
+ 274
+ ],
+ "asm": [
+ "9D|popf"
+ ]
+ },
+ {
+ "id": 280,
+ "entry": "F000:00B2",
+ "term": "F000:00B5",
+ "pred": [
+ 275
+ ],
+ "succ": [
+ 284
+ ],
+ "asm": [
+ "BA0408|mov DX,0x0804",
+ "52|push DX"
+ ]
+ },
+ {
+ "id": 284,
+ "entry": "F000:00B6",
+ "term": "F000:00B6",
+ "pred": [
+ 280
+ ],
+ "succ": [
+ 291
+ ],
+ "asm": [
+ "9D|popf"
]
}
],
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump2.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump2.json
index 228cd4e856..65687ce468 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump2.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/jump2.json
@@ -6,7 +6,7 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:FFFE",
- "lastExecutedBlockId": 59,
+ "lastExecutedBlockId": 96,
"blocks": [
{
"id": 2,
@@ -15,7 +15,7 @@
"pred": [],
"succ": [
5,
- 59
+ 96
],
"asm": [
"BC0010|mov SP,0x1000",
@@ -24,12 +24,12 @@
]
},
{
- "id": 59,
+ "id": 96,
"entry": "F000:FFF8",
"term": "F000:FFFE",
"pred": [
2,
- 57
+ 93
],
"succ": [],
"asm": [
@@ -46,7 +46,7 @@
2
],
"succ": [
- 11
+ 12
],
"asm": [
"BB00F0|mov BX,0xF000",
@@ -56,31 +56,31 @@
]
},
{
- "id": 57,
+ "id": 93,
"entry": "F000:0010",
"term": "F000:0010",
"pred": [
- 13,
- 55
+ 14,
+ 86
],
"succ": [
- 59
+ 96
],
"asm": [
"C3|ret near"
]
},
{
- "id": 11,
+ "id": 12,
"entry": "F000:000B",
"term": "F000:000C",
"pred": [
5,
- 11
+ 12
],
"succ": [
- 11,
- 13
+ 12,
+ 14
],
"asm": [
"51|push CX",
@@ -88,43 +88,44 @@
]
},
{
- "id": 55,
+ "id": 86,
"entry": "F000:12AA",
"term": "F000:12AA",
"pred": [
- 52
+ 83
],
"succ": [
- 57
+ 93
],
"asm": [
"C20A00|ret near 0x000A"
]
},
{
- "id": 13,
+ "id": 14,
"entry": "F000:000E",
"term": "F000:000E",
"pred": [
- 11
+ 12
],
"succ": [
- 15,
- 57
+ 26,
+ 93
],
"asm": [
"FFD0|call near AX"
]
},
{
- "id": 52,
+ "id": 83,
"entry": "F000:12A4",
"term": "F000:12A7",
"pred": [
- 50
+ 81
],
"succ": [
- 55
+ 86,
+ 88
],
"asm": [
"B90000|mov CX,0",
@@ -132,14 +133,16 @@
]
},
{
- "id": 15,
+ "id": 26,
"entry": "F000:1290",
"term": "F000:1293",
"pred": [
- 13
+ 14,
+ 26
],
"succ": [
- 18
+ 26,
+ 29
],
"asm": [
"B9FFFF|mov CX,0xFFFF",
@@ -147,29 +150,43 @@
]
},
{
- "id": 50,
+ "id": 88,
+ "entry": "F000:12A9",
+ "dead": true,
+ "term": "F000:12A9",
+ "pred": [
+ 83
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 81,
"entry": "F000:12A2",
"term": "F000:12A2",
"pred": [
- 25,
- 48
+ 40,
+ 73
],
"succ": [
- 52
+ 40,
+ 83
],
"asm": [
"E3F9|jcxz short 0x129D"
]
},
{
- "id": 18,
+ "id": 29,
"entry": "F000:1295",
"term": "F000:1298",
"pred": [
- 15
+ 26
],
"succ": [
- 21
+ 32
],
"asm": [
"BA4000|mov DX,0x0040",
@@ -177,116 +194,104 @@
]
},
{
- "id": 48,
- "entry": "E342:EBF1",
- "term": "E342:EBF1",
- "pred": [
- 46
- ],
- "succ": [
- 50
- ],
- "asm": [
- "CB|ret far"
- ]
- },
- {
- "id": 25,
+ "id": 40,
"entry": "F000:129D",
"term": "F000:129D",
"pred": [
- 23
+ 39,
+ 81
],
"succ": [
- 27,
- 50
+ 44,
+ 81
],
"asm": [
"9AE0EB42E3|call far E342:EBE0"
]
},
{
- "id": 21,
- "entry": "F000:1299",
- "term": "F000:1299",
+ "id": 73,
+ "entry": "E342:EBF1",
+ "term": "E342:EBF1",
"pred": [
- 18
+ 71
],
"succ": [
- 23
+ 81
],
"asm": [
- "9D|popf"
+ "CB|ret far"
]
},
{
- "id": 46,
- "entry": "E342:EBEE",
- "term": "E342:EBEE",
+ "id": 32,
+ "entry": "F000:1299",
+ "term": "F000:1299",
"pred": [
- 43
+ 29
],
"succ": [
- 48
+ 39
],
"asm": [
- "E001|loopne 0xEBF1"
+ "9D|popf"
]
},
{
- "id": 27,
+ "id": 44,
"entry": "E342:EBE0",
"term": "E342:EBE0",
"pred": [
- 25
+ 40
],
"succ": [
- 29,
- 38
+ 49,
+ 61
],
"asm": [
"FF160030|call near word ptr DS:[0x3000]"
]
},
{
- "id": 23,
+ "id": 39,
"entry": "F000:129A",
"term": "F000:129A",
"pred": [
- 21
+ 32
],
"succ": [
- 25
+ 40,
+ 42
],
"asm": [
"E101|loope 0x129D"
]
},
{
- "id": 43,
- "entry": "E342:EBE9",
- "term": "E342:EBEC",
+ "id": 71,
+ "entry": "E342:EBEE",
+ "term": "E342:EBEE",
"pred": [
- 41
+ 68
],
"succ": [
- 46
+ 73,
+ 75
],
"asm": [
- "B90100|mov CX,1",
- "E0FB|loopne 0xEBE9"
+ "E001|loopne 0xEBF1"
]
},
{
- "id": 29,
+ "id": 49,
"entry": "E342:FDE0",
"term": "E342:FDE7",
"pred": [
- 27
+ 44
],
"succ": [
- 34,
- 36
+ 56,
+ 58
],
"asm": [
"BBF02F|mov BX,0x2FF0",
@@ -296,15 +301,15 @@
]
},
{
- "id": 38,
+ "id": 61,
"entry": "E342:EBE4",
"term": "E342:EBE7",
"pred": [
- 27,
- 36
+ 44,
+ 58
],
"succ": [
- 41
+ 63
],
"asm": [
"BA0000|mov DX,0",
@@ -312,47 +317,90 @@
]
},
{
- "id": 41,
- "entry": "E342:EBE8",
- "term": "E342:EBE8",
+ "id": 42,
+ "entry": "F000:129C",
+ "dead": true,
+ "term": "F000:129C",
+ "pred": [
+ 39
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 75,
+ "entry": "E342:EBF0",
+ "dead": true,
+ "term": "E342:EBF0",
+ "pred": [
+ 71
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 68,
+ "entry": "E342:EBE9",
+ "term": "E342:EBEC",
"pred": [
- 38
+ 63,
+ 68
],
"succ": [
- 43
+ 68,
+ 71
],
"asm": [
- "9D|popf"
+ "B90100|mov CX,1",
+ "E0FB|loopne 0xEBE9"
]
},
{
- "id": 34,
+ "id": 56,
"entry": "F000:4000",
"term": "F000:4000",
"pred": [
- 29
+ 49
],
"succ": [
- 36
+ 58
],
"asm": [
"CA0200|ret far 2"
]
},
{
- "id": 36,
+ "id": 58,
"entry": "E342:FDEA",
"term": "E342:FDEA",
"pred": [
- 29,
- 34
+ 49,
+ 56
],
"succ": [
- 38
+ 61
],
"asm": [
"C3|ret near"
]
+ },
+ {
+ "id": 63,
+ "entry": "E342:EBE8",
+ "term": "E342:EBE8",
+ "pred": [
+ 61
+ ],
+ "succ": [
+ 68
+ ],
+ "asm": [
+ "9D|popf"
+ ]
}
],
"truncated": false
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/linearsamesegmenteddifferent.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/linearsamesegmenteddifferent.json
index b414437269..07fbb6c60c 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/linearsamesegmenteddifferent.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/linearsamesegmenteddifferent.json
@@ -6,7 +6,7 @@
"F000:FFF0"
],
"lastExecutedAddress": "FFEF:0116",
- "lastExecutedBlockId": 20,
+ "lastExecutedBlockId": 31,
"blocks": [
{
"id": 2,
@@ -21,11 +21,11 @@
]
},
{
- "id": 20,
+ "id": 31,
"entry": "FFEF:0110",
"term": "FFEF:0116",
"pred": [
- 18
+ 30
],
"succ": [],
"asm": [
@@ -42,7 +42,7 @@
2
],
"succ": [
- 11
+ 19
],
"asm": [
"BC0010|mov SP,0x1000",
@@ -55,42 +55,42 @@
]
},
{
- "id": 18,
+ "id": 30,
"entry": "FFEF:000D",
"term": "FFEF:000D",
"pred": [
- 13
+ 20
],
"succ": [
- 20
+ 31
],
"asm": [
"E90001|jmp near 0x0110"
]
},
{
- "id": 11,
+ "id": 19,
"entry": "F000:FEFD",
"term": "F000:FEFD",
"pred": [
3
],
"succ": [
- 13
+ 20
],
"asm": [
"E90001|jmp near 0"
]
},
{
- "id": 13,
+ "id": 20,
"entry": "F000:0000",
"term": "F000:0007",
"pred": [
- 11
+ 19
],
"succ": [
- 18
+ 30
],
"asm": [
"B80100|mov AX,1",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/lockprefix.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/lockprefix.json
index d8ce5b2320..6f965ad209 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/lockprefix.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/lockprefix.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0057",
- "lastExecutedBlockId": 36,
+ "lastExecutedBlockId": 62,
"blocks": [
{
- "id": 36,
+ "id": 62,
"entry": "F000:0057",
"term": "F000:0057",
"pred": [
- 20,
- 32
+ 37,
+ 58
],
"succ": [],
"asm": [
@@ -34,18 +34,18 @@
]
},
{
- "id": 20,
+ "id": 37,
"entry": "F000:005A",
"term": "F000:0068",
"pred": [
3,
- 28,
- 32
+ 52,
+ 58
],
"succ": [
- 28,
- 32,
- 36
+ 52,
+ 58,
+ 62
],
"asm": [
"55|push BP",
@@ -58,16 +58,16 @@
]
},
{
- "id": 32,
+ "id": 58,
"entry": "F000:004E",
"term": "F000:0054",
"pred": [
- 20,
- 28
+ 37,
+ 52
],
"succ": [
- 20,
- 36
+ 37,
+ 62
],
"asm": [
"C70604005700|mov word ptr DS:[4],0x0057",
@@ -82,8 +82,8 @@
2
],
"succ": [
- 20,
- 28
+ 37,
+ 52
],
"asm": [
"31C0|xor AX,AX",
@@ -104,16 +104,16 @@
]
},
{
- "id": 28,
+ "id": 52,
"entry": "F000:0045",
"term": "F000:004B",
"pred": [
3,
- 20
+ 37
],
"succ": [
- 20,
- 32
+ 37,
+ 58
],
"asm": [
"C70604004E00|mov word ptr DS:[4],0x004E",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/ownerlesscallcycle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/ownerlesscallcycle.json
index cf8432aab9..c548675450 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/ownerlesscallcycle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/ownerlesscallcycle.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0023",
- "lastExecutedBlockId": 20,
+ "lastExecutedBlockId": 25,
"blocks": [
{
- "id": 20,
+ "id": 25,
"entry": "F000:0023",
"term": "F000:0023",
"pred": [
- 11
+ 19
],
"succ": [],
"asm": [
@@ -33,17 +33,17 @@
]
},
{
- "id": 11,
+ "id": 19,
"entry": "F000:0016",
"term": "F000:0021",
"pred": [
3,
- 16,
- 18
+ 23,
+ 27
],
"succ": [
- 16,
- 20
+ 23,
+ 25
],
"asm": [
"2EFE062500|inc byte ptr CS:[0x0025]",
@@ -60,7 +60,7 @@
2
],
"succ": [
- 11
+ 19
],
"asm": [
"B80000|mov AX,0",
@@ -73,29 +73,29 @@
]
},
{
- "id": 16,
+ "id": 23,
"entry": "F000:0013",
"term": "F000:0013",
"pred": [
- 11
+ 19
],
"succ": [
- 11,
- 18
+ 19,
+ 27
],
"asm": [
"E80E00|call near 0x0024"
]
},
{
- "id": 18,
+ "id": 27,
"entry": "F000:0024",
"term": "F000:0024",
"pred": [
- 16
+ 23
],
"succ": [
- 11
+ 19
],
"asm": [
"C3|ret near"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_cross_function_loop.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_cross_function_loop.json
index 553ab73267..14a7b83a92 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_cross_function_loop.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_cross_function_loop.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0014",
- "lastExecutedBlockId": 26,
+ "lastExecutedBlockId": 42,
"blocks": [
{
- "id": 26,
+ "id": 42,
"entry": "F000:0014",
"term": "F000:0014",
"pred": [
- 19,
- 24
+ 12,
+ 36
],
"succ": [],
"asm": [
@@ -34,30 +34,30 @@
]
},
{
- "id": 24,
+ "id": 12,
"entry": "F000:001A",
"term": "F000:001A",
"pred": [
9
],
"succ": [
- 26
+ 42
],
"asm": [
"C3|ret near"
]
},
{
- "id": 19,
+ "id": 36,
"entry": "F000:000E",
"term": "F000:0011",
"pred": [
3,
- 17
+ 19
],
"succ": [
- 14,
- 26
+ 16,
+ 42
],
"asm": [
"B90200|mov CX,2",
@@ -73,7 +73,7 @@
],
"succ": [
9,
- 19
+ 36
],
"asm": [
"B80010|mov AX,0x1000",
@@ -89,11 +89,11 @@
"term": "F000:0016",
"pred": [
3,
- 22
+ 21
],
"succ": [
12,
- 24
+ 14
],
"asm": [
"49|dec CX",
@@ -101,16 +101,16 @@
]
},
{
- "id": 14,
+ "id": 16,
"entry": "F000:001B",
"term": "F000:001C",
"pred": [
- 12,
- 19
+ 14,
+ 36
],
"succ": [
- 17,
- 22
+ 19,
+ 21
],
"asm": [
"49|dec CX",
@@ -118,39 +118,39 @@
]
},
{
- "id": 17,
+ "id": 19,
"entry": "F000:0020",
"term": "F000:0020",
"pred": [
- 14
+ 16
],
"succ": [
- 19
+ 36
],
"asm": [
"C3|ret near"
]
},
{
- "id": 12,
+ "id": 14,
"entry": "F000:0018",
"term": "F000:0018",
"pred": [
9
],
"succ": [
- 14
+ 16
],
"asm": [
"EB01|jmp short 0x001B"
]
},
{
- "id": 22,
+ "id": 21,
"entry": "F000:001E",
"term": "F000:001E",
"pred": [
- 14
+ 16
],
"succ": [
9
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_indirect_call_jump.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_indirect_call_jump.json
index 28776d05c8..015625a76c 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_indirect_call_jump.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_indirect_call_jump.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0016",
- "lastExecutedBlockId": 15,
+ "lastExecutedBlockId": 22,
"blocks": [
{
- "id": 15,
+ "id": 22,
"entry": "F000:0016",
"term": "F000:0016",
"pred": [
- 12
+ 19
],
"succ": [],
"asm": [
@@ -33,15 +33,15 @@
]
},
{
- "id": 12,
+ "id": 19,
"entry": "F000:000D",
"term": "F000:0010",
"pred": [
3,
- 9
+ 15
],
"succ": [
- 15
+ 22
],
"asm": [
"BB1600|mov BX,0x0016",
@@ -56,8 +56,8 @@
2
],
"succ": [
- 9,
- 12
+ 15,
+ 19
],
"asm": [
"B80010|mov AX,0x1000",
@@ -68,14 +68,14 @@
]
},
{
- "id": 9,
+ "id": 15,
"entry": "F000:0012",
"term": "F000:0015",
"pred": [
3
],
"succ": [
- 12
+ 19
],
"asm": [
"B81111|mov AX,0x1111",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_jump_into_function_middle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_jump_into_function_middle.json
index df8adc75ee..9036f73da7 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_jump_into_function_middle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_jump_into_function_middle.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:000E",
- "lastExecutedBlockId": 17,
+ "lastExecutedBlockId": 26,
"blocks": [
{
- "id": 17,
+ "id": 26,
"entry": "F000:000E",
"term": "F000:000E",
"pred": [
- 12,
- 15
+ 20,
+ 23
],
"succ": [],
"asm": [
@@ -34,16 +34,16 @@
]
},
{
- "id": 15,
+ "id": 23,
"entry": "F000:0012",
"term": "F000:0015",
"pred": [
8,
- 14
+ 21
],
"succ": [
- 12,
- 17
+ 20,
+ 26
],
"asm": [
"BB2222|mov BX,0x2222",
@@ -51,16 +51,16 @@
]
},
{
- "id": 12,
+ "id": 20,
"entry": "F000:000B",
"term": "F000:000B",
"pred": [
3,
- 15
+ 23
],
"succ": [
- 14,
- 17
+ 21,
+ 26
],
"asm": [
"E80800|call near 0x0016"
@@ -75,7 +75,7 @@
],
"succ": [
8,
- 12
+ 20
],
"asm": [
"B80010|mov AX,0x1000",
@@ -92,21 +92,21 @@
3
],
"succ": [
- 15
+ 23
],
"asm": [
"B81111|mov AX,0x1111"
]
},
{
- "id": 14,
+ "id": 21,
"entry": "F000:0016",
"term": "F000:0016",
"pred": [
- 12
+ 20
],
"succ": [
- 15
+ 23
],
"asm": [
"EBFA|jmp short 0x0012"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_mixed_activation_cycle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_mixed_activation_cycle.json
index 82176597ca..ec29bf06be 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_mixed_activation_cycle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_mixed_activation_cycle.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0014",
- "lastExecutedBlockId": 25,
+ "lastExecutedBlockId": 39,
"blocks": [
{
- "id": 25,
+ "id": 39,
"entry": "F000:0014",
"term": "F000:0014",
"pred": [
- 18,
- 20
+ 31,
+ 34
],
"succ": [],
"asm": [
@@ -34,7 +34,7 @@
]
},
{
- "id": 18,
+ "id": 31,
"entry": "F000:001E",
"term": "F000:001E",
"pred": [
@@ -42,24 +42,24 @@
16
],
"succ": [
- 20,
- 25
+ 34,
+ 39
],
"asm": [
"C3|ret near"
]
},
{
- "id": 20,
+ "id": 34,
"entry": "F000:000E",
"term": "F000:0011",
"pred": [
3,
- 18
+ 31
],
"succ": [
13,
- 25
+ 39
],
"asm": [
"B90200|mov CX,2",
@@ -75,7 +75,7 @@
],
"succ": [
9,
- 20
+ 34
],
"asm": [
"B80010|mov AX,0x1000",
@@ -93,7 +93,7 @@
13
],
"succ": [
- 18
+ 31
],
"asm": [
"C3|ret near"
@@ -105,11 +105,11 @@
"term": "F000:001B",
"pred": [
3,
- 23
+ 18
],
"succ": [
11,
- 18
+ 31
],
"asm": [
"E80100|call near 0x001F"
@@ -121,11 +121,11 @@
"term": "F000:0016",
"pred": [
11,
- 20
+ 34
],
"succ": [
16,
- 23
+ 18
],
"asm": [
"49|dec CX",
@@ -147,7 +147,7 @@
]
},
{
- "id": 23,
+ "id": 18,
"entry": "F000:0018",
"term": "F000:0018",
"pred": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_dominated_shared.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_dominated_shared.json
index 56e1e7616a..7f76013a03 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_dominated_shared.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_dominated_shared.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:001C",
- "lastExecutedBlockId": 37,
+ "lastExecutedBlockId": 62,
"blocks": [
{
- "id": 37,
+ "id": 62,
"entry": "F000:001C",
"term": "F000:001C",
"pred": [
- 15,
- 32
+ 19,
+ 57
],
"succ": [],
"asm": [
@@ -34,18 +34,18 @@
]
},
{
- "id": 15,
+ "id": 19,
"entry": "F000:0033",
"term": "F000:0036",
"pred": [
12,
- 23
+ 17
],
"succ": [
- 18,
- 26,
- 32,
- 37
+ 36,
+ 44,
+ 57,
+ 62
],
"asm": [
"B933C3|mov CX,0xC333",
@@ -53,16 +53,16 @@
]
},
{
- "id": 32,
+ "id": 57,
"entry": "F000:0017",
"term": "F000:0019",
"pred": [
- 15,
- 26
+ 19,
+ 44
],
"succ": [
- 29,
- 37
+ 46,
+ 62
],
"asm": [
"B001|mov AL,1",
@@ -78,7 +78,7 @@
],
"succ": [
9,
- 18
+ 36
],
"asm": [
"B80010|mov AX,0x1000",
@@ -89,16 +89,16 @@
]
},
{
- "id": 18,
+ "id": 36,
"entry": "F000:000D",
"term": "F000:000F",
"pred": [
3,
- 15
+ 19
],
"succ": [
9,
- 26
+ 44
],
"asm": [
"B001|mov AL,1",
@@ -106,16 +106,16 @@
]
},
{
- "id": 26,
+ "id": 44,
"entry": "F000:0012",
"term": "F000:0014",
"pred": [
- 15,
- 18
+ 19,
+ 36
],
"succ": [
- 29,
- 32
+ 46,
+ 57
],
"asm": [
"B000|mov AL,0",
@@ -128,10 +128,10 @@
"term": "F000:002C",
"pred": [
9,
- 29
+ 46
],
"succ": [
- 15
+ 19
],
"asm": [
"BB11A1|mov BX,0xA111",
@@ -139,15 +139,15 @@
]
},
{
- "id": 23,
+ "id": 17,
"entry": "F000:002E",
"term": "F000:0031",
"pred": [
- 21,
- 35
+ 14,
+ 50
],
"succ": [
- 15
+ 19
],
"asm": [
"BB22B2|mov BX,0xB222",
@@ -155,16 +155,16 @@
]
},
{
- "id": 29,
+ "id": 46,
"entry": "F000:0023",
"term": "F000:0025",
"pred": [
- 26,
- 32
+ 44,
+ 57
],
"succ": [
12,
- 35
+ 50
],
"asm": [
"3C00|cmp AL,0",
@@ -177,11 +177,11 @@
"term": "F000:001F",
"pred": [
3,
- 18
+ 36
],
"succ": [
12,
- 21
+ 14
],
"asm": [
"3C00|cmp AL,0",
@@ -189,28 +189,28 @@
]
},
{
- "id": 21,
+ "id": 14,
"entry": "F000:0021",
"term": "F000:0021",
"pred": [
9
],
"succ": [
- 23
+ 17
],
"asm": [
"EB0B|jmp short 0x002E"
]
},
{
- "id": 35,
+ "id": 50,
"entry": "F000:0027",
"term": "F000:0027",
"pred": [
- 29
+ 46
],
"succ": [
- 23
+ 17
],
"asm": [
"EB05|jmp short 0x002E"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_irreducible_shared.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_irreducible_shared.json
index 15cec2942d..cc52205841 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_irreducible_shared.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_multi_entry_irreducible_shared.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0014",
- "lastExecutedBlockId": 26,
+ "lastExecutedBlockId": 42,
"blocks": [
{
- "id": 26,
+ "id": 42,
"entry": "F000:0014",
"term": "F000:0014",
"pred": [
- 19,
- 24
+ 16,
+ 34
],
"succ": [],
"asm": [
@@ -34,30 +34,30 @@
]
},
{
- "id": 24,
+ "id": 16,
"entry": "F000:001E",
"term": "F000:001E",
"pred": [
11
],
"succ": [
- 26
+ 42
],
"asm": [
"C3|ret near"
]
},
{
- "id": 19,
+ "id": 34,
"entry": "F000:000E",
"term": "F000:0011",
"pred": [
3,
- 17
+ 20
],
"succ": [
- 22,
- 26
+ 36,
+ 42
],
"asm": [
"B90100|mov CX,1",
@@ -73,7 +73,7 @@
],
"succ": [
9,
- 19
+ 34
],
"asm": [
"B80010|mov AX,0x1000",
@@ -93,7 +93,7 @@
],
"succ": [
14,
- 24
+ 16
],
"asm": [
"83F900|cmp CX,0",
@@ -101,11 +101,11 @@
]
},
{
- "id": 22,
+ "id": 36,
"entry": "F000:0017",
"term": "F000:0017",
"pred": [
- 19
+ 34
],
"succ": [
14
@@ -115,14 +115,14 @@
]
},
{
- "id": 17,
+ "id": 20,
"entry": "F000:0024",
"term": "F000:0024",
"pred": [
14
],
"succ": [
- 19
+ 34
],
"asm": [
"C3|ret near"
@@ -148,11 +148,11 @@
"term": "F000:0022",
"pred": [
11,
- 22
+ 36
],
"succ": [
11,
- 17
+ 20
],
"asm": [
"83F901|cmp CX,1",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_shared_tail.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_shared_tail.json
index 55335c438f..fe8d9cd94d 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_shared_tail.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/partition_shared_tail.json
@@ -6,15 +6,15 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:000E",
- "lastExecutedBlockId": 19,
+ "lastExecutedBlockId": 30,
"blocks": [
{
- "id": 19,
+ "id": 30,
"entry": "F000:000E",
"term": "F000:000E",
"pred": [
11,
- 14
+ 23
],
"succ": [],
"asm": [
@@ -39,11 +39,11 @@
"term": "F000:001C",
"pred": [
8,
- 16
+ 24
],
"succ": [
- 14,
- 19
+ 23,
+ 30
],
"asm": [
"B93333|mov CX,0x3333",
@@ -51,7 +51,7 @@
]
},
{
- "id": 14,
+ "id": 23,
"entry": "F000:000B",
"term": "F000:000B",
"pred": [
@@ -59,8 +59,8 @@
11
],
"succ": [
- 16,
- 19
+ 24,
+ 30
],
"asm": [
"E80600|call near 0x0014"
@@ -75,7 +75,7 @@
],
"succ": [
8,
- 14
+ 23
],
"asm": [
"B80010|mov AX,0x1000",
@@ -100,11 +100,11 @@
]
},
{
- "id": 16,
+ "id": 24,
"entry": "F000:0014",
"term": "F000:0017",
"pred": [
- 14
+ 23
],
"succ": [
11
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/rep.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/rep.json
index 3af9e1479a..6ec0b1594d 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/rep.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/rep.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:6026",
- "lastExecutedBlockId": 216,
+ "lastExecutedBlockId": 368,
"blocks": [
{
- "id": 216,
+ "id": 368,
"entry": "F000:601B",
"term": "F000:6026",
"pred": [
- 212
+ 362
],
"succ": [],
"asm": [
@@ -38,14 +38,14 @@
]
},
{
- "id": 212,
+ "id": 362,
"entry": "F000:5031",
"term": "F000:5034",
"pred": [
- 210
+ 351
],
"succ": [
- 216
+ 368
],
"asm": [
"FC|cld",
@@ -84,14 +84,14 @@
]
},
{
- "id": 210,
+ "id": 351,
"entry": "F000:5030",
"term": "F000:5030",
"pred": [
- 201
+ 343
],
"succ": [
- 212
+ 362
],
"asm": [
"9D|popf"
@@ -105,21 +105,21 @@
5
],
"succ": [
- 25
+ 46
],
"asm": [
"9D|popf"
]
},
{
- "id": 201,
+ "id": 343,
"entry": "F000:501D",
"term": "F000:502F",
"pred": [
- 199
+ 329
],
"succ": [
- 210
+ 351
],
"asm": [
"B92360|mov CX,0x6023",
@@ -133,14 +133,14 @@
]
},
{
- "id": 25,
+ "id": 46,
"entry": "F000:0023",
"term": "F000:003B",
"pred": [
23
],
"succ": [
- 39
+ 72
],
"asm": [
"F3A4|rep movs byte ptr ES:[DI],byte ptr DS:[SI]",
@@ -159,28 +159,28 @@
]
},
{
- "id": 199,
+ "id": 329,
"entry": "F000:501C",
"term": "F000:501C",
"pred": [
- 196
+ 324
],
"succ": [
- 201
+ 343
],
"asm": [
"9D|popf"
]
},
{
- "id": 39,
+ "id": 72,
"entry": "F000:1000",
"term": "F000:1005",
"pred": [
- 25
+ 46
],
"succ": [
- 43
+ 78
],
"asm": [
"B90000|mov CX,0",
@@ -189,14 +189,14 @@
]
},
{
- "id": 196,
+ "id": 324,
"entry": "F000:5018",
"term": "F000:501B",
"pred": [
- 192
+ 318
],
"succ": [
- 199
+ 329
],
"asm": [
"BB0000|mov BX,0",
@@ -204,28 +204,29 @@
]
},
{
- "id": 43,
+ "id": 78,
"entry": "F000:0FFE",
"term": "F000:0FFE",
"pred": [
- 39
+ 72
],
"succ": [
- 45
+ 79
],
"asm": [
"EB07|jmp short 0x1007"
]
},
{
- "id": 192,
+ "id": 318,
"entry": "F000:5010",
"term": "F000:5015",
"pred": [
- 188
+ 312
],
"succ": [
- 196
+ 324,
+ 326
],
"asm": [
"B90200|mov CX,2",
@@ -234,14 +235,14 @@
]
},
{
- "id": 45,
+ "id": 79,
"entry": "F000:1007",
"term": "F000:100C",
"pred": [
- 43
+ 78
],
"succ": [
- 49
+ 87
],
"asm": [
"B90A11|mov CX,0x110A",
@@ -250,14 +251,28 @@
]
},
{
- "id": 188,
+ "id": 326,
+ "entry": "F000:5017",
+ "dead": true,
+ "term": "F000:5017",
+ "pred": [
+ 318
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 312,
"entry": "F000:5008",
"term": "F000:500D",
"pred": [
- 184
+ 309
],
"succ": [
- 192
+ 318,
+ 320
],
"asm": [
"B90200|mov CX,2",
@@ -266,28 +281,42 @@
]
},
{
- "id": 49,
+ "id": 87,
"entry": "F000:0FFC",
"term": "F000:0FFC",
"pred": [
- 45
+ 79
],
"succ": [
- 51
+ 88
],
"asm": [
"EB10|jmp short 0x100E"
]
},
{
- "id": 184,
+ "id": 320,
+ "entry": "F000:500F",
+ "dead": true,
+ "term": "F000:500F",
+ "pred": [
+ 312
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 309,
"entry": "F000:5000",
"term": "F000:5005",
"pred": [
- 178
+ 299
],
"succ": [
- 188
+ 312,
+ 314
],
"asm": [
"B90200|mov CX,2",
@@ -296,28 +325,41 @@
]
},
{
- "id": 51,
+ "id": 88,
"entry": "F000:100E",
"term": "F000:100E",
"pred": [
- 49
+ 87
],
"succ": [
- 53
+ 92
],
"asm": [
"FFE1|jmp near CX"
]
},
{
- "id": 178,
+ "id": 314,
+ "entry": "F000:5007",
+ "dead": true,
+ "term": "F000:5007",
+ "pred": [
+ 309
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 299,
"entry": "F000:403F",
"term": "F000:4049",
"pred": [
- 176
+ 289
],
"succ": [
- 184
+ 309
],
"asm": [
"BE0030|mov SI,0x3000",
@@ -328,14 +370,14 @@
]
},
{
- "id": 53,
+ "id": 92,
"entry": "F000:110A",
"term": "F000:1110",
"pred": [
- 51
+ 88
],
"succ": [
- 57
+ 95
],
"asm": [
"B90500|mov CX,5",
@@ -344,42 +386,42 @@
]
},
{
- "id": 176,
+ "id": 289,
"entry": "F000:403E",
"term": "F000:403E",
"pred": [
- 169
+ 282
],
"succ": [
- 178
+ 299
],
"asm": [
"9D|popf"
]
},
{
- "id": 57,
+ "id": 95,
"entry": "F000:1111",
"term": "F000:1111",
"pred": [
- 53
+ 92
],
"succ": [
- 59
+ 101
],
"asm": [
"9D|popf"
]
},
{
- "id": 169,
+ "id": 282,
"entry": "F000:4030",
"term": "F000:403D",
"pred": [
- 167
+ 281
],
"succ": [
- 176
+ 289
],
"asm": [
"B80706|mov AX,0x0607",
@@ -391,14 +433,14 @@
]
},
{
- "id": 59,
+ "id": 101,
"entry": "F000:1112",
"term": "F000:1118",
"pred": [
- 57
+ 95
],
"succ": [
- 64
+ 109
],
"asm": [
"F259|pop CX",
@@ -408,42 +450,42 @@
]
},
{
- "id": 167,
+ "id": 281,
"entry": "F000:122C",
"term": "F000:122C",
"pred": [
- 163
+ 275
],
"succ": [
- 169
+ 282
],
"asm": [
"E9012E|jmp near 0x4030"
]
},
{
- "id": 64,
+ "id": 109,
"entry": "F000:200A",
"term": "F000:200A",
"pred": [
- 59
+ 101
],
"succ": [
- 66
+ 110
],
"asm": [
"53|push BX"
]
},
{
- "id": 163,
+ "id": 275,
"entry": "F000:4029",
"term": "F000:402E",
"pred": [
- 161
+ 269
],
"succ": [
- 167
+ 281
],
"asm": [
"B84000|mov AX,0x0040",
@@ -452,42 +494,43 @@
]
},
{
- "id": 66,
+ "id": 110,
"entry": "F000:200B",
"term": "F000:200B",
"pred": [
- 64
+ 109
],
"succ": [
- 68
+ 114
],
"asm": [
"9D|popf"
]
},
{
- "id": 161,
+ "id": 269,
"entry": "F000:4028",
"term": "F000:4028",
"pred": [
- 158
+ 266
],
"succ": [
- 163
+ 275
],
"asm": [
"9D|popf"
]
},
{
- "id": 68,
+ "id": 114,
"entry": "F000:200C",
"term": "F000:202A",
"pred": [
- 66
+ 110
],
"succ": [
- 82
+ 127,
+ 129
],
"asm": [
"B90200|mov CX,2",
@@ -506,14 +549,14 @@
]
},
{
- "id": 158,
+ "id": 266,
"entry": "F000:4024",
"term": "F000:4027",
"pred": [
- 156
+ 265
],
"succ": [
- 161
+ 269
],
"asm": [
"BB4000|mov BX,0x0040",
@@ -521,56 +564,69 @@
]
},
{
- "id": 82,
+ "id": 127,
"entry": "F000:202D",
"term": "F000:202D",
"pred": [
- 68
+ 114
],
"succ": [
- 84
+ 145
],
"asm": [
"FF260430|jmp near word ptr DS:[0x3004]"
]
},
{
- "id": 156,
+ "id": 129,
+ "entry": "F000:202C",
+ "dead": true,
+ "term": "F000:202C",
+ "pred": [
+ 114
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 265,
"entry": "F000:122F",
"term": "F000:122F",
"pred": [
- 148
+ 251
],
"succ": [
- 158
+ 266
],
"asm": [
"E9F22D|jmp near 0x4024"
]
},
{
- "id": 84,
+ "id": 145,
"entry": "F000:0809",
"term": "F000:0809",
"pred": [
- 82
+ 127
],
"succ": [
- 86
+ 146
],
"asm": [
"E90128|jmp near 0x300D"
]
},
{
- "id": 148,
+ "id": 251,
"entry": "F000:4012",
"term": "F000:4022",
"pred": [
- 146
+ 243
],
"succ": [
- 156
+ 265
],
"asm": [
"B93412|mov CX,0x1234",
@@ -583,14 +639,14 @@
]
},
{
- "id": 86,
+ "id": 146,
"entry": "F000:300D",
"term": "F000:3018",
"pred": [
- 84
+ 145
],
"succ": [
- 92
+ 152
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -601,42 +657,42 @@
]
},
{
- "id": 146,
+ "id": 243,
"entry": "F000:4011",
"term": "F000:4011",
"pred": [
- 143
+ 238
],
"succ": [
- 148
+ 251
],
"asm": [
"9D|popf"
]
},
{
- "id": 92,
+ "id": 152,
"entry": "F000:3019",
"term": "F000:3019",
"pred": [
- 86
+ 146
],
"succ": [
- 94
+ 161
],
"asm": [
"9D|popf"
]
},
{
- "id": 143,
+ "id": 238,
"entry": "F000:400D",
"term": "F000:4010",
"pred": [
- 139
+ 235
],
"succ": [
- 146
+ 243
],
"asm": [
"BB4000|mov BX,0x0040",
@@ -644,14 +700,15 @@
]
},
{
- "id": 94,
+ "id": 161,
"entry": "F000:301A",
"term": "F000:3028",
"pred": [
- 92
+ 152
],
"succ": [
- 102
+ 168,
+ 170
],
"asm": [
"F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
@@ -664,14 +721,15 @@
]
},
{
- "id": 139,
+ "id": 235,
"entry": "F000:4005",
"term": "F000:400A",
"pred": [
- 137
+ 230
],
"succ": [
- 143
+ 238,
+ 240
],
"asm": [
"B90400|mov CX,4",
@@ -680,56 +738,82 @@
]
},
{
- "id": 102,
+ "id": 168,
"entry": "F000:302B",
"term": "F000:302B",
"pred": [
- 94
+ 161
],
"succ": [
- 104
+ 180
],
"asm": [
"FF260830|jmp near word ptr DS:[0x3008]"
]
},
{
- "id": 137,
+ "id": 170,
+ "entry": "F000:302A",
+ "dead": true,
+ "term": "F000:302A",
+ "pred": [
+ 161
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 240,
+ "entry": "F000:400C",
+ "dead": true,
+ "term": "F000:400C",
+ "pred": [
+ 235
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 230,
"entry": "F000:4004",
"term": "F000:4004",
"pred": [
- 134
+ 228
],
"succ": [
- 139
+ 235
],
"asm": [
"9D|popf"
]
},
{
- "id": 104,
+ "id": 180,
"entry": "F000:0607",
"term": "F000:0607",
"pred": [
- 102
+ 168
],
"succ": [
- 106
+ 181
],
"asm": [
"E9252A|jmp near 0x302F"
]
},
{
- "id": 134,
+ "id": 228,
"entry": "F000:4000",
"term": "F000:4003",
"pred": [
- 127
+ 214
],
"succ": [
- 137
+ 230
],
"asm": [
"BB0000|mov BX,0",
@@ -737,14 +821,14 @@
]
},
{
- "id": 106,
+ "id": 181,
"entry": "F000:302F",
"term": "F000:3039",
"pred": [
- 104
+ 180
],
"succ": [
- 112
+ 193
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -755,14 +839,14 @@
]
},
{
- "id": 127,
+ "id": 214,
"entry": "F000:304D",
"term": "F000:305A",
"pred": [
- 125
+ 213
],
"succ": [
- 134
+ 228
],
"asm": [
"B90200|mov CX,2",
@@ -774,42 +858,42 @@
]
},
{
- "id": 112,
+ "id": 193,
"entry": "F000:F003",
"term": "F000:F003",
"pred": [
- 106
+ 181
],
"succ": [
- 114
+ 194
],
"asm": [
"E93640|jmp near 0x303C"
]
},
{
- "id": 125,
+ "id": 213,
"entry": "F000:0102",
"term": "F000:0102",
"pred": [
- 119
+ 203
],
"succ": [
- 127
+ 214
],
"asm": [
"E9482F|jmp near 0x304D"
]
},
{
- "id": 114,
+ "id": 194,
"entry": "F000:303C",
"term": "F000:303F",
"pred": [
- 112
+ 193
],
"succ": [
- 117
+ 197
],
"asm": [
"BB4000|mov BX,0x0040",
@@ -817,14 +901,14 @@
]
},
{
- "id": 119,
+ "id": 203,
"entry": "F000:3041",
"term": "F000:304B",
"pred": [
- 117
+ 197
],
"succ": [
- 125
+ 213
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -835,14 +919,14 @@
]
},
{
- "id": 117,
+ "id": 197,
"entry": "F000:3040",
"term": "F000:3040",
"pred": [
- 114
+ 194
],
"succ": [
- 119
+ 203
],
"asm": [
"9D|popf"
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/segpr.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/segpr.json
index 1977b212f2..efb8c310b6 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/segpr.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/segpr.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:121F",
- "lastExecutedBlockId": 46,
+ "lastExecutedBlockId": 86,
"blocks": [
{
- "id": 46,
+ "id": 86,
"entry": "F000:1200",
"term": "F000:121F",
"pred": [
- 40
+ 72
],
"succ": [],
"asm": [
@@ -45,15 +45,15 @@
]
},
{
- "id": 40,
+ "id": 72,
"entry": "F000:0065",
"term": "F000:0074",
"pred": [
3,
- 32
+ 66
],
"succ": [
- 46
+ 86
],
"asm": [
"83EC06|sub SP,6",
@@ -71,8 +71,8 @@
2
],
"succ": [
- 32,
- 40
+ 66,
+ 72
],
"asm": [
"BB00F1|mov BX,0xF100",
@@ -106,14 +106,14 @@
]
},
{
- "id": 32,
+ "id": 66,
"entry": "F000:1100",
"term": "F000:1111",
"pred": [
3
],
"succ": [
- 40
+ 72
],
"asm": [
"89E6|mov SI,SP",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifycall.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifycall.json
index fd013843c9..805863dcf4 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifycall.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifycall.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0010",
- "lastExecutedBlockId": 24,
+ "lastExecutedBlockId": 39,
"blocks": [
{
- "id": 24,
+ "id": 39,
"entry": "F000:0010",
"term": "F000:0010",
"pred": [
- 26
+ 41
],
"succ": [],
"asm": [
@@ -33,17 +33,17 @@
]
},
{
- "id": 26,
+ "id": 41,
"entry": "F000:0010",
"term": "F000:0010",
"pred": [
12,
- 17,
- 19
+ 14,
+ 31
],
"succ": [
- 14,
- 24
+ 28,
+ 39
],
"asm": [
"selector"
@@ -57,7 +57,7 @@
2
],
"succ": [
- 17
+ 31
],
"asm": [
"B80000|mov AX,0",
@@ -67,15 +67,15 @@
]
},
{
- "id": 14,
+ "id": 28,
"entry": "F000:0010",
"dead": true,
"term": "F000:0015",
"pred": [
- 26
+ 41
],
"succ": [
- 17
+ 31
],
"asm": [
"90|nop",
@@ -91,37 +91,37 @@
9
],
"succ": [
- 26
+ 41
],
"asm": [
"C3|ret near"
]
},
{
- "id": 17,
+ "id": 31,
"entry": "F000:000D",
"term": "F000:000D",
"pred": [
3,
- 14
+ 28
],
"succ": [
9,
- 26
+ 41
],
"asm": [
"E80700|call near 0x0017"
]
},
{
- "id": 19,
+ "id": 14,
"entry": "F000:001E",
"term": "F000:0029",
"pred": [
9
],
"succ": [
- 26
+ 41
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -135,11 +135,11 @@
"entry": "F000:0017",
"term": "F000:001C",
"pred": [
- 17
+ 31
],
"succ": [
12,
- 19
+ 14
],
"asm": [
"803E2B0000|cmp byte ptr DS:[0x002B],0",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyinstructions.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyinstructions.json
index 225f3f98bb..506bfcbf83 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyinstructions.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyinstructions.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:1406",
- "lastExecutedBlockId": 29,
+ "lastExecutedBlockId": 30252,
"blocks": [
{
- "id": 29,
+ "id": 30252,
"entry": "F000:1400",
"term": "F000:1406",
"pred": [
- 22
+ 30245
],
"succ": [],
"asm": [
@@ -37,16 +37,16 @@
]
},
{
- "id": 22,
+ "id": 30245,
"entry": "F000:1400",
"term": "F000:1400",
"pred": [
- 19
+ 30234
],
"succ": [
- 15,
- 23,
- 29
+ 30228,
+ 30239,
+ 30252
],
"asm": [
"selector"
@@ -60,7 +60,7 @@
2
],
"succ": [
- 19
+ 30234
],
"asm": [
"B80000|mov AX,0",
@@ -70,33 +70,30 @@
]
},
{
- "id": 15,
+ "id": 30228,
"entry": "F000:1400",
"dead": true,
- "term": "F000:1407",
+ "term": "F000:1400",
"pred": [
- 22
+ 30245
],
"succ": [
- 19
+ 30231
],
"asm": [
- "B80100|mov AX,1",
- "50|push AX",
- "BE000C|mov SI,0x0C00",
- "EA0B0000F0|jmp far F000:000B"
+ "B80100|mov AX,1"
]
},
{
- "id": 23,
+ "id": 30239,
"entry": "F000:1400",
"dead": true,
"term": "F000:1409",
"pred": [
- 22
+ 30245
],
"succ": [
- 19
+ 30234
],
"asm": [
"BB0100|mov BX,1",
@@ -107,16 +104,16 @@
]
},
{
- "id": 19,
+ "id": 30234,
"entry": "F000:000B",
"term": "F000:001A",
"pred": [
3,
- 15,
- 23
+ 30231,
+ 30239
],
"succ": [
- 22
+ 30245
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -127,6 +124,23 @@
"F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
"E9E313|jmp near 0x1400"
]
+ },
+ {
+ "id": 30231,
+ "entry": "F000:1403",
+ "dead": true,
+ "term": "F000:1407",
+ "pred": [
+ 30228
+ ],
+ "succ": [
+ 30234
+ ],
+ "asm": [
+ "50|push AX",
+ "BE000C|mov SI,0x0C00",
+ "EA0B0000F0|jmp far F000:000B"
+ ]
}
],
"truncated": false
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyje.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyje.json
index 7f763a4d26..4329643d6b 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyje.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyje.json
@@ -6,10 +6,10 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0008",
- "lastExecutedBlockId": 18,
+ "lastExecutedBlockId": 9,
"blocks": [
{
- "id": 18,
+ "id": 9,
"entry": "F000:0008",
"term": "F000:0008",
"pred": [
@@ -39,11 +39,11 @@
"pred": [
3,
7,
- 13
+ 17
],
"succ": [
7,
- 18
+ 9
],
"asm": [
"83F900|cmp CX,0",
@@ -72,8 +72,8 @@
15
],
"succ": [
- 13,
- 15
+ 15,
+ 17
],
"asm": [
"B8____|mov AX,word ptr [0x000F000A]",
@@ -84,7 +84,7 @@
]
},
{
- "id": 13,
+ "id": 17,
"entry": "F000:001B",
"term": "F000:001E",
"pred": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyterminator.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyterminator.json
index c349492c98..5a4e490ab5 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyterminator.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyterminator.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:0021",
- "lastExecutedBlockId": 32,
+ "lastExecutedBlockId": 53,
"blocks": [
{
- "id": 32,
+ "id": 53,
"entry": "F000:0020",
"term": "F000:0021",
"pred": [
- 30
+ 52
],
"succ": [],
"asm": [
@@ -34,14 +34,14 @@
]
},
{
- "id": 30,
+ "id": 52,
"entry": "F000:0019",
"term": "F000:0019",
"pred": [
- 17
+ 35
],
"succ": [
- 32
+ 53
],
"asm": [
"EB05|jmp short 0x0020"
@@ -55,7 +55,7 @@
2
],
"succ": [
- 20
+ 38
],
"asm": [
"B80000|mov AX,0",
@@ -66,17 +66,17 @@
]
},
{
- "id": 17,
+ "id": 35,
"entry": "F000:0014",
"term": "F000:0019",
"pred": [
14,
- 20,
- 22
+ 16,
+ 38
],
"succ": [
- 29,
- 30
+ 52,
+ 56
],
"asm": [
"B84200|mov AX,0x0042",
@@ -85,16 +85,16 @@
]
},
{
- "id": 20,
+ "id": 38,
"entry": "F000:0010",
"term": "F000:0011",
"pred": [
3,
- 29
+ 56
],
"succ": [
11,
- 17
+ 35
],
"asm": [
"50|push AX",
@@ -102,15 +102,16 @@
]
},
{
- "id": 29,
+ "id": 56,
"entry": "F000:0019",
"dead": true,
"term": "F000:0019",
"pred": [
- 17
+ 35
],
"succ": [
- 20
+ 38,
+ 40
],
"asm": [
"75F5|jne short 0x0010"
@@ -124,7 +125,7 @@
11
],
"succ": [
- 17
+ 35
],
"asm": [
"FE064000|inc byte ptr DS:[0x0040]",
@@ -132,14 +133,14 @@
]
},
{
- "id": 22,
+ "id": 16,
"entry": "F000:0029",
"term": "F000:003A",
"pred": [
11
],
"succ": [
- 17
+ 35
],
"asm": [
"B800F0|mov AX,0xF000",
@@ -154,16 +155,31 @@
"entry": "F000:0022",
"term": "F000:0027",
"pred": [
- 20
+ 38
],
"succ": [
14,
- 22
+ 16
],
"asm": [
"803E400000|cmp byte ptr DS:[0x0040],0",
"7412|je short 0x003B"
]
+ },
+ {
+ "id": 40,
+ "entry": "F000:001B",
+ "dead": true,
+ "term": "F000:001F",
+ "pred": [
+ 56
+ ],
+ "succ": [],
+ "asm": [
+ "B8ADDE|mov AX,0xDEAD",
+ "50|push AX",
+ "F4|hlt"
+ ]
}
],
"truncated": false
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyvalue.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyvalue.json
index 6847d132d1..4143645419 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyvalue.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/selfmodifyvalue.json
@@ -37,10 +37,10 @@
"entry": "F000:000D",
"term": "F000:001A",
"pred": [
- 15
+ 16
],
"succ": [
- 15,
+ 16,
18
],
"asm": [
@@ -60,7 +60,7 @@
2
],
"succ": [
- 15
+ 16
],
"asm": [
"B80000|mov AX,0",
@@ -70,7 +70,7 @@
]
},
{
- "id": 15,
+ "id": 16,
"entry": "F000:000B",
"term": "F000:000B",
"pred": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_branch.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_branch.json
new file mode 100644
index 0000000000..36dcfc30e2
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_branch.json
@@ -0,0 +1,88 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:001F",
+ "lastExecutedBlockId": 14,
+ "blocks": [
+ {
+ "id": 14,
+ "entry": "F000:001A",
+ "term": "F000:001F",
+ "pred": [
+ 8,
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204AA|mov byte ptr DS:[0x0402],0xAA",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:0018",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104EE|mov byte ptr DS:[0x0401],0xEE",
+ "EB00|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB07|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_call_entry.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_call_entry.json
new file mode 100644
index 0000000000..e4c58a9bd2
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_call_entry.json
@@ -0,0 +1,100 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:0023",
+ "lastExecutedBlockId": 16,
+ "blocks": [
+ {
+ "id": 16,
+ "entry": "F000:001E",
+ "term": "F000:0023",
+ "pred": [
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204AA|mov byte ptr DS:[0x0402],0xAA",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 16
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB0B|jmp short 0x001E"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:0013",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 12
+ ],
+ "asm": [
+ "E80500|call near 0x001B"
+ ]
+ },
+ {
+ "id": 12,
+ "entry": "F000:001B",
+ "dead": true,
+ "term": "F000:001D",
+ "pred": [
+ 8
+ ],
+ "succ": [],
+ "asm": [
+ "B042|mov AL,0x42",
+ "C3|ret near"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_closure.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_closure.json
new file mode 100644
index 0000000000..068f638702
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_closure.json
@@ -0,0 +1,122 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:0029",
+ "lastExecutedBlockId": 15,
+ "blocks": [
+ {
+ "id": 15,
+ "entry": "F000:0024",
+ "term": "F000:0029",
+ "pred": [
+ 10,
+ 22
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204BB|mov byte ptr DS:[0x0402],0xBB",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB11|jmp short 0x0024"
+ ]
+ },
+ {
+ "id": 22,
+ "entry": "F000:0022",
+ "dead": true,
+ "term": "F000:0022",
+ "pred": [
+ 20
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "EB00|jmp short 0x0024"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ },
+ {
+ "id": 20,
+ "entry": "F000:001B",
+ "dead": true,
+ "term": "F000:0020",
+ "pred": [
+ 8,
+ 20
+ ],
+ "succ": [
+ 20,
+ 22
+ ],
+ "asm": [
+ "FE060104|inc byte ptr DS:[0x0401]",
+ "49|dec CX",
+ "75F9|jne short 0x001B"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:0016",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 20
+ ],
+ "asm": [
+ "B90300|mov CX,3",
+ "C606010400|mov byte ptr DS:[0x0401],0"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_convergence.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_convergence.json
new file mode 100644
index 0000000000..dc1a00ca05
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_convergence.json
@@ -0,0 +1,89 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:0024",
+ "lastExecutedBlockId": 14,
+ "blocks": [
+ {
+ "id": 14,
+ "entry": "F000:001A",
+ "term": "F000:0024",
+ "pred": [
+ 8,
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204CC|mov byte ptr DS:[0x0402],0xCC",
+ "C6060304FF|mov byte ptr DS:[0x0403],0xFF",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:0018",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104BB|mov byte ptr DS:[0x0401],0xBB",
+ "EB00|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104AA|mov byte ptr DS:[0x0401],0xAA",
+ "EB07|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_discard.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_discard.json
new file mode 100644
index 0000000000..36dcfc30e2
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_discard.json
@@ -0,0 +1,88 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:001F",
+ "lastExecutedBlockId": 14,
+ "blocks": [
+ {
+ "id": 14,
+ "entry": "F000:001A",
+ "term": "F000:001F",
+ "pred": [
+ 8,
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204AA|mov byte ptr DS:[0x0402],0xAA",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:0018",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104EE|mov byte ptr DS:[0x0401],0xEE",
+ "EB00|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 14
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB07|jmp short 0x001A"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_invalid_opcode.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_invalid_opcode.json
new file mode 100644
index 0000000000..b8cee5d539
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_invalid_opcode.json
@@ -0,0 +1,70 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:001C",
+ "lastExecutedBlockId": 12,
+ "blocks": [
+ {
+ "id": 12,
+ "entry": "F000:0017",
+ "term": "F000:001C",
+ "pred": [
+ 9
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204EE|mov byte ptr DS:[0x0402],0xEE",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 9,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 12
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB04|jmp short 0x0017"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 9
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_mixed_block.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_mixed_block.json
new file mode 100644
index 0000000000..b381855656
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_mixed_block.json
@@ -0,0 +1,87 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:001D",
+ "lastExecutedBlockId": 15,
+ "blocks": [
+ {
+ "id": 15,
+ "entry": "F000:0018",
+ "term": "F000:001D",
+ "pred": [
+ 8,
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204AA|mov byte ptr DS:[0x0402],0xAA",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "term": "F000:0013",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "dead": true,
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "C6060104EE|mov byte ptr DS:[0x0401],0xEE",
+ "EB05|jmp short 0x0018"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "3C00|cmp AL,0",
+ "7407|je short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_smc_guard.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_smc_guard.json
new file mode 100644
index 0000000000..ec899703b4
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_smc_guard.json
@@ -0,0 +1,89 @@
+{
+ "currentContextDepth": 1,
+ "currentContextEntryPoint": "F000:FFF0",
+ "totalEntryPoints": 1,
+ "entryPointAddresses": [
+ "F000:FFF0"
+ ],
+ "lastExecutedAddress": "F000:0025",
+ "lastExecutedBlockId": 15,
+ "blocks": [
+ {
+ "id": 15,
+ "entry": "F000:0020",
+ "term": "F000:0025",
+ "pred": [
+ 8,
+ 10
+ ],
+ "succ": [],
+ "asm": [
+ "C6060204AA|mov byte ptr DS:[0x0402],0xAA",
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 2,
+ "entry": "F000:FFF0",
+ "term": "F000:FFF0",
+ "pred": [],
+ "succ": [
+ 3
+ ],
+ "asm": [
+ "EB0E|jmp short 0"
+ ]
+ },
+ {
+ "id": 10,
+ "entry": "F000:000C",
+ "term": "F000:0011",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "C6060104DD|mov byte ptr DS:[0x0401],0xDD",
+ "EB0D|jmp short 0x0020"
+ ]
+ },
+ {
+ "id": 8,
+ "entry": "F000:0013",
+ "dead": true,
+ "term": "F000:001E",
+ "pred": [
+ 3
+ ],
+ "succ": [
+ 15
+ ],
+ "asm": [
+ "2EC6061A00EE|mov byte ptr CS:[0x001A],0xEE",
+ "C606010400|mov byte ptr DS:[0x0401],0",
+ "EB00|jmp short 0x0020"
+ ]
+ },
+ {
+ "id": 3,
+ "entry": "F000:0000",
+ "term": "F000:000A",
+ "pred": [
+ 2
+ ],
+ "succ": [
+ 8,
+ 10
+ ],
+ "asm": [
+ "C606000401|mov byte ptr DS:[0x0400],1",
+ "A00005|mov AL,byte ptr DS:[0x0500]",
+ "84C0|and AL,AL",
+ "7507|jne short 0x0013"
+ ]
+ }
+ ],
+ "truncated": false
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/sticli.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/sticli.json
index 3af3b31b17..7b802fc344 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/sticli.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/sticli.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:000C",
- "lastExecutedBlockId": 9,
+ "lastExecutedBlockId": 12,
"blocks": [
{
- "id": 9,
+ "id": 12,
"entry": "F000:0009",
"term": "F000:000C",
"pred": [
- 7
+ 11
],
"succ": [],
"asm": [
@@ -35,14 +35,14 @@
]
},
{
- "id": 7,
+ "id": 11,
"entry": "F000:0006",
"term": "F000:0006",
"pred": [
3
],
"succ": [
- 9
+ 12
],
"asm": [
"B83412|mov AX,0x1234"
@@ -56,7 +56,7 @@
2
],
"succ": [
- 7
+ 11
],
"asm": [
"B80010|mov AX,0x1000",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/strings.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/strings.json
index 7014ee2756..57efa9e98a 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/strings.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/strings.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:D00B",
- "lastExecutedBlockId": 70,
+ "lastExecutedBlockId": 116,
"blocks": [
{
- "id": 70,
+ "id": 116,
"entry": "F000:D000",
"term": "F000:D00B",
"pred": [
- 66
+ 110
],
"succ": [],
"asm": [
@@ -36,14 +36,14 @@
]
},
{
- "id": 66,
+ "id": 110,
"entry": "F000:8013",
"term": "F000:8017",
"pred": [
- 61
+ 95
],
"succ": [
- 70
+ 116
],
"asm": [
"B800D0|mov AX,0xD000",
@@ -59,7 +59,7 @@
2
],
"succ": [
- 12
+ 21
],
"asm": [
"B900F0|mov CX,0xF000",
@@ -73,14 +73,14 @@
]
},
{
- "id": 61,
+ "id": 95,
"entry": "F000:1350",
"term": "F000:1354",
"pred": [
- 56
+ 91
],
"succ": [
- 66
+ 110
],
"asm": [
"B080|mov AL,0x80",
@@ -90,14 +90,14 @@
]
},
{
- "id": 12,
+ "id": 21,
"entry": "F000:0097",
"term": "F000:0099",
"pred": [
3
],
"succ": [
- 16
+ 27
],
"asm": [
"A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
@@ -106,14 +106,15 @@
]
},
{
- "id": 56,
+ "id": 91,
"entry": "F000:1300",
"term": "F000:1305",
"pred": [
- 49
+ 79
],
"succ": [
- 61
+ 95,
+ 97
],
"asm": [
"8905|mov word ptr DS:[DI],AX",
@@ -123,14 +124,14 @@
]
},
{
- "id": 16,
+ "id": 27,
"entry": "F000:0046",
"term": "F000:0048",
"pred": [
- 12
+ 21
],
"succ": [
- 20
+ 33
],
"asm": [
"A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
@@ -139,14 +140,27 @@
]
},
{
- "id": 49,
+ "id": 97,
+ "entry": "F000:1307",
+ "dead": true,
+ "term": "F000:1307",
+ "pred": [
+ 91
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 79,
"entry": "F000:80FF",
"term": "F000:8107",
"pred": [
- 44
+ 71
],
"succ": [
- 56
+ 91
],
"asm": [
"BF0220|mov DI,0x2002",
@@ -158,14 +172,14 @@
]
},
{
- "id": 20,
+ "id": 33,
"entry": "F000:0082",
"term": "F000:0084",
"pred": [
- 16
+ 27
],
"succ": [
- 24
+ 39
],
"asm": [
"A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
@@ -174,14 +188,14 @@
]
},
{
- "id": 44,
+ "id": 71,
"entry": "F000:80C2",
"term": "F000:80C7",
"pred": [
- 36
+ 57
],
"succ": [
- 49
+ 79
],
"asm": [
"8ED9|mov DS,CX",
@@ -191,14 +205,14 @@
]
},
{
- "id": 24,
+ "id": 39,
"entry": "F000:0812",
"term": "F000:0814",
"pred": [
- 20
+ 33
],
"succ": [
- 28
+ 45
],
"asm": [
"A7|cmps word ptr DS:[SI],word ptr ES:[DI]",
@@ -207,14 +221,14 @@
]
},
{
- "id": 36,
+ "id": 57,
"entry": "F000:C200",
"term": "F000:C20E",
"pred": [
- 33
+ 53
],
"succ": [
- 44
+ 71
],
"asm": [
"BA0010|mov DX,0x1000",
@@ -227,14 +241,14 @@
]
},
{
- "id": 28,
+ "id": 45,
"entry": "F000:0883",
"term": "F000:0887",
"pred": [
- 24
+ 39
],
"succ": [
- 33
+ 53
],
"asm": [
"B410|mov AH,0x10",
@@ -244,14 +258,14 @@
]
},
{
- "id": 33,
+ "id": 53,
"entry": "F000:10C2",
"term": "F000:10C3",
"pred": [
- 28
+ 45
],
"succ": [
- 36
+ 57
],
"asm": [
"AD|lods AX,word ptr DS:[SI]",
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/test386.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/test386.json
index fcbf76ed8f..56b66b31e0 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/test386.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/test386.json
@@ -6,14 +6,14 @@
"F000:FFF0"
],
"lastExecutedAddress": "F000:15C4",
- "lastExecutedBlockId": 1665,
+ "lastExecutedBlockId": 3204,
"blocks": [
{
- "id": 1665,
+ "id": 3204,
"entry": "F000:15C3",
"term": "F000:15C4",
"pred": [
- 1655
+ 3194
],
"succ": [],
"asm": [
@@ -34,14 +34,14 @@
]
},
{
- "id": 1655,
+ "id": 3194,
"entry": "F000:15AF",
"term": "F000:15C2",
"pred": [
- 1652
+ 3190
],
"succ": [
- 1665
+ 3204
],
"asm": [
"8EC2|mov ES,DX",
@@ -63,7 +63,7 @@
2
],
"succ": [
- 14
+ 15
],
"asm": [
"FA|cli",
@@ -76,14 +76,15 @@
]
},
{
- "id": 1652,
+ "id": 3190,
"entry": "F000:15A6",
"term": "F000:15AD",
"pred": [
- 1644
+ 3181
],
"succ": [
- 1655
+ 386,
+ 3194
],
"asm": [
"6681FB78563412|cmp EBX,0x12345678",
@@ -91,16 +92,16 @@
]
},
{
- "id": 14,
+ "id": 15,
"entry": "F000:0054",
"term": "F000:0069",
"pred": [
3,
- 14
+ 15
],
"succ": [
- 14,
- 16
+ 15,
+ 17
],
"asm": [
"67C7048500000000C715|mov word ptr DS:[4*EAX],0x15C7",
@@ -110,14 +111,263 @@
]
},
{
- "id": 1644,
+ "id": 386,
+ "entry": "F000:15C7",
+ "dead": true,
+ "term": "F000:15C9",
+ "pred": [
+ 383,
+ 388,
+ 398,
+ 407,
+ 411,
+ 423,
+ 427,
+ 439,
+ 443,
+ 455,
+ 459,
+ 471,
+ 475,
+ 487,
+ 491,
+ 503,
+ 507,
+ 511,
+ 525,
+ 536,
+ 542,
+ 548,
+ 557,
+ 565,
+ 571,
+ 577,
+ 587,
+ 595,
+ 601,
+ 607,
+ 616,
+ 624,
+ 630,
+ 636,
+ 645,
+ 653,
+ 659,
+ 665,
+ 1055,
+ 1058,
+ 1063,
+ 1073,
+ 1079,
+ 1125,
+ 1128,
+ 1133,
+ 1155,
+ 1159,
+ 1168,
+ 1172,
+ 1176,
+ 1180,
+ 1188,
+ 1192,
+ 1204,
+ 1208,
+ 1212,
+ 1221,
+ 1225,
+ 1236,
+ 1240,
+ 1249,
+ 1253,
+ 1257,
+ 1261,
+ 1269,
+ 1273,
+ 1285,
+ 1289,
+ 1293,
+ 1302,
+ 1306,
+ 1317,
+ 1321,
+ 1330,
+ 1334,
+ 1338,
+ 1342,
+ 1350,
+ 1354,
+ 1366,
+ 1370,
+ 1374,
+ 1383,
+ 1387,
+ 1398,
+ 1402,
+ 1411,
+ 1415,
+ 1419,
+ 1423,
+ 1431,
+ 1435,
+ 1447,
+ 1451,
+ 1455,
+ 1464,
+ 1468,
+ 1479,
+ 1483,
+ 1492,
+ 1496,
+ 1500,
+ 1504,
+ 1512,
+ 1516,
+ 1528,
+ 1532,
+ 1536,
+ 1545,
+ 1549,
+ 1560,
+ 1564,
+ 1573,
+ 1577,
+ 1581,
+ 1585,
+ 1593,
+ 1597,
+ 1609,
+ 1613,
+ 1617,
+ 1626,
+ 1630,
+ 1643,
+ 1647,
+ 1663,
+ 1667,
+ 1671,
+ 1679,
+ 1683,
+ 1694,
+ 1698,
+ 1702,
+ 1710,
+ 1714,
+ 1718,
+ 1731,
+ 1735,
+ 1751,
+ 1755,
+ 1759,
+ 1767,
+ 1771,
+ 1782,
+ 1786,
+ 1790,
+ 1798,
+ 1802,
+ 1806,
+ 1819,
+ 1823,
+ 1839,
+ 1843,
+ 1847,
+ 1855,
+ 1859,
+ 1870,
+ 1874,
+ 1878,
+ 1886,
+ 1890,
+ 1894,
+ 1907,
+ 1911,
+ 1927,
+ 1931,
+ 1935,
+ 1943,
+ 1947,
+ 1958,
+ 1962,
+ 1966,
+ 1974,
+ 1978,
+ 1982,
+ 1995,
+ 1999,
+ 2015,
+ 2019,
+ 2023,
+ 2031,
+ 2035,
+ 2046,
+ 2050,
+ 2054,
+ 2062,
+ 2066,
+ 2070,
+ 2083,
+ 2087,
+ 2103,
+ 2107,
+ 2111,
+ 2119,
+ 2123,
+ 2134,
+ 2138,
+ 2142,
+ 2150,
+ 2154,
+ 2170,
+ 2914,
+ 2921,
+ 2940,
+ 2953,
+ 2963,
+ 2969,
+ 2988,
+ 2995,
+ 3014,
+ 3029,
+ 3041,
+ 3043,
+ 3061,
+ 3065,
+ 3074,
+ 3078,
+ 3090,
+ 3094,
+ 3103,
+ 3107,
+ 3119,
+ 3123,
+ 3132,
+ 3136,
+ 3148,
+ 3152,
+ 3161,
+ 3165,
+ 3177,
+ 3181,
+ 3190
+ ],
+ "succ": [
+ 396
+ ],
+ "asm": [
+ "8CC8|mov AX,CS",
+ "A90700|test AX,7"
+ ]
+ },
+ {
+ "id": 3181,
"entry": "F000:158A",
"term": "F000:15A4",
"pred": [
- 1641
+ 3177
],
"succ": [
- 1652
+ 386,
+ 3190
],
"asm": [
"8EC2|mov ES,DX",
@@ -130,14 +380,15 @@
]
},
{
- "id": 16,
+ "id": 17,
"entry": "F000:006B",
"term": "F000:0086",
"pred": [
- 14
+ 15
],
"succ": [
- 30
+ 31,
+ 33
],
"asm": [
"B80010|mov AX,0x1000",
@@ -156,2607 +407,2940 @@
]
},
{
- "id": 1641,
- "entry": "F000:1584",
- "term": "F000:1588",
+ "id": 396,
+ "entry": "F000:15CC",
+ "dead": true,
+ "term": "F000:15CC",
"pred": [
- 1630
+ 386,
+ 396
],
"succ": [
- 1644
+ 396,
+ 398
],
"asm": [
- "81FB3412|cmp BX,0x1234",
- "753D|jne short 0x15C7"
+ "75FE|jne short 0x15CC"
]
},
{
- "id": 30,
- "entry": "F000:0088",
- "term": "F000:0088",
+ "id": 383,
+ "entry": "F000:039F",
+ "term": "F000:03A5",
"pred": [
- 16
+ 381
],
"succ": [
- 32
+ 386,
+ 388
],
"asm": [
- "7223|jb short 0x00AD"
+ "663D00000100|cmp EAX,0x00010000",
+ "0F851E12|jne near 0x15C7"
]
},
{
- "id": 1630,
- "entry": "F000:1566",
- "term": "F000:1582",
+ "id": 388,
+ "entry": "F000:03A9",
+ "term": "F000:03B0",
"pred": [
- 1627
+ 383
],
"succ": [
- 1641
+ 386,
+ 394
],
"asm": [
- "8EC2|mov ES,DX",
- "8EE1|mov FS,CX",
- "8CE9|mov CX,GS",
- "8CC2|mov DX,ES",
- "26C7053412|mov word ptr ES:[DI],0x1234",
- "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
- "260FB51D|lgs BX,word ptr ES:[DI]",
- "8CE8|mov AX,GS",
- "3DCDAB|cmp AX,0xABCD",
- "7543|jne short 0x15C7"
+ "6681F900000200|cmp ECX,0x00020000",
+ "0F851312|jne near 0x15C7"
]
},
{
- "id": 32,
- "entry": "F000:00AD",
- "term": "F000:00AD",
+ "id": 398,
+ "entry": "F000:15CE",
+ "dead": true,
+ "term": "F000:15CF",
"pred": [
- 30
+ 396
],
"succ": [
- 34
+ 386
],
"asm": [
- "72DC|jb short 0x008B"
+ "FA|cli",
+ "EBF6|jmp short 0x15C7"
]
},
{
- "id": 1627,
- "entry": "F000:155D",
- "term": "F000:1564",
+ "id": 407,
+ "entry": "F000:03C5",
+ "term": "F000:03CB",
"pred": [
- 1619
+ 405
],
"succ": [
- 1630
+ 386,
+ 411
],
"asm": [
- "6681FB78563412|cmp EBX,0x12345678",
- "7561|jne short 0x15C7"
+ "663D00000200|cmp EAX,0x00020000",
+ "0F85F811|jne near 0x15C7"
]
},
{
- "id": 34,
- "entry": "F000:008B",
- "term": "F000:008E",
+ "id": 411,
+ "entry": "F000:03CF",
+ "term": "F000:03D3",
"pred": [
- 32
+ 407
],
"succ": [
- 38
+ 386,
+ 415
],
"asm": [
- "B440|mov AH,0x40",
- "9E|sahf",
- "751C|jne short 0x00AC"
+ "6683F900|cmp ECX,0",
+ "0F85F011|jne near 0x15C7"
]
},
{
- "id": 1619,
- "entry": "F000:1541",
- "term": "F000:155B",
+ "id": 423,
+ "entry": "F000:03E3",
+ "term": "F000:03E6",
"pred": [
- 1616
+ 421
],
"succ": [
- 1627
+ 386,
+ 427
],
"asm": [
- "8EC2|mov ES,DX",
- "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
- "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
- "26660FB41D|lfs EBX,dword ptr ES:[DI]",
- "8CE0|mov AX,FS",
- "3DDEBC|cmp AX,0xBCDE",
- "756A|jne short 0x15C7"
+ "3D0001|cmp AX,0x0100",
+ "0F85DD11|jne near 0x15C7"
]
},
{
- "id": 38,
- "entry": "F000:0090",
- "term": "F000:0090",
+ "id": 427,
+ "entry": "F000:03EA",
+ "term": "F000:03EE",
"pred": [
- 34
+ 423
],
"succ": [
- 40
+ 386,
+ 431
],
"asm": [
- "741D|je short 0x00AF"
+ "81F9FFFE|cmp CX,0xFEFF",
+ "0F85D511|jne near 0x15C7"
]
},
{
- "id": 1616,
- "entry": "F000:1539",
- "term": "F000:153D",
+ "id": 439,
+ "entry": "F000:03FE",
+ "term": "F000:0401",
"pred": [
- 1605
+ 437
],
"succ": [
- 1619
+ 386,
+ 443
],
"asm": [
- "81FB3412|cmp BX,0x1234",
- "0F858600|jne near 0x15C7"
+ "3DFF00|cmp AX,0x00FF",
+ "0F85C211|jne near 0x15C7"
]
},
{
- "id": 40,
- "entry": "F000:00AF",
- "term": "F000:00AF",
+ "id": 443,
+ "entry": "F000:0405",
+ "term": "F000:0408",
"pred": [
- 38
+ 439
],
"succ": [
- 42
+ 386,
+ 447
],
"asm": [
- "74E2|je short 0x0093"
+ "83F900|cmp CX,0",
+ "0F85BB11|jne near 0x15C7"
]
},
{
- "id": 1605,
- "entry": "F000:1519",
- "term": "F000:1535",
+ "id": 455,
+ "entry": "F000:0423",
+ "term": "F000:0429",
"pred": [
- 1602
+ 453
],
"succ": [
- 1616
+ 386,
+ 459
],
"asm": [
- "8EC2|mov ES,DX",
- "8EC1|mov ES,CX",
- "8CE1|mov CX,FS",
- "8CC2|mov DX,ES",
- "26C7053412|mov word ptr ES:[DI],0x1234",
- "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
- "260FB41D|lfs BX,word ptr ES:[DI]",
- "8CE0|mov AX,FS",
- "3DCDAB|cmp AX,0xABCD",
- "0F858E00|jne near 0x15C7"
+ "663D00000100|cmp EAX,0x00010000",
+ "0F859A11|jne near 0x15C7"
]
},
{
- "id": 42,
- "entry": "F000:0093",
- "term": "F000:0096",
+ "id": 459,
+ "entry": "F000:042D",
+ "term": "F000:0434",
"pred": [
- 40
+ 455
],
"succ": [
- 46
+ 386,
+ 463
],
"asm": [
- "B404|mov AH,4",
- "9E|sahf",
- "7B14|jnp short 0x00AC"
+ "6681F900000100|cmp ECX,0x00010000",
+ "0F858F11|jne near 0x15C7"
]
},
{
- "id": 1602,
- "entry": "F000:150E",
- "term": "F000:1515",
+ "id": 471,
+ "entry": "F000:0443",
+ "term": "F000:0446",
"pred": [
- 1594
+ 469
],
"succ": [
- 1605
+ 386,
+ 475
],
"asm": [
- "6681FB78563412|cmp EBX,0x12345678",
- "0F85AE00|jne near 0x15C7"
+ "3D0001|cmp AX,0x0100",
+ "0F857D11|jne near 0x15C7"
]
},
{
- "id": 46,
- "entry": "F000:0098",
- "term": "F000:0098",
+ "id": 475,
+ "entry": "F000:044A",
+ "term": "F000:044E",
"pred": [
- 42
+ 471
],
"succ": [
- 48
+ 386,
+ 479
],
"asm": [
- "7A17|jp short 0x00B1"
+ "81F9FFFE|cmp CX,0xFEFF",
+ "0F857511|jne near 0x15C7"
]
},
{
- "id": 1594,
- "entry": "F000:14F1",
- "term": "F000:150A",
+ "id": 487,
+ "entry": "F000:045D",
+ "term": "F000:0460",
"pred": [
- 1591
+ 485
],
"succ": [
- 1602
+ 386,
+ 491
],
"asm": [
- "8EC2|mov ES,DX",
- "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
- "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
- "2666C41D|les EBX,dword ptr ES:[DI]",
- "8CC0|mov AX,ES",
- "3DDEBC|cmp AX,0xBCDE",
- "0F85B900|jne near 0x15C7"
+ "3DFF00|cmp AX,0x00FF",
+ "0F856311|jne near 0x15C7"
]
},
{
- "id": 48,
- "entry": "F000:00B1",
- "term": "F000:00B1",
+ "id": 491,
+ "entry": "F000:0464",
+ "term": "F000:0467",
"pred": [
- 46
+ 487
],
"succ": [
- 50
+ 386,
+ 495
],
"asm": [
- "7AE8|jp short 0x009B"
+ "83F900|cmp CX,0",
+ "0F855C11|jne near 0x15C7"
]
},
{
- "id": 1591,
- "entry": "F000:14E9",
- "term": "F000:14ED",
+ "id": 503,
+ "entry": "F000:0482",
+ "term": "F000:0488",
"pred": [
- 1580
+ 501
],
"succ": [
- 1594
+ 386,
+ 507
],
"asm": [
- "81FB3412|cmp BX,0x1234",
- "0F85D600|jne near 0x15C7"
+ "663D00000100|cmp EAX,0x00010000",
+ "0F853B11|jne near 0x15C7"
]
},
{
- "id": 50,
- "entry": "F000:009B",
- "term": "F000:009E",
+ "id": 507,
+ "entry": "F000:048C",
+ "term": "F000:0490",
"pred": [
- 48
+ 503
],
"succ": [
- 54
+ 386,
+ 511
],
"asm": [
- "B480|mov AH,0x80",
- "9E|sahf",
- "790C|jns short 0x00AC"
+ "6683F900|cmp ECX,0",
+ "0F853311|jne near 0x15C7"
]
},
{
- "id": 1580,
- "entry": "F000:14CA",
- "term": "F000:14E5",
+ "id": 511,
+ "entry": "F000:0494",
+ "term": "F000:04BB",
"pred": [
- 1577
+ 507
],
"succ": [
- 1591
+ 386,
+ 525
],
"asm": [
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "8CC1|mov CX,ES",
- "8CC2|mov DX,ES",
- "26C7053412|mov word ptr ES:[DI],0x1234",
- "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
- "26C41D|les BX,word ptr ES:[DI]",
- "8CC0|mov AX,ES",
- "3DCDAB|cmp AX,0xABCD",
- "0F85DE00|jne near 0x15C7"
+ "B002|mov AL,2",
+ "BA9909|mov DX,0x0999",
+ "EE|out DX,AL",
+ "66B801000080|mov EAX,0x80000001",
+ "66F7E8|imul EAX",
+ "66B811223344|mov EAX,0x44332211",
+ "6689C3|mov EBX,EAX",
+ "66B955667788|mov ECX,0x88776655",
+ "66F7E1|mul ECX",
+ "66F7F1|div ECX",
+ "6639D8|cmp EAX,EBX",
+ "0F850811|jne near 0x15C7"
]
},
{
- "id": 54,
- "entry": "F000:00A0",
- "term": "F000:00A0",
+ "id": 525,
+ "entry": "F000:04BF",
+ "term": "F000:04D0",
"pred": [
- 50
+ 511
],
"succ": [
- 56
+ 386,
+ 536
],
"asm": [
- "7811|js short 0x00B3"
+ "B003|mov AL,3",
+ "BA9909|mov DX,0x0999",
+ "EE|out DX,AL",
+ "BA0020|mov DX,0x2000",
+ "8ED2|mov SS,DX",
+ "31C0|xor AX,AX",
+ "8CD0|mov AX,SS",
+ "39D0|cmp AX,DX",
+ "0F85F310|jne near 0x15C7"
]
},
{
- "id": 1577,
- "entry": "F000:14BF",
- "term": "F000:14C6",
+ "id": 536,
+ "entry": "F000:04D4",
+ "term": "F000:04DF",
"pred": [
- 1569
+ 525
],
"succ": [
- 1580
+ 386,
+ 542
],
"asm": [
- "6681FB78563412|cmp EBX,0x12345678",
- "0F85FD00|jne near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CD0|mov EAX,SS",
+ "39D0|cmp AX,DX",
+ "0F85E410|jne near 0x15C7"
]
},
{
- "id": 56,
- "entry": "F000:00B3",
- "term": "F000:00B3",
+ "id": 542,
+ "entry": "F000:04E3",
+ "term": "F000:04F1",
"pred": [
- 54
+ 536
],
"succ": [
- 58
+ 386,
+ 548
],
"asm": [
- "78EE|js short 0x00A3"
- ]
- },
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C160000|mov word ptr DS:[0],SS",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F85D210|jne near 0x15C7"
+ ]
+ },
{
- "id": 1569,
- "entry": "F000:14A2",
- "term": "F000:14BB",
+ "id": 548,
+ "entry": "F000:04F5",
+ "term": "F000:0503",
"pred": [
- 1566
+ 542
],
"succ": [
- 1577
+ 386,
+ 557
],
"asm": [
- "8EC2|mov ES,DX",
- "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
- "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
- "2666C51D|lds EBX,dword ptr ES:[DI]",
- "8CD8|mov AX,DS",
- "3DDEBC|cmp AX,0xBCDE",
- "0F850801|jne near 0x15C7"
+ "8CD9|mov CX,DS",
+ "31C0|xor AX,AX",
+ "8ED0|mov SS,AX",
+ "8E160000|mov SS,word ptr DS:[0]",
+ "8CD0|mov AX,SS",
+ "39D0|cmp AX,DX",
+ "0F85C010|jne near 0x15C7"
]
},
{
- "id": 58,
- "entry": "F000:00A3",
- "term": "F000:00A6",
+ "id": 557,
+ "entry": "F000:0507",
+ "term": "F000:0512",
"pred": [
- 56
+ 548
],
"succ": [
- 62
+ 386,
+ 565
],
"asm": [
- "B441|mov AH,0x41",
- "9E|sahf",
- "7704|ja short 0x00AC"
+ "BA0020|mov DX,0x2000",
+ "8EDA|mov DS,DX",
+ "31C0|xor AX,AX",
+ "8CD8|mov AX,DS",
+ "39D0|cmp AX,DX",
+ "0F85B110|jne near 0x15C7"
]
},
{
- "id": 1566,
- "entry": "F000:149A",
- "term": "F000:149E",
+ "id": 565,
+ "entry": "F000:0516",
+ "term": "F000:0521",
"pred": [
- 1555
+ 557
],
"succ": [
- 1569
+ 386,
+ 571
],
"asm": [
- "81FB3412|cmp BX,0x1234",
- "0F852501|jne near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CD8|mov EAX,DS",
+ "39D0|cmp AX,DX",
+ "0F85A210|jne near 0x15C7"
]
},
{
- "id": 62,
- "entry": "F000:00A8",
- "term": "F000:00A8",
+ "id": 571,
+ "entry": "F000:0525",
+ "term": "F000:0533",
"pred": [
- 58
+ 565
],
"succ": [
- 64
+ 386,
+ 577
],
"asm": [
- "760B|jbe short 0x00B5"
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C1E0000|mov word ptr DS:[0],DS",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F859010|jne near 0x15C7"
]
},
{
- "id": 1555,
- "entry": "F000:147B",
- "term": "F000:1496",
+ "id": 577,
+ "entry": "F000:0537",
+ "term": "F000:0548",
"pred": [
- 1552
+ 571
],
"succ": [
- 1566
+ 386,
+ 587
],
"asm": [
- "8EC2|mov ES,DX",
- "8ED1|mov SS,CX",
"8CD9|mov CX,DS",
- "8CC2|mov DX,ES",
- "26C7053412|mov word ptr ES:[DI],0x1234",
- "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
- "26C51D|lds BX,word ptr ES:[DI]",
+ "31C0|xor AX,AX",
+ "8ED8|mov DS,AX",
+ "8EC1|mov ES,CX",
+ "268E1E0000|mov DS,word ptr ES:[0]",
"8CD8|mov AX,DS",
- "3DCDAB|cmp AX,0xABCD",
- "0F852D01|jne near 0x15C7"
+ "39D0|cmp AX,DX",
+ "0F857B10|jne near 0x15C7"
]
},
{
- "id": 64,
- "entry": "F000:00B5",
- "term": "F000:00B5",
+ "id": 587,
+ "entry": "F000:054C",
+ "term": "F000:0557",
"pred": [
- 62
+ 577
],
"succ": [
- 66
+ 386,
+ 595
],
"asm": [
- "76F3|jbe short 0x00AA"
+ "BA0020|mov DX,0x2000",
+ "8EC2|mov ES,DX",
+ "31C0|xor AX,AX",
+ "8CC0|mov AX,ES",
+ "39D0|cmp AX,DX",
+ "0F856C10|jne near 0x15C7"
]
},
{
- "id": 1552,
- "entry": "F000:1470",
- "term": "F000:1477",
+ "id": 595,
+ "entry": "F000:055B",
+ "term": "F000:0566",
"pred": [
- 1544
+ 587
],
"succ": [
- 1555
+ 386,
+ 601
],
"asm": [
- "6681FB78563412|cmp EBX,0x12345678",
- "0F854C01|jne near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CC0|mov EAX,ES",
+ "39D0|cmp AX,DX",
+ "0F855D10|jne near 0x15C7"
]
},
{
- "id": 66,
- "entry": "F000:00AA",
- "term": "F000:00AA",
+ "id": 601,
+ "entry": "F000:056A",
+ "term": "F000:0578",
"pred": [
- 64
+ 595
],
"succ": [
- 68
+ 386,
+ 607
],
"asm": [
- "EB0B|jmp short 0x00B7"
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C060000|mov word ptr DS:[0],ES",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F854B10|jne near 0x15C7"
]
},
{
- "id": 1544,
- "entry": "F000:1452",
- "term": "F000:146C",
+ "id": 607,
+ "entry": "F000:057C",
+ "term": "F000:058A",
"pred": [
- 1541
+ 601
],
"succ": [
- 1552
+ 386,
+ 616
],
"asm": [
- "8EC2|mov ES,DX",
- "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
- "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
- "26660FB21D|lss EBX,dword ptr ES:[DI]",
- "8CD0|mov AX,SS",
- "3DDEBC|cmp AX,0xBCDE",
- "0F855701|jne near 0x15C7"
+ "8CD9|mov CX,DS",
+ "31C0|xor AX,AX",
+ "8EC0|mov ES,AX",
+ "8E060000|mov ES,word ptr DS:[0]",
+ "8CC0|mov AX,ES",
+ "39D0|cmp AX,DX",
+ "0F853910|jne near 0x15C7"
]
},
{
- "id": 68,
- "entry": "F000:00B7",
- "term": "F000:00BE",
+ "id": 616,
+ "entry": "F000:058E",
+ "term": "F000:0599",
"pred": [
- 66
+ 607
],
"succ": [
- 74
+ 386,
+ 624
],
"asm": [
- "B4D4|mov AH,0xD4",
- "9E|sahf",
- "B80000|mov AX,0",
- "9E|sahf",
- "731B|jae short 0x00DB"
+ "BA0020|mov DX,0x2000",
+ "8EE2|mov FS,DX",
+ "31C0|xor AX,AX",
+ "8CE0|mov AX,FS",
+ "39D0|cmp AX,DX",
+ "0F852A10|jne near 0x15C7"
]
},
{
- "id": 1541,
- "entry": "F000:144A",
- "term": "F000:144E",
+ "id": 624,
+ "entry": "F000:059D",
+ "term": "F000:05A8",
"pred": [
- 1524
+ 616
],
"succ": [
- 1544
+ 386,
+ 630
],
"asm": [
- "81FB3412|cmp BX,0x1234",
- "0F857501|jne near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CE0|mov EAX,FS",
+ "39D0|cmp AX,DX",
+ "0F851B10|jne near 0x15C7"
]
},
{
- "id": 74,
- "entry": "F000:00DB",
- "term": "F000:00DB",
+ "id": 630,
+ "entry": "F000:05AC",
+ "term": "F000:05BA",
"pred": [
- 68
+ 624
],
"succ": [
- 76
+ 386,
+ 636
],
"asm": [
- "73E4|jae short 0x00C1"
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C260000|mov word ptr DS:[0],FS",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F850910|jne near 0x15C7"
]
},
{
- "id": 1524,
- "entry": "F000:141B",
- "term": "F000:1446",
+ "id": 636,
+ "entry": "F000:05BE",
+ "term": "F000:05CC",
"pred": [
- 1522
+ 630
],
"succ": [
- 1541
+ 386,
+ 645
],
"asm": [
- "BA0023|mov DX,0x2300",
- "8EDA|mov DS,DX",
- "BA0063|mov DX,0x6300",
- "8EC2|mov ES,DX",
- "B006|mov AL,6",
- "BA9909|mov DX,0x0999",
- "EE|out DX,AL",
- "BF0000|mov DI,0",
- "8CD1|mov CX,SS",
- "8CC2|mov DX,ES",
- "26C7053412|mov word ptr ES:[DI],0x1234",
- "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
- "260FB21D|lss BX,word ptr ES:[DI]",
- "8CD0|mov AX,SS",
- "3DCDAB|cmp AX,0xABCD",
- "0F857D01|jne near 0x15C7"
+ "8CD9|mov CX,DS",
+ "31C0|xor AX,AX",
+ "8EE0|mov FS,AX",
+ "8E260000|mov FS,word ptr DS:[0]",
+ "8CE0|mov AX,FS",
+ "39D0|cmp AX,DX",
+ "0F85F70F|jne near 0x15C7"
]
},
{
- "id": 76,
- "entry": "F000:00C1",
- "term": "F000:00C4",
+ "id": 645,
+ "entry": "F000:05D0",
+ "term": "F000:05DB",
"pred": [
- 74
+ 636
],
"succ": [
- 80
+ 386,
+ 653
],
"asm": [
- "B495|mov AH,0x95",
- "9E|sahf",
- "7517|jne short 0x00DD"
+ "BA0020|mov DX,0x2000",
+ "8EEA|mov GS,DX",
+ "31C0|xor AX,AX",
+ "8CE8|mov AX,GS",
+ "39D0|cmp AX,DX",
+ "0F85E80F|jne near 0x15C7"
]
},
{
- "id": 1522,
- "entry": "F000:1417",
- "term": "F000:1417",
+ "id": 653,
+ "entry": "F000:05DF",
+ "term": "F000:05EA",
"pred": [
- 1502,
- 1517
+ 645
],
"succ": [
- 1524
+ 386,
+ 659
],
"asm": [
- "0F83AC01|jae near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CE8|mov EAX,GS",
+ "39D0|cmp AX,DX",
+ "0F85D90F|jne near 0x15C7"
]
},
{
- "id": 80,
- "entry": "F000:00DD",
- "term": "F000:00DD",
+ "id": 659,
+ "entry": "F000:05EE",
+ "term": "F000:05FC",
"pred": [
- 76
+ 653
],
"succ": [
- 82
+ 386,
+ 665
],
"asm": [
- "75E8|jne short 0x00C7"
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C2E0000|mov word ptr DS:[0],GS",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F85C70F|jne near 0x15C7"
]
},
{
- "id": 1502,
- "entry": "F000:13EE",
- "term": "F000:13F2",
+ "id": 665,
+ "entry": "F000:0600",
+ "term": "F000:060E",
"pred": [
- 1498
+ 659
],
"succ": [
- 1506,
- 1522
+ 386,
+ 674
],
"asm": [
- "83C008|add AX,8",
- "F9|stc",
- "66CB|ret far"
+ "8CD9|mov CX,DS",
+ "31C0|xor AX,AX",
+ "8EE8|mov GS,AX",
+ "8E2E0000|mov GS,word ptr DS:[0]",
+ "8CE8|mov AX,GS",
+ "39D0|cmp AX,DX",
+ "0F85B50F|jne near 0x15C7"
]
},
{
- "id": 1517,
- "entry": "F000:1407",
- "term": "F000:1414",
+ "id": 1055,
+ "entry": "F000:0633",
+ "term": "F000:0636",
"pred": [
- 1515
+ 674
],
"succ": [
- 1498,
- 1522
+ 386,
+ 1058
],
"asm": [
- "F8|clc",
- "66C704E5130000|mov dword ptr DS:[SI],0x000013E5",
- "C7440400F0|mov word ptr DS:[SI\u002B4],0xF000",
- "66FF1C|call far dword ptr DS:[SI]"
- ]
+ "83FCF9|cmp SP,-7",
+ "0F858D0F|jne near 0x15C7"
+ ]
},
{
- "id": 82,
- "entry": "F000:00C7",
- "term": "F000:00CA",
+ "id": 1058,
+ "entry": "F000:063A",
+ "term": "F000:0648",
"pred": [
- 80
+ 1055
],
"succ": [
- 86
+ 386,
+ 1063
],
"asm": [
- "B4D1|mov AH,0xD1",
- "9E|sahf",
- "7B13|jnp short 0x00DF"
+ "36813EFBFF00F0|cmp word ptr SS:[0xFFFB],0xF000",
+ "36813EF9FF2E06|cmp word ptr SS:[0xFFF9],0x062E",
+ "0F857B0F|jne near 0x15C7"
]
},
{
- "id": 1506,
- "entry": "F000:13DF",
- "term": "F000:13DF",
+ "id": 1063,
+ "entry": "F000:064C",
+ "term": "F000:0663",
"pred": [
- 1495,
- 1502
+ 1058
],
"succ": [
- 1508
+ 386,
+ 1073
],
"asm": [
- "0F83E401|jae near 0x15C7"
+ "B80000|mov AX,0",
+ "8ED8|mov DS,AX",
+ "C7061800C715|mov word ptr DS:[0x0018],0x15C7",
+ "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
+ "31C0|xor AX,AX",
+ "8CC8|mov AX,CS",
+ "39D0|cmp AX,DX",
+ "0F85600F|jne near 0x15C7"
]
},
{
- "id": 1498,
- "entry": "F000:13E5",
- "term": "F000:13EA",
+ "id": 1073,
+ "entry": "F000:0667",
+ "term": "F000:0672",
"pred": [
- 1495,
- 1517
+ 1063
],
"succ": [
- 1502
+ 386,
+ 1079
],
"asm": [
- "83E808|sub AX,8",
- "39C4|cmp SP,AX",
- "0F85D901|jne near 0x15C7"
+ "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
+ "668CC8|mov EAX,CS",
+ "39D0|cmp AX,DX",
+ "0F85510F|jne near 0x15C7"
]
},
{
- "id": 1515,
- "entry": "F000:1403",
- "term": "F000:1403",
+ "id": 1079,
+ "entry": "F000:0676",
+ "term": "F000:0684",
"pred": [
- 1487,
- 1510
+ 1073
],
"succ": [
- 1517
+ 386,
+ 1085
],
"asm": [
- "0F83C001|jae near 0x15C7"
+ "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
+ "8C0E0000|mov word ptr DS:[0],CS",
+ "39160000|cmp word ptr DS:[0],DX",
+ "0F853F0F|jne near 0x15C7"
]
},
{
- "id": 86,
- "entry": "F000:00DF",
- "term": "F000:00DF",
+ "id": 1125,
+ "entry": "F000:06A8",
+ "term": "F000:06AB",
"pred": [
- 82
+ 1085
],
"succ": [
- 88
+ 386,
+ 1128
],
"asm": [
- "7BEC|jnp short 0x00CD"
+ "83FCF9|cmp SP,-7",
+ "0F85180F|jne near 0x15C7"
]
},
{
- "id": 1508,
- "entry": "F000:13E3",
- "term": "F000:13E3",
+ "id": 1128,
+ "entry": "F000:06AF",
+ "term": "F000:06BD",
"pred": [
- 1506
+ 1125
],
"succ": [
- 1510
+ 386,
+ 1133
],
"asm": [
- "EB12|jmp short 0x13F7"
+ "36813EFBFF00F0|cmp word ptr SS:[0xFFFB],0xF000",
+ "36813EF9FFA106|cmp word ptr SS:[0xFFF9],0x06A1",
+ "0F85060F|jne near 0x15C7"
]
},
{
- "id": 1495,
- "entry": "F000:13D6",
- "term": "F000:13D7",
+ "id": 1133,
+ "entry": "F000:06C1",
+ "term": "F000:06FC",
"pred": [
- 1493
+ 1128
],
"succ": [
- 1498,
- 1506
+ 386,
+ 1155
],
"asm": [
- "F8|clc",
- "669AE513000000F0|call far F000:13E5"
+ "B80000|mov AX,0",
+ "8ED8|mov DS,AX",
+ "C7061800C715|mov word ptr DS:[0x0018],0x15C7",
+ "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
+ "BA0021|mov DX,0x2100",
+ "8EDA|mov DS,DX",
+ "BA0061|mov DX,0x6100",
+ "8EC2|mov ES,DX",
+ "B004|mov AL,4",
+ "BA9909|mov DX,0x0999",
+ "EE|out DX,AL",
+ "FC|cld",
+ "66BFFFFF0100|mov EDI,0x0001FFFF",
+ "66BBFFFF0000|mov EBX,0x0000FFFF",
+ "B000|mov AL,0",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "B078|mov AL,0x78",
+ "AA|stos byte ptr ES:[DI],AL",
+ "26673803|cmp byte ptr ES:[EBX],AL",
+ "0F85C70E|jne near 0x15C7"
]
},
{
- "id": 1487,
- "entry": "F000:13CE",
- "term": "F000:13D2",
+ "id": 1155,
+ "entry": "F000:0700",
+ "term": "F000:0707",
"pred": [
- 1483
+ 1133
],
"succ": [
- 1491,
- 1515
+ 386,
+ 1159
],
"asm": [
- "83C004|add AX,4",
- "F9|stc",
- "CB|ret far"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85BC0E|jne near 0x15C7"
]
},
{
- "id": 1510,
- "entry": "F000:13F7",
- "term": "F000:1401",
+ "id": 1159,
+ "entry": "F000:070B",
+ "term": "F000:0727",
"pred": [
- 1508
+ 1155
],
"succ": [
- 1483,
- 1515
+ 386,
+ 1168
],
"asm": [
- "F8|clc",
- "C704C513|mov word ptr DS:[SI],0x13C5",
- "C7440200F0|mov word ptr DS:[SI\u002B2],0xF000",
- "FF1C|call far dword ptr DS:[SI]"
+ "66BEFFFF0100|mov ESI,0x0001FFFF",
+ "66BFFFFF0100|mov EDI,0x0001FFFF",
+ "66BBFFFF0000|mov EBX,0x0000FFFF",
+ "3E678803|mov byte ptr DS:[EBX],AL",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "3C00|cmp AL,0",
+ "0F849C0E|je near 0x15C7"
]
},
{
- "id": 88,
- "entry": "F000:00CD",
- "term": "F000:00D0",
+ "id": 1168,
+ "entry": "F000:072B",
+ "term": "F000:072C",
"pred": [
- 86
+ 1159
],
"succ": [
- 92
+ 386,
+ 1172
],
"asm": [
- "B455|mov AH,0x55",
- "9E|sahf",
- "790F|jns short 0x00E1"
+ "A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
+ "0F85970E|jne near 0x15C7"
]
},
{
- "id": 1493,
- "entry": "F000:13C3",
- "term": "F000:13C3",
+ "id": 1172,
+ "entry": "F000:0730",
+ "term": "F000:0737",
"pred": [
- 1491
+ 1168
],
"succ": [
- 1495
+ 386,
+ 1176
],
"asm": [
- "EB11|jmp short 0x13D6"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F858C0E|jne near 0x15C7"
]
},
{
- "id": 1491,
- "entry": "F000:13BF",
- "term": "F000:13BF",
+ "id": 1176,
+ "entry": "F000:073B",
+ "term": "F000:0742",
"pred": [
- 1479,
- 1487
+ 1172
],
"succ": [
- 1493
+ 386,
+ 1180
],
"asm": [
- "0F830402|jae near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85810E|jne near 0x15C7"
]
},
{
- "id": 1483,
- "entry": "F000:13C5",
- "term": "F000:13CA",
+ "id": 1180,
+ "entry": "F000:0746",
+ "term": "F000:0755",
"pred": [
- 1479,
- 1510
+ 1176
],
"succ": [
- 1487
+ 386,
+ 1188
],
"asm": [
- "83E804|sub AX,4",
- "39C4|cmp SP,AX",
- "0F85F901|jne near 0x15C7"
+ "66BFFFFF0100|mov EDI,0x0001FFFF",
+ "B078|mov AL,0x78",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "3C00|cmp AL,0",
+ "AE|scas AL,byte ptr ES:[DI]",
+ "0F856E0E|jne near 0x15C7"
]
},
{
- "id": 92,
- "entry": "F000:00E1",
- "term": "F000:00E1",
+ "id": 1188,
+ "entry": "F000:0759",
+ "term": "F000:0760",
"pred": [
- 88
+ 1180
],
"succ": [
- 94
+ 386,
+ 1192
],
"asm": [
- "79F0|jns short 0x00D3"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85630E|jne near 0x15C7"
]
},
{
- "id": 1479,
- "entry": "F000:13B7",
- "term": "F000:13BA",
+ "id": 1192,
+ "entry": "F000:0764",
+ "term": "F000:0783",
"pred": [
- 1477
+ 1188
],
"succ": [
- 1483,
- 1491
+ 386,
+ 1204
],
"asm": [
- "89E0|mov AX,SP",
- "F8|clc",
- "9AC51300F0|call far F000:13C5"
+ "66BEFFFF0100|mov ESI,0x0001FFFF",
+ "66BFFFFF0100|mov EDI,0x0001FFFF",
+ "B078|mov AL,0x78",
+ "3E678803|mov byte ptr DS:[EBX],AL",
+ "B000|mov AL,0",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "A4|movs byte ptr ES:[DI],byte ptr DS:[SI]",
+ "B078|mov AL,0x78",
+ "26673803|cmp byte ptr ES:[EBX],AL",
+ "0F85400E|jne near 0x15C7"
]
},
{
- "id": 94,
- "entry": "F000:00D3",
- "term": "F000:00D6",
+ "id": 1204,
+ "entry": "F000:0787",
+ "term": "F000:078E",
"pred": [
- 92
+ 1192
],
"succ": [
- 98
+ 386,
+ 1208
],
"asm": [
- "B494|mov AH,0x94",
- "9E|sahf",
- "770B|ja short 0x00E3"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85350E|jne near 0x15C7"
]
},
{
- "id": 1477,
- "entry": "F000:13B3",
- "term": "F000:13B3",
+ "id": 1208,
+ "entry": "F000:0792",
+ "term": "F000:0799",
"pred": [
- 1459,
- 1473
+ 1204
],
"succ": [
- 1479
+ 386,
+ 1212
],
"asm": [
- "0F831002|jae near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F852A0E|jne near 0x15C7"
]
},
{
- "id": 98,
- "entry": "F000:00E3",
- "term": "F000:00E3",
+ "id": 1212,
+ "entry": "F000:079D",
+ "term": "F000:07AF",
"pred": [
- 94
+ 1208
],
"succ": [
- 100
+ 386,
+ 1221
],
"asm": [
- "77F4|ja short 0x00D9"
+ "66BEFFFF0100|mov ESI,0x0001FFFF",
+ "B078|mov AL,0x78",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "6631C0|xor EAX,EAX",
+ "AC|lods AL,byte ptr DS:[SI]",
+ "3C78|cmp AL,0x78",
+ "0F85140E|jne near 0x15C7"
]
},
{
- "id": 1459,
- "entry": "F000:1396",
- "term": "F000:139A",
+ "id": 1221,
+ "entry": "F000:07B3",
+ "term": "F000:07BA",
"pred": [
- 1455
+ 1212
],
"succ": [
- 1463,
- 1477
+ 386,
+ 1225
],
"asm": [
- "83C004|add AX,4",
- "F9|stc",
- "66C3|ret near"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85090E|jne near 0x15C7"
]
},
{
- "id": 1473,
- "entry": "F000:13A9",
- "term": "F000:13B0",
+ "id": 1225,
+ "entry": "F000:07BE",
+ "term": "F000:07DA",
"pred": [
- 1471
+ 1221
],
"succ": [
- 1455,
- 1477
+ 386,
+ 1236
],
"asm": [
- "F8|clc",
- "66BB8D130000|mov EBX,0x0000138D",
- "66FFD3|call near EBX"
+ "FC|cld",
+ "66BFFEFF0100|mov EDI,0x0001FFFE",
+ "66BBFEFF0000|mov EBX,0x0000FFFE",
+ "B80000|mov AX,0",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "B87856|mov AX,0x5678",
+ "AB|stos word ptr ES:[DI],AX",
+ "26673903|cmp word ptr ES:[EBX],AX",
+ "0F85E90D|jne near 0x15C7"
]
},
{
- "id": 100,
- "entry": "F000:00D9",
- "term": "F000:00D9",
+ "id": 1236,
+ "entry": "F000:07DE",
+ "term": "F000:07E5",
"pred": [
- 98
+ 1225
],
"succ": [
- 102
+ 386,
+ 1240
],
"asm": [
- "EB0A|jmp short 0x00E5"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85DE0D|jne near 0x15C7"
]
},
{
- "id": 1463,
- "entry": "F000:1387",
- "term": "F000:1387",
+ "id": 1240,
+ "entry": "F000:07E9",
+ "term": "F000:0806",
"pred": [
- 1452,
- 1459
+ 1236
],
"succ": [
- 1465
+ 386,
+ 1249
],
"asm": [
- "0F833C02|jae near 0x15C7"
+ "66BEFEFF0100|mov ESI,0x0001FFFE",
+ "66BFFEFF0100|mov EDI,0x0001FFFE",
+ "66BBFEFF0000|mov EBX,0x0000FFFE",
+ "3E678903|mov word ptr DS:[EBX],AX",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "83F800|cmp AX,0",
+ "0F84BD0D|je near 0x15C7"
]
},
{
- "id": 1455,
- "entry": "F000:138D",
- "term": "F000:1392",
+ "id": 1249,
+ "entry": "F000:080A",
+ "term": "F000:080B",
"pred": [
- 1452,
- 1473
+ 1240
],
"succ": [
- 1459
+ 386,
+ 1253
],
"asm": [
- "83E804|sub AX,4",
- "39C4|cmp SP,AX",
- "0F853102|jne near 0x15C7"
+ "A7|cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "0F85B80D|jne near 0x15C7"
]
},
{
- "id": 1471,
- "entry": "F000:13A5",
- "term": "F000:13A5",
+ "id": 1253,
+ "entry": "F000:080F",
+ "term": "F000:0816",
"pred": [
- 1444,
- 1467
+ 1249
],
"succ": [
- 1473
+ 386,
+ 1257
],
"asm": [
- "0F831E02|jae near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85AD0D|jne near 0x15C7"
]
},
{
- "id": 102,
- "entry": "F000:00E5",
- "term": "F000:00EC",
+ "id": 1257,
+ "entry": "F000:081A",
+ "term": "F000:0821",
"pred": [
- 100
+ 1253
],
"succ": [
- 108
+ 386,
+ 1261
],
"asm": [
- "B400|mov AH,0",
- "9E|sahf",
- "B040|mov AL,0x40",
- "D0E0|shl AL,1",
- "7134|jno short 0x0122"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85A20D|jne near 0x15C7"
]
},
{
- "id": 1465,
- "entry": "F000:138B",
- "term": "F000:138B",
+ "id": 1261,
+ "entry": "F000:0825",
+ "term": "F000:0836",
"pred": [
- 1463
+ 1257
],
"succ": [
- 1467
+ 386,
+ 1269
],
"asm": [
- "EB12|jmp short 0x139F"
+ "66BFFEFF0100|mov EDI,0x0001FFFE",
+ "B87856|mov AX,0x5678",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "83F800|cmp AX,0",
+ "AF|scas AX,word ptr ES:[DI]",
+ "0F858D0D|jne near 0x15C7"
]
},
{
- "id": 1452,
- "entry": "F000:1380",
- "term": "F000:1381",
+ "id": 1269,
+ "entry": "F000:083A",
+ "term": "F000:0841",
"pred": [
- 1450
+ 1261
],
"succ": [
- 1455,
- 1463
+ 386,
+ 1273
],
"asm": [
- "F8|clc",
- "66E806000000|call near 0x138D"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85820D|jne near 0x15C7"
]
},
{
- "id": 1444,
- "entry": "F000:1378",
- "term": "F000:137C",
+ "id": 1273,
+ "entry": "F000:0845",
+ "term": "F000:0867",
"pred": [
- 1440
+ 1269
],
"succ": [
- 1448,
- 1471
+ 386,
+ 1285
],
"asm": [
- "83C002|add AX,2",
- "F9|stc",
- "C3|ret near"
+ "66BEFEFF0100|mov ESI,0x0001FFFE",
+ "66BFFEFF0100|mov EDI,0x0001FFFE",
+ "B87856|mov AX,0x5678",
+ "3E678903|mov word ptr DS:[EBX],AX",
+ "B80000|mov AX,0",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "A5|movs word ptr ES:[DI],word ptr DS:[SI]",
+ "B87856|mov AX,0x5678",
+ "26673903|cmp word ptr ES:[EBX],AX",
+ "0F855C0D|jne near 0x15C7"
]
},
{
- "id": 1467,
- "entry": "F000:139F",
- "term": "F000:13A3",
+ "id": 1285,
+ "entry": "F000:086B",
+ "term": "F000:0872",
"pred": [
- 1465
+ 1273
],
"succ": [
- 1440,
- 1471
+ 386,
+ 1289
],
"asm": [
- "F8|clc",
- "BB6F13|mov BX,0x136F",
- "FFD3|call near BX"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85510D|jne near 0x15C7"
]
},
{
- "id": 108,
- "entry": "F000:00EE",
- "term": "F000:00EE",
+ "id": 1289,
+ "entry": "F000:0876",
+ "term": "F000:087D",
"pred": [
- 102
+ 1285
],
"succ": [
- 110
+ 386,
+ 1293
],
"asm": [
- "7033|jo short 0x0123"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85460D|jne near 0x15C7"
]
},
{
- "id": 1450,
- "entry": "F000:136D",
- "term": "F000:136D",
+ "id": 1293,
+ "entry": "F000:0881",
+ "term": "F000:0895",
"pred": [
- 1448
+ 1289
],
"succ": [
- 1452
+ 386,
+ 1302
],
"asm": [
- "EB11|jmp short 0x1380"
+ "66BEFEFF0100|mov ESI,0x0001FFFE",
+ "B87856|mov AX,0x5678",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "6631C0|xor EAX,EAX",
+ "AD|lods AX,word ptr DS:[SI]",
+ "3D7856|cmp AX,0x5678",
+ "0F852E0D|jne near 0x15C7"
]
},
{
- "id": 1448,
- "entry": "F000:1369",
- "term": "F000:1369",
+ "id": 1302,
+ "entry": "F000:0899",
+ "term": "F000:08A0",
"pred": [
- 1428,
- 1444
+ 1293
],
"succ": [
- 1450
+ 386,
+ 1306
],
"asm": [
- "0F835A02|jae near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85230D|jne near 0x15C7"
]
},
{
- "id": 1440,
- "entry": "F000:136F",
- "term": "F000:1374",
+ "id": 1306,
+ "entry": "F000:08A4",
+ "term": "F000:08C9",
"pred": [
- 1428,
- 1467
+ 1302
],
"succ": [
- 1444
+ 386,
+ 1317
],
"asm": [
- "83E802|sub AX,2",
- "39C4|cmp SP,AX",
- "0F854F02|jne near 0x15C7"
+ "FC|cld",
+ "66BFFCFF0100|mov EDI,0x0001FFFC",
+ "66BBFCFF0000|mov EBX,0x0000FFFC",
+ "66B800000000|mov EAX,0",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "66B878563412|mov EAX,0x12345678",
+ "66AB|stos dword ptr ES:[DI],EAX",
+ "2666673903|cmp dword ptr ES:[EBX],EAX",
+ "0F85FA0C|jne near 0x15C7"
]
},
{
- "id": 110,
- "entry": "F000:0123",
- "term": "F000:0123",
+ "id": 1317,
+ "entry": "F000:08CD",
+ "term": "F000:08D4",
"pred": [
- 108
+ 1306
],
"succ": [
- 112
+ 386,
+ 1321
],
"asm": [
- "70CC|jo short 0x00F1"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85EF0C|jne near 0x15C7"
]
},
{
- "id": 1428,
- "entry": "F000:1350",
- "term": "F000:1366",
+ "id": 1321,
+ "entry": "F000:08D8",
+ "term": "F000:08F8",
"pred": [
- 1425
+ 1317
],
"succ": [
- 1440,
- 1448
+ 386,
+ 1330
],
"asm": [
- "BA0022|mov DX,0x2200",
- "8EDA|mov DS,DX",
- "BA0062|mov DX,0x6200",
- "8EC2|mov ES,DX",
- "B005|mov AL,5",
- "BA9909|mov DX,0x0999",
- "EE|out DX,AL",
- "BE0000|mov SI,0",
- "89E0|mov AX,SP",
- "F8|clc",
- "E80600|call near 0x136F"
+ "66BEFCFF0100|mov ESI,0x0001FFFC",
+ "66BFFCFF0100|mov EDI,0x0001FFFC",
+ "66BBFCFF0000|mov EBX,0x0000FFFC",
+ "3E66678903|mov dword ptr DS:[EBX],EAX",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6683F800|cmp EAX,0",
+ "0F84CB0C|je near 0x15C7"
]
},
{
- "id": 112,
- "entry": "F000:00F1",
- "term": "F000:00F1",
+ "id": 1330,
+ "entry": "F000:08FC",
+ "term": "F000:08FE",
"pred": [
- 110
+ 1321
],
"succ": [
- 114
+ 386,
+ 1334
],
"asm": [
- "7C2F|jl short 0x0122"
+ "66A7|cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "0F85C50C|jne near 0x15C7"
]
},
{
- "id": 1425,
- "entry": "F000:1345",
- "term": "F000:134C",
+ "id": 1334,
+ "entry": "F000:0902",
+ "term": "F000:0909",
"pred": [
- 1422
+ 1330
],
"succ": [
- 1428
+ 386,
+ 1338
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F857702|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85BA0C|jne near 0x15C7"
]
},
{
- "id": 114,
- "entry": "F000:00F3",
- "term": "F000:00F3",
+ "id": 1338,
+ "entry": "F000:090D",
+ "term": "F000:0914",
"pred": [
- 112
+ 1334
],
"succ": [
- 116
+ 386,
+ 1342
],
"asm": [
- "7D30|jge short 0x0125"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85AF0C|jne near 0x15C7"
]
},
{
- "id": 1422,
- "entry": "F000:133A",
- "term": "F000:1341",
+ "id": 1342,
+ "entry": "F000:0918",
+ "term": "F000:092F",
"pred": [
- 1415
+ 1338
],
"succ": [
- 1425
+ 386,
+ 1350
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F858202|jne near 0x15C7"
+ "66BFFCFF0100|mov EDI,0x0001FFFC",
+ "66B878563412|mov EAX,0x12345678",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6683F800|cmp EAX,0",
+ "66AF|scas EAX,dword ptr ES:[DI]",
+ "0F85940C|jne near 0x15C7"
]
},
{
- "id": 116,
- "entry": "F000:0125",
- "term": "F000:0125",
+ "id": 1350,
+ "entry": "F000:0933",
+ "term": "F000:093A",
"pred": [
- 114
+ 1342
],
"succ": [
- 118
+ 386,
+ 1354
],
"asm": [
- "7DCF|jge short 0x00F6"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85890C|jne near 0x15C7"
]
},
{
- "id": 1415,
- "entry": "F000:131D",
- "term": "F000:1336",
+ "id": 1354,
+ "entry": "F000:093E",
+ "term": "F000:096D",
"pred": [
- 1412
+ 1350
],
"succ": [
- 1422
+ 386,
+ 1366
],
"asm": [
- "66B940000000|mov ECX,0x00000040",
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F858D02|jne near 0x15C7"
+ "66BEFCFF0100|mov ESI,0x0001FFFC",
+ "66BFFCFF0100|mov EDI,0x0001FFFC",
+ "66B878563412|mov EAX,0x12345678",
+ "3E66678903|mov dword ptr DS:[EBX],EAX",
+ "66B800000000|mov EAX,0",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "66A5|movs dword ptr ES:[DI],dword ptr DS:[SI]",
+ "66B878563412|mov EAX,0x12345678",
+ "2666673903|cmp dword ptr ES:[EBX],EAX",
+ "0F85560C|jne near 0x15C7"
]
},
{
- "id": 118,
- "entry": "F000:00F6",
- "term": "F000:00F6",
+ "id": 1366,
+ "entry": "F000:0971",
+ "term": "F000:0978",
"pred": [
- 116
+ 1354
],
"succ": [
- 120
+ 386,
+ 1370
],
"asm": [
- "7E2A|jle short 0x0122"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F854B0C|jne near 0x15C7"
]
},
{
- "id": 1412,
- "entry": "F000:1312",
- "term": "F000:1319",
+ "id": 1370,
+ "entry": "F000:097C",
+ "term": "F000:0983",
"pred": [
- 1409
+ 1366
],
"succ": [
- 1415
+ 386,
+ 1374
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85AA02|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85400C|jne near 0x15C7"
]
},
{
- "id": 120,
- "entry": "F000:00F8",
- "term": "F000:00F8",
- "pred": [
- 118
+ "id": 1374,
+ "entry": "F000:0987",
+ "term": "F000:09A3",
+ "pred": [
+ 1370
],
"succ": [
- 122
+ 386,
+ 1383
],
"asm": [
- "7F2D|jg short 0x0127"
+ "66BEFCFF0100|mov ESI,0x0001FFFC",
+ "66B878563412|mov EAX,0x12345678",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6631C0|xor EAX,EAX",
+ "66AD|lods EAX,dword ptr DS:[SI]",
+ "663D78563412|cmp EAX,0x12345678",
+ "0F85200C|jne near 0x15C7"
]
},
{
- "id": 1409,
- "entry": "F000:1307",
- "term": "F000:130E",
+ "id": 1383,
+ "entry": "F000:09A7",
+ "term": "F000:09AE",
"pred": [
- 1399
+ 1374
],
"succ": [
- 1412
+ 386,
+ 1387
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85B502|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85150C|jne near 0x15C7"
]
},
{
- "id": 122,
- "entry": "F000:0127",
- "term": "F000:0127",
+ "id": 1387,
+ "entry": "F000:09B2",
+ "term": "F000:09CC",
"pred": [
- 120
+ 1383
],
"succ": [
- 124
+ 386,
+ 1398
],
"asm": [
- "7FD2|jg short 0x00FB"
+ "FD|std",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "B000|mov AL,0",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "B078|mov AL,0x78",
+ "AA|stos byte ptr ES:[DI],AL",
+ "26673803|cmp byte ptr ES:[EBX],AL",
+ "0F85F70B|jne near 0x15C7"
]
},
{
- "id": 1399,
- "entry": "F000:12DB",
- "term": "F000:1303",
+ "id": 1398,
+ "entry": "F000:09D0",
+ "term": "F000:09D7",
"pred": [
- 1396
+ 1387
],
"succ": [
- 1409
+ 386,
+ 1402
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66B800000000|mov EAX,0",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B940000000|mov ECX,0x00000040",
- "F366A5|rep movs dword ptr ES:[DI],dword ptr DS:[SI]",
- "6683F900|cmp ECX,0",
- "0F85C002|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85EC0B|jne near 0x15C7"
]
},
{
- "id": 124,
- "entry": "F000:00FB",
- "term": "F000:00FE",
+ "id": 1402,
+ "entry": "F000:09DB",
+ "term": "F000:09F7",
"pred": [
- 122
+ 1398
],
"succ": [
- 128
+ 386,
+ 1411
],
"asm": [
- "B440|mov AH,0x40",
- "9E|sahf",
- "7C29|jl short 0x0129"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "3E678803|mov byte ptr DS:[EBX],AL",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "3C00|cmp AL,0",
+ "0F84CC0B|je near 0x15C7"
]
},
{
- "id": 1396,
- "entry": "F000:12D0",
- "term": "F000:12D7",
+ "id": 1411,
+ "entry": "F000:09FB",
+ "term": "F000:09FC",
"pred": [
- 1389
+ 1402
],
"succ": [
- 1399
+ 386,
+ 1415
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85EC02|jne near 0x15C7"
+ "A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
+ "0F85C70B|jne near 0x15C7"
]
},
{
- "id": 128,
- "entry": "F000:0129",
- "term": "F000:0129",
+ "id": 1415,
+ "entry": "F000:0A00",
+ "term": "F000:0A07",
"pred": [
- 124
+ 1411
],
"succ": [
- 130
+ 386,
+ 1419
],
"asm": [
- "7CD6|jl short 0x0101"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85BC0B|jne near 0x15C7"
]
},
{
- "id": 1389,
- "entry": "F000:12B3",
- "term": "F000:12CC",
+ "id": 1419,
+ "entry": "F000:0A0B",
+ "term": "F000:0A12",
"pred": [
- 1386
+ 1415
],
"succ": [
- 1396
+ 386,
+ 1423
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "66B940000000|mov ECX,0x00000040",
- "F366AF|repe scas EAX,dword ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F85F702|jne near 0x15C7"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85B10B|jne near 0x15C7"
]
},
{
- "id": 130,
- "entry": "F000:0101",
- "term": "F000:0101",
+ "id": 1423,
+ "entry": "F000:0A16",
+ "term": "F000:0A25",
"pred": [
- 128
+ 1419
],
"succ": [
- 132
+ 386,
+ 1431
],
"asm": [
- "7E28|jle short 0x012B"
+ "66BF00000100|mov EDI,0x00010000",
+ "B078|mov AL,0x78",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "3C00|cmp AL,0",
+ "AE|scas AL,byte ptr ES:[DI]",
+ "0F859E0B|jne near 0x15C7"
]
},
{
- "id": 1386,
- "entry": "F000:12A8",
- "term": "F000:12AF",
+ "id": 1431,
+ "entry": "F000:0A29",
+ "term": "F000:0A30",
"pred": [
- 1383
+ 1423
],
"succ": [
- 1389
+ 386,
+ 1435
],
"asm": [
"6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F851403|jne near 0x15C7"
+ "0F85930B|jne near 0x15C7"
]
},
{
- "id": 132,
- "entry": "F000:012B",
- "term": "F000:012B",
+ "id": 1435,
+ "entry": "F000:0A34",
+ "term": "F000:0A53",
"pred": [
- 130
+ 1431
],
"succ": [
- 134
+ 386,
+ 1447
],
"asm": [
- "7ED7|jle short 0x0104"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "B078|mov AL,0x78",
+ "3E678803|mov byte ptr DS:[EBX],AL",
+ "B000|mov AL,0",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "A4|movs byte ptr ES:[DI],byte ptr DS:[SI]",
+ "B078|mov AL,0x78",
+ "26673803|cmp byte ptr ES:[EBX],AL",
+ "0F85700B|jne near 0x15C7"
]
},
{
- "id": 1383,
- "entry": "F000:129D",
- "term": "F000:12A4",
+ "id": 1447,
+ "entry": "F000:0A57",
+ "term": "F000:0A5E",
"pred": [
- 1368
+ 1435
],
"succ": [
- 1386
+ 386,
+ 1451
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F851F03|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85650B|jne near 0x15C7"
]
},
{
- "id": 134,
- "entry": "F000:0104",
- "term": "F000:010A",
+ "id": 1451,
+ "entry": "F000:0A62",
+ "term": "F000:0A69",
"pred": [
- 132
+ 1447
],
"succ": [
- 137
+ 386,
+ 1455
],
"asm": [
- "66B901000000|mov ECX,1",
- "E316|jcxz short 0x0122"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F855A0B|jne near 0x15C7"
]
},
{
- "id": 1368,
- "entry": "F000:126A",
- "term": "F000:1299",
+ "id": 1455,
+ "entry": "F000:0A6D",
+ "term": "F000:0A7F",
"pred": [
- 1365
+ 1451
],
"succ": [
- 1383
+ 386,
+ 1464
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B940000000|mov ECX,0x00000040",
- "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F852A03|jne near 0x15C7"
+ "66BE00000100|mov ESI,0x00010000",
+ "B078|mov AL,0x78",
+ "26678803|mov byte ptr ES:[EBX],AL",
+ "6631C0|xor EAX,EAX",
+ "AC|lods AL,byte ptr DS:[SI]",
+ "3C78|cmp AL,0x78",
+ "0F85440B|jne near 0x15C7"
]
},
{
- "id": 137,
- "entry": "F000:010C",
- "term": "F000:0112",
+ "id": 1464,
+ "entry": "F000:0A83",
+ "term": "F000:0A8A",
"pred": [
- 134
+ 1455
],
"succ": [
- 140
+ 386,
+ 1468
],
"asm": [
- "66B900000100|mov ECX,0x00010000",
- "E319|jcxz short 0x012D"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85390B|jne near 0x15C7"
]
},
{
- "id": 1365,
- "entry": "F000:125F",
- "term": "F000:1266",
+ "id": 1468,
+ "entry": "F000:0A8E",
+ "term": "F000:0AAA",
"pred": [
- 1353
+ 1464
],
"succ": [
- 1368
+ 386,
+ 1479
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F855D03|jne near 0x15C7"
+ "FD|std",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "B80000|mov AX,0",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "B87856|mov AX,0x5678",
+ "AB|stos word ptr ES:[DI],AX",
+ "26673903|cmp word ptr ES:[EBX],AX",
+ "0F85190B|jne near 0x15C7"
]
},
{
- "id": 140,
- "entry": "F000:012D",
- "term": "F000:012D",
+ "id": 1479,
+ "entry": "F000:0AAE",
+ "term": "F000:0AB5",
"pred": [
- 137
+ 1468
],
"succ": [
- 142
+ 386,
+ 1483
],
"asm": [
- "E3E5|jcxz short 0x0114"
+ "6681FFFEFF0100|cmp EDI,0x0001FFFE",
+ "0F850E0B|jne near 0x15C7"
]
},
{
- "id": 1353,
- "entry": "F000:1229",
- "term": "F000:125B",
+ "id": 1483,
+ "entry": "F000:0AB9",
+ "term": "F000:0AD6",
"pred": [
- 1350
+ 1479
],
"succ": [
- 1365
+ 386,
+ 1492
],
"asm": [
- "FD|std",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "6683F900|cmp ECX,0",
- "0F856803|jne near 0x15C7"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "3E678903|mov word ptr DS:[EBX],AX",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "83F800|cmp AX,0",
+ "0F84ED0A|je near 0x15C7"
]
},
{
- "id": 142,
- "entry": "F000:0114",
- "term": "F000:0114",
+ "id": 1492,
+ "entry": "F000:0ADA",
+ "term": "F000:0ADB",
"pred": [
- 140
+ 1483
],
"succ": [
- 144
+ 386,
+ 1496
],
"asm": [
- "67E30B|jecxz short 0x0122"
+ "A7|cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "0F85E80A|jne near 0x15C7"
]
},
{
- "id": 1350,
- "entry": "F000:121E",
- "term": "F000:1225",
+ "id": 1496,
+ "entry": "F000:0ADF",
+ "term": "F000:0AE6",
"pred": [
- 1347
+ 1492
],
"succ": [
- 1353
+ 386,
+ 1500
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F859E03|jne near 0x15C7"
+ "6681FFFEFF0100|cmp EDI,0x0001FFFE",
+ "0F85DD0A|jne near 0x15C7"
]
},
{
- "id": 144,
- "entry": "F000:0117",
- "term": "F000:011D",
+ "id": 1500,
+ "entry": "F000:0AEA",
+ "term": "F000:0AF1",
"pred": [
- 142
+ 1496
],
"succ": [
- 147
+ 386,
+ 1504
],
"asm": [
- "66B900000000|mov ECX,0",
- "67E30F|jecxz short 0x012F"
+ "6681FEFEFF0100|cmp ESI,0x0001FFFE",
+ "0F85D20A|jne near 0x15C7"
]
},
{
- "id": 1347,
- "entry": "F000:1213",
- "term": "F000:121A",
+ "id": 1504,
+ "entry": "F000:0AF5",
+ "term": "F000:0B06",
"pred": [
- 1340
+ 1500
],
"succ": [
- 1350
+ 386,
+ 1512
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85A903|jne near 0x15C7"
+ "66BF00000100|mov EDI,0x00010000",
+ "B87856|mov AX,0x5678",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "83F800|cmp AX,0",
+ "AF|scas AX,word ptr ES:[DI]",
+ "0F85BD0A|jne near 0x15C7"
]
},
{
- "id": 147,
- "entry": "F000:012F",
- "term": "F000:012F",
+ "id": 1512,
+ "entry": "F000:0B0A",
+ "term": "F000:0B11",
"pred": [
- 144
+ 1504
],
"succ": [
- 149
+ 386,
+ 1516
],
"asm": [
- "67E3EE|jecxz short 0x0120"
+ "6681FFFEFF0100|cmp EDI,0x0001FFFE",
+ "0F85B20A|jne near 0x15C7"
]
},
{
- "id": 1340,
- "entry": "F000:11F7",
- "term": "F000:120F",
+ "id": 1516,
+ "entry": "F000:0B15",
+ "term": "F000:0B37",
"pred": [
- 1337
+ 1512
],
"succ": [
- 1347
+ 386,
+ 1528
],
"asm": [
- "66B980000000|mov ECX,0x00000080",
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F85B403|jne near 0x15C7"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "B87856|mov AX,0x5678",
+ "3E678903|mov word ptr DS:[EBX],AX",
+ "B80000|mov AX,0",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "A5|movs word ptr ES:[DI],word ptr DS:[SI]",
+ "B87856|mov AX,0x5678",
+ "26673903|cmp word ptr ES:[EBX],AX",
+ "0F858C0A|jne near 0x15C7"
]
},
{
- "id": 149,
- "entry": "F000:0120",
- "term": "F000:0120",
+ "id": 1528,
+ "entry": "F000:0B3B",
+ "term": "F000:0B42",
"pred": [
- 147
+ 1516
],
"succ": [
- 151
+ 386,
+ 1532
],
"asm": [
- "EB10|jmp short 0x0132"
+ "6681FFFEFF0100|cmp EDI,0x0001FFFE",
+ "0F85810A|jne near 0x15C7"
]
},
{
- "id": 1337,
- "entry": "F000:11EC",
- "term": "F000:11F3",
+ "id": 1532,
+ "entry": "F000:0B46",
+ "term": "F000:0B4D",
"pred": [
- 1334
+ 1528
],
"succ": [
- 1340
+ 386,
+ 1536
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85D003|jne near 0x15C7"
+ "6681FEFEFF0100|cmp ESI,0x0001FFFE",
+ "0F85760A|jne near 0x15C7"
]
},
{
- "id": 151,
- "entry": "F000:0132",
- "term": "F000:0135",
+ "id": 1536,
+ "entry": "F000:0B51",
+ "term": "F000:0B65",
"pred": [
- 149
+ 1532
],
"succ": [
- 155
+ 386,
+ 1545
],
"asm": [
- "B401|mov AH,1",
- "9E|sahf",
- "0F83B700|jae near 0x01F0"
+ "66BE00000100|mov ESI,0x00010000",
+ "B87856|mov AX,0x5678",
+ "26678903|mov word ptr ES:[EBX],AX",
+ "6631C0|xor EAX,EAX",
+ "AD|lods AX,word ptr DS:[SI]",
+ "3D7856|cmp AX,0x5678",
+ "0F855E0A|jne near 0x15C7"
]
},
{
- "id": 1334,
- "entry": "F000:11E1",
- "term": "F000:11E8",
+ "id": 1545,
+ "entry": "F000:0B69",
+ "term": "F000:0B70",
"pred": [
- 1324
+ 1536
],
"succ": [
- 1337
+ 386,
+ 1549
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85DB03|jne near 0x15C7"
+ "6681FEFEFF0100|cmp ESI,0x0001FFFE",
+ "0F85530A|jne near 0x15C7"
]
},
{
- "id": 155,
- "entry": "F000:0139",
- "term": "F000:0139",
+ "id": 1549,
+ "entry": "F000:0B74",
+ "term": "F000:0B99",
"pred": [
- 151
+ 1545
],
"succ": [
- 157
+ 386,
+ 1560
],
"asm": [
- "0F82B400|jb near 0x01F1"
+ "FD|std",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "66B800000000|mov EAX,0",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "66B878563412|mov EAX,0x12345678",
+ "66AB|stos dword ptr ES:[DI],EAX",
+ "2666673903|cmp dword ptr ES:[EBX],EAX",
+ "0F852A0A|jne near 0x15C7"
]
},
{
- "id": 1324,
- "entry": "F000:11B7",
- "term": "F000:11DD",
+ "id": 1560,
+ "entry": "F000:0B9D",
+ "term": "F000:0BA4",
"pred": [
- 1321
+ 1549
],
"succ": [
- 1334
+ 386,
+ 1564
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66B800000000|mov EAX,0",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B980000000|mov ECX,0x00000080",
- "F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
- "6683F900|cmp ECX,0",
- "0F85E603|jne near 0x15C7"
+ "6681FFFCFF0100|cmp EDI,0x0001FFFC",
+ "0F851F0A|jne near 0x15C7"
]
},
{
- "id": 157,
- "entry": "F000:01F1",
- "term": "F000:01F1",
+ "id": 1564,
+ "entry": "F000:0BA8",
+ "term": "F000:0BC8",
"pred": [
- 155
+ 1560
],
"succ": [
- 159
+ 386,
+ 1573
],
"asm": [
- "0F8249FF|jb near 0x013E"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "66BB00000000|mov EBX,0",
+ "3E66678903|mov dword ptr DS:[EBX],EAX",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6683F800|cmp EAX,0",
+ "0F84FB09|je near 0x15C7"
]
},
{
- "id": 1321,
- "entry": "F000:11AC",
- "term": "F000:11B3",
+ "id": 1573,
+ "entry": "F000:0BCC",
+ "term": "F000:0BCE",
"pred": [
- 1314
+ 1564
],
"succ": [
- 1324
+ 386,
+ 1577
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F851004|jne near 0x15C7"
+ "66A7|cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "0F85F509|jne near 0x15C7"
]
},
{
- "id": 159,
- "entry": "F000:013E",
- "term": "F000:0141",
+ "id": 1577,
+ "entry": "F000:0BD2",
+ "term": "F000:0BD9",
"pred": [
- 157
+ 1573
],
"succ": [
- 163
+ 386,
+ 1581
],
"asm": [
- "B440|mov AH,0x40",
- "9E|sahf",
- "0F85AB00|jne near 0x01F0"
+ "6681FFFCFF0100|cmp EDI,0x0001FFFC",
+ "0F85EA09|jne near 0x15C7"
]
},
{
- "id": 1314,
- "entry": "F000:1190",
- "term": "F000:11A8",
+ "id": 1581,
+ "entry": "F000:0BDD",
+ "term": "F000:0BE4",
"pred": [
- 1311
+ 1577
],
"succ": [
- 1321
+ 386,
+ 1585
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "66B980000000|mov ECX,0x00000080",
- "F3AF|repe scas AX,word ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F851B04|jne near 0x15C7"
+ "6681FEFCFF0100|cmp ESI,0x0001FFFC",
+ "0F85DF09|jne near 0x15C7"
]
},
{
- "id": 163,
- "entry": "F000:0145",
- "term": "F000:0145",
+ "id": 1585,
+ "entry": "F000:0BE8",
+ "term": "F000:0BFF",
"pred": [
- 159
+ 1581
],
"succ": [
- 165
+ 386,
+ 1593
],
"asm": [
- "0F84AC00|je near 0x01F5"
+ "66BF00000100|mov EDI,0x00010000",
+ "66B878563412|mov EAX,0x12345678",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6683F800|cmp EAX,0",
+ "66AF|scas EAX,dword ptr ES:[DI]",
+ "0F85C409|jne near 0x15C7"
]
},
{
- "id": 1311,
- "entry": "F000:1185",
- "term": "F000:118C",
+ "id": 1593,
+ "entry": "F000:0C03",
+ "term": "F000:0C0A",
"pred": [
- 1308
+ 1585
],
"succ": [
- 1314
+ 386,
+ 1597
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F853704|jne near 0x15C7"
+ "6681FFFCFF0100|cmp EDI,0x0001FFFC",
+ "0F85B909|jne near 0x15C7"
]
},
{
- "id": 165,
- "entry": "F000:01F5",
- "term": "F000:01F5",
+ "id": 1597,
+ "entry": "F000:0C0E",
+ "term": "F000:0C3D",
"pred": [
- 163
+ 1593
],
"succ": [
- 167
+ 386,
+ 1609
],
"asm": [
- "0F8451FF|je near 0x014A"
+ "66BE00000100|mov ESI,0x00010000",
+ "66BF00000100|mov EDI,0x00010000",
+ "66B878563412|mov EAX,0x12345678",
+ "3E66678903|mov dword ptr DS:[EBX],EAX",
+ "66B800000000|mov EAX,0",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "66A5|movs dword ptr ES:[DI],dword ptr DS:[SI]",
+ "66B878563412|mov EAX,0x12345678",
+ "2666673903|cmp dword ptr ES:[EBX],EAX",
+ "0F858609|jne near 0x15C7"
]
},
{
- "id": 1308,
- "entry": "F000:117A",
- "term": "F000:1181",
+ "id": 1609,
+ "entry": "F000:0C41",
+ "term": "F000:0C48",
"pred": [
- 1293
+ 1597
],
"succ": [
- 1311
+ 386,
+ 1613
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F854204|jne near 0x15C7"
+ "6681FFFCFF0100|cmp EDI,0x0001FFFC",
+ "0F857B09|jne near 0x15C7"
]
},
{
- "id": 167,
- "entry": "F000:014A",
- "term": "F000:014D",
+ "id": 1613,
+ "entry": "F000:0C4C",
+ "term": "F000:0C53",
"pred": [
- 165
+ 1609
],
"succ": [
- 171
+ 386,
+ 1617
],
"asm": [
- "B404|mov AH,4",
- "9E|sahf",
- "0F8B9F00|jnp near 0x01F0"
+ "6681FEFCFF0100|cmp ESI,0x0001FFFC",
+ "0F857009|jne near 0x15C7"
]
},
{
- "id": 1293,
- "entry": "F000:1149",
- "term": "F000:1176",
+ "id": 1617,
+ "entry": "F000:0C57",
+ "term": "F000:0C73",
"pred": [
- 1290
+ 1613
],
"succ": [
- 1308
+ 386,
+ 1626
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B980000000|mov ECX,0x00000080",
- "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F854D04|jne near 0x15C7"
+ "66BE00000100|mov ESI,0x00010000",
+ "66B878563412|mov EAX,0x12345678",
+ "2666678903|mov dword ptr ES:[EBX],EAX",
+ "6631C0|xor EAX,EAX",
+ "66AD|lods EAX,dword ptr DS:[SI]",
+ "663D78563412|cmp EAX,0x12345678",
+ "0F855009|jne near 0x15C7"
]
},
{
- "id": 171,
- "entry": "F000:0151",
- "term": "F000:0151",
+ "id": 1626,
+ "entry": "F000:0C77",
+ "term": "F000:0C7E",
"pred": [
- 167
+ 1617
],
"succ": [
- 173
+ 386,
+ 1630
],
"asm": [
- "0F8AA400|jp near 0x01F9"
+ "6681FEFCFF0100|cmp ESI,0x0001FFFC",
+ "0F854509|jne near 0x15C7"
]
},
{
- "id": 1290,
- "entry": "F000:113E",
- "term": "F000:1145",
+ "id": 1630,
+ "entry": "F000:0C82",
+ "term": "F000:0CB3",
"pred": [
- 1278
+ 1626
],
"succ": [
- 1293
+ 386,
+ 1643
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F857E04|jne near 0x15C7"
+ "FC|cld",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "6683F900|cmp ECX,0",
+ "0F851009|jne near 0x15C7"
]
},
{
- "id": 173,
- "entry": "F000:01F9",
- "term": "F000:01F9",
+ "id": 1643,
+ "entry": "F000:0CB7",
+ "term": "F000:0CBE",
"pred": [
- 171
+ 1630
],
"succ": [
- 175
+ 386,
+ 1647
],
"asm": [
- "0F8A59FF|jp near 0x0156"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F850509|jne near 0x15C7"
]
},
{
- "id": 1278,
- "entry": "F000:1109",
- "term": "F000:113A",
+ "id": 1647,
+ "entry": "F000:0CC2",
+ "term": "F000:0CEF",
"pred": [
- 1275
+ 1643
],
"succ": [
- 1290
+ 386,
+ 1663
],
"asm": [
- "FD|std",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B900010000|mov ECX,0x00000100",
+ "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
"6683F900|cmp ECX,0",
- "0F858904|jne near 0x15C7"
+ "0F85D408|jne near 0x15C7"
]
},
{
- "id": 175,
- "entry": "F000:0156",
- "term": "F000:0159",
+ "id": 1663,
+ "entry": "F000:0CF3",
+ "term": "F000:0CFA",
"pred": [
- 173
+ 1647
],
"succ": [
- 179
+ 386,
+ 1667
],
"asm": [
- "B480|mov AH,0x80",
- "9E|sahf",
- "0F899300|jns near 0x01F0"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85C908|jne near 0x15C7"
]
},
{
- "id": 1275,
- "entry": "F000:10FE",
- "term": "F000:1105",
+ "id": 1667,
+ "entry": "F000:0CFE",
+ "term": "F000:0D05",
"pred": [
- 1272
+ 1663
],
"succ": [
- 1278
+ 386,
+ 1671
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85BE04|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85BE08|jne near 0x15C7"
]
},
{
- "id": 179,
- "entry": "F000:015D",
- "term": "F000:015D",
+ "id": 1671,
+ "entry": "F000:0D09",
+ "term": "F000:0D21",
"pred": [
- 175
+ 1667
],
"succ": [
- 181
+ 386,
+ 1679
],
"asm": [
- "0F889C00|js near 0x01FD"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AE|repe scas AL,byte ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F85A208|jne near 0x15C7"
]
},
{
- "id": 1272,
- "entry": "F000:10F3",
- "term": "F000:10FA",
+ "id": 1679,
+ "entry": "F000:0D25",
+ "term": "F000:0D2C",
"pred": [
- 1265
+ 1671
],
"succ": [
- 1275
+ 386,
+ 1683
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85C904|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F859708|jne near 0x15C7"
]
},
{
- "id": 181,
- "entry": "F000:01FD",
- "term": "F000:01FD",
+ "id": 1683,
+ "entry": "F000:0D30",
+ "term": "F000:0D56",
"pred": [
- 179
+ 1679
],
"succ": [
- 183
+ 386,
+ 1694
],
"asm": [
- "0F8861FF|js near 0x0162"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B800000000|mov EAX,0",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B900010000|mov ECX,0x00000100",
+ "F3A4|rep movs byte ptr ES:[DI],byte ptr DS:[SI]",
+ "6683F900|cmp ECX,0",
+ "0F856D08|jne near 0x15C7"
]
},
{
- "id": 1265,
- "entry": "F000:10D7",
- "term": "F000:10EF",
+ "id": 1694,
+ "entry": "F000:0D5A",
+ "term": "F000:0D61",
"pred": [
- 1262
+ 1683
],
"succ": [
- 1272
+ 386,
+ 1698
],
"asm": [
- "66B900010000|mov ECX,0x00000100",
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F85D404|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F856208|jne near 0x15C7"
]
},
{
- "id": 183,
- "entry": "F000:0162",
- "term": "F000:0165",
+ "id": 1698,
+ "entry": "F000:0D65",
+ "term": "F000:0D6C",
"pred": [
- 181
+ 1694
],
"succ": [
- 187
+ 386,
+ 1702
],
"asm": [
- "B441|mov AH,0x41",
- "9E|sahf",
- "0F878700|ja near 0x01F0"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F855708|jne near 0x15C7"
]
},
{
- "id": 1262,
- "entry": "F000:10CC",
- "term": "F000:10D3",
+ "id": 1702,
+ "entry": "F000:0D70",
+ "term": "F000:0D88",
"pred": [
- 1259
+ 1698
],
"succ": [
- 1265
+ 386,
+ 1710
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85F004|jne near 0x15C7"
+ "66B900010000|mov ECX,0x00000100",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F853B08|jne near 0x15C7"
]
},
{
- "id": 187,
- "entry": "F000:0169",
- "term": "F000:0169",
+ "id": 1710,
+ "entry": "F000:0D8C",
+ "term": "F000:0D93",
"pred": [
- 183
+ 1702
],
"succ": [
- 189
+ 386,
+ 1714
],
"asm": [
- "0F869400|jbe near 0x0201"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F853008|jne near 0x15C7"
]
},
{
- "id": 1259,
- "entry": "F000:10C1",
- "term": "F000:10C8",
+ "id": 1714,
+ "entry": "F000:0D97",
+ "term": "F000:0D9E",
"pred": [
- 1249
+ 1710
],
"succ": [
- 1262
+ 386,
+ 1718
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85FB04|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F852508|jne near 0x15C7"
]
},
{
- "id": 189,
- "entry": "F000:0201",
- "term": "F000:0201",
+ "id": 1718,
+ "entry": "F000:0DA2",
+ "term": "F000:0DD3",
"pred": [
- 187
+ 1714
],
"succ": [
- 191
+ 386,
+ 1731
],
"asm": [
- "0F8668FF|jbe near 0x016D"
+ "FC|cld",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "6683F900|cmp ECX,0",
+ "0F85F007|jne near 0x15C7"
]
},
{
- "id": 1249,
- "entry": "F000:1097",
- "term": "F000:10BD",
+ "id": 1731,
+ "entry": "F000:0DD7",
+ "term": "F000:0DDE",
"pred": [
- 1246
+ 1718
],
"succ": [
- 1259
+ 386,
+ 1735
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66B800000000|mov EAX,0",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B900010000|mov ECX,0x00000100",
- "F3A4|rep movs byte ptr ES:[DI],byte ptr DS:[SI]",
- "6683F900|cmp ECX,0",
- "0F850605|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85E507|jne near 0x15C7"
]
},
{
- "id": 191,
- "entry": "F000:016D",
- "term": "F000:016D",
+ "id": 1735,
+ "entry": "F000:0DE2",
+ "term": "F000:0E0F",
"pred": [
- 189
+ 1731
],
"succ": [
- 193
+ 386,
+ 1751
],
"asm": [
- "E99500|jmp near 0x0205"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B980000000|mov ECX,0x00000080",
+ "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F85B407|jne near 0x15C7"
]
},
{
- "id": 1246,
- "entry": "F000:108C",
- "term": "F000:1093",
+ "id": 1751,
+ "entry": "F000:0E13",
+ "term": "F000:0E1A",
"pred": [
- 1239
+ 1735
],
"succ": [
- 1249
+ 386,
+ 1755
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F853005|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F85A907|jne near 0x15C7"
]
},
{
- "id": 193,
- "entry": "F000:0205",
- "term": "F000:020C",
+ "id": 1755,
+ "entry": "F000:0E1E",
+ "term": "F000:0E25",
"pred": [
- 191
+ 1751
],
"succ": [
- 199
+ 386,
+ 1759
],
"asm": [
- "B4D4|mov AH,0xD4",
- "9E|sahf",
- "B80000|mov AX,0",
- "9E|sahf",
- "0F83A400|jae near 0x02B4"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F859E07|jne near 0x15C7"
]
},
{
- "id": 1239,
- "entry": "F000:1070",
- "term": "F000:1088",
+ "id": 1759,
+ "entry": "F000:0E29",
+ "term": "F000:0E41",
"pred": [
- 1236
+ 1755
],
"succ": [
- 1246
+ 386,
+ 1767
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "66BEFF000100|mov ESI,0x000100FF",
- "66B900010000|mov ECX,0x00000100",
- "F3AE|repe scas AL,byte ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F853B05|jne near 0x15C7"
- ]
- },
- {
- "id": 199,
- "entry": "F000:02B4",
- "term": "F000:02B4",
- "pred": [
- 193
- ],
- "succ": [
- 201
- ],
- "asm": [
- "0F8359FF|jae near 0x0211"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AF|repe scas AX,word ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F858207|jne near 0x15C7"
]
},
{
- "id": 1236,
- "entry": "F000:1065",
- "term": "F000:106C",
+ "id": 1767,
+ "entry": "F000:0E45",
+ "term": "F000:0E4C",
"pred": [
- 1233
+ 1759
],
"succ": [
- 1239
+ 386,
+ 1771
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F855705|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F857707|jne near 0x15C7"
]
},
{
- "id": 201,
- "entry": "F000:0211",
- "term": "F000:0214",
+ "id": 1771,
+ "entry": "F000:0E50",
+ "term": "F000:0E76",
"pred": [
- 199
+ 1767
],
"succ": [
- 205
+ 386,
+ 1782
],
"asm": [
- "B495|mov AH,0x95",
- "9E|sahf",
- "0F85A000|jne near 0x02B8"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B800000000|mov EAX,0",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B980000000|mov ECX,0x00000080",
+ "F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
+ "6683F900|cmp ECX,0",
+ "0F854D07|jne near 0x15C7"
]
},
{
- "id": 1233,
- "entry": "F000:105A",
- "term": "F000:1061",
+ "id": 1782,
+ "entry": "F000:0E7A",
+ "term": "F000:0E81",
"pred": [
- 1218
+ 1771
],
"succ": [
- 1236
+ 386,
+ 1786
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F856205|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F854207|jne near 0x15C7"
]
},
{
- "id": 205,
- "entry": "F000:02B8",
- "term": "F000:02B8",
+ "id": 1786,
+ "entry": "F000:0E85",
+ "term": "F000:0E8C",
"pred": [
- 201
+ 1782
],
"succ": [
- 207
+ 386,
+ 1790
],
"asm": [
- "0F855DFF|jne near 0x0219"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F853707|jne near 0x15C7"
]
},
{
- "id": 1218,
- "entry": "F000:1029",
- "term": "F000:1056",
+ "id": 1790,
+ "entry": "F000:0E90",
+ "term": "F000:0EA8",
"pred": [
- 1215
+ 1786
],
"succ": [
- 1233
+ 386,
+ 1798
],
"asm": [
- "66BFFF000100|mov EDI,0x000100FF",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B900010000|mov ECX,0x00000100",
- "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
+ "66B980000000|mov ECX,0x00000080",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
"6683F900|cmp ECX,0",
- "0F856D05|jne near 0x15C7"
+ "0F851B07|jne near 0x15C7"
]
},
{
- "id": 207,
- "entry": "F000:0219",
- "term": "F000:021C",
+ "id": 1798,
+ "entry": "F000:0EAC",
+ "term": "F000:0EB3",
"pred": [
- 205
+ 1790
],
"succ": [
- 211
+ 386,
+ 1802
],
"asm": [
- "B4D1|mov AH,0xD1",
- "9E|sahf",
- "0F8B9C00|jnp near 0x02BC"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F851007|jne near 0x15C7"
]
},
{
- "id": 1215,
- "entry": "F000:101E",
- "term": "F000:1025",
+ "id": 1802,
+ "entry": "F000:0EB7",
+ "term": "F000:0EBE",
"pred": [
- 1203
+ 1798
],
"succ": [
- 1218
+ 386,
+ 1806
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F859E05|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F850507|jne near 0x15C7"
]
},
{
- "id": 211,
- "entry": "F000:02BC",
- "term": "F000:02BC",
+ "id": 1806,
+ "entry": "F000:0EC2",
+ "term": "F000:0EF4",
"pred": [
- 207
+ 1802
],
"succ": [
- 213
+ 386,
+ 1819
],
"asm": [
- "0F8B61FF|jnp near 0x0221"
+ "FC|cld",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B878563412|mov EAX,0x12345678",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
+ "6683F900|cmp ECX,0",
+ "0F85CF06|jne near 0x15C7"
]
},
{
- "id": 1203,
- "entry": "F000:0FE9",
- "term": "F000:101A",
+ "id": 1819,
+ "entry": "F000:0EF8",
+ "term": "F000:0EFF",
"pred": [
- 1200
+ 1806
],
"succ": [
- 1215
+ 386,
+ 1823
],
"asm": [
- "FD|std",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B878563412|mov EAX,0x12345678",
- "66BEFF000100|mov ESI,0x000100FF",
- "66BFFF000100|mov EDI,0x000100FF",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "6683F900|cmp ECX,0",
- "0F85A905|jne near 0x15C7"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85C406|jne near 0x15C7"
]
},
{
- "id": 213,
- "entry": "F000:0221",
- "term": "F000:0224",
+ "id": 1823,
+ "entry": "F000:0F03",
+ "term": "F000:0F32",
"pred": [
- 211
+ 1819
],
"succ": [
- 217
+ 386,
+ 1839
],
"asm": [
- "B455|mov AH,0x55",
- "9E|sahf",
- "0F899800|jns near 0x02C0"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B940000000|mov ECX,0x00000040",
+ "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F859106|jne near 0x15C7"
]
},
{
- "id": 1200,
- "entry": "F000:0FDE",
- "term": "F000:0FE5",
+ "id": 1839,
+ "entry": "F000:0F36",
+ "term": "F000:0F3D",
"pred": [
- 1197
+ 1823
],
"succ": [
- 1203
+ 386,
+ 1843
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85DE05|jne near 0x15C7"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F858606|jne near 0x15C7"
]
},
{
- "id": 217,
- "entry": "F000:02C0",
- "term": "F000:02C0",
+ "id": 1843,
+ "entry": "F000:0F41",
+ "term": "F000:0F48",
"pred": [
- 213
+ 1839
],
"succ": [
- 219
+ 386,
+ 1847
],
"asm": [
- "0F8965FF|jns near 0x0229"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F857B06|jne near 0x15C7"
]
},
{
- "id": 1197,
- "entry": "F000:0FD3",
- "term": "F000:0FDA",
+ "id": 1847,
+ "entry": "F000:0F4C",
+ "term": "F000:0F65",
"pred": [
- 1190
+ 1843
],
"succ": [
- 1200
+ 386,
+ 1855
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85E905|jne near 0x15C7"
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AF|repe scas EAX,dword ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F855E06|jne near 0x15C7"
]
},
{
- "id": 219,
- "entry": "F000:0229",
- "term": "F000:022C",
+ "id": 1855,
+ "entry": "F000:0F69",
+ "term": "F000:0F70",
"pred": [
- 217
+ 1847
],
"succ": [
- 223
+ 386,
+ 1859
],
"asm": [
- "B494|mov AH,0x94",
- "9E|sahf",
- "0F879400|ja near 0x02C4"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F855306|jne near 0x15C7"
]
},
{
- "id": 1190,
- "entry": "F000:0FB6",
- "term": "F000:0FCF",
+ "id": 1859,
+ "entry": "F000:0F74",
+ "term": "F000:0F9C",
"pred": [
- 1187
+ 1855
],
"succ": [
- 1197
+ 386,
+ 1870
],
"asm": [
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66B800000000|mov EAX,0",
"66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
"66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "66B940000000|mov ECX,0x00000040",
+ "F366A5|rep movs dword ptr ES:[DI],dword ptr DS:[SI]",
"6683F900|cmp ECX,0",
- "0F85F405|jne near 0x15C7"
+ "0F852706|jne near 0x15C7"
]
},
{
- "id": 223,
- "entry": "F000:02C4",
- "term": "F000:02C4",
+ "id": 1870,
+ "entry": "F000:0FA0",
+ "term": "F000:0FA7",
"pred": [
- 219
+ 1859
],
"succ": [
- 225
+ 386,
+ 1874
],
"asm": [
- "0F8769FF|ja near 0x0231"
+ "6681FE00000100|cmp ESI,0x00010000",
+ "0F851C06|jne near 0x15C7"
]
},
{
- "id": 1187,
+ "id": 1874,
"entry": "F000:0FAB",
"term": "F000:0FB2",
"pred": [
- 1184
+ 1870
],
"succ": [
- 1190
+ 386,
+ 1878
],
"asm": [
"6681FF00000100|cmp EDI,0x00010000",
@@ -2764,1467 +3348,1615 @@
]
},
{
- "id": 225,
- "entry": "F000:0231",
- "term": "F000:0231",
+ "id": 1878,
+ "entry": "F000:0FB6",
+ "term": "F000:0FCF",
"pred": [
- 223
+ 1874
],
"succ": [
- 227
+ 386,
+ 1886
],
"asm": [
- "E99400|jmp near 0x02C8"
+ "66B940000000|mov ECX,0x00000040",
+ "66BF00FF0100|mov EDI,0x0001FF00",
+ "66BE00FF0100|mov ESI,0x0001FF00",
+ "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F85F405|jne near 0x15C7"
]
},
{
- "id": 1184,
- "entry": "F000:0FA0",
- "term": "F000:0FA7",
+ "id": 1886,
+ "entry": "F000:0FD3",
+ "term": "F000:0FDA",
"pred": [
- 1174
+ 1878
],
"succ": [
- 1187
+ 386,
+ 1890
],
"asm": [
"6681FE00000100|cmp ESI,0x00010000",
- "0F851C06|jne near 0x15C7"
+ "0F85E905|jne near 0x15C7"
]
},
{
- "id": 227,
- "entry": "F000:02C8",
- "term": "F000:02CF",
+ "id": 1890,
+ "entry": "F000:0FDE",
+ "term": "F000:0FE5",
"pred": [
- 225
+ 1886
],
"succ": [
- 233
+ 386,
+ 1894
],
"asm": [
- "B400|mov AH,0",
- "9E|sahf",
- "B040|mov AL,0x40",
- "D0E0|shl AL,1",
- "0F81A700|jno near 0x037A"
+ "6681FF00000100|cmp EDI,0x00010000",
+ "0F85DE05|jne near 0x15C7"
]
},
{
- "id": 1174,
- "entry": "F000:0F74",
- "term": "F000:0F9C",
+ "id": 1894,
+ "entry": "F000:0FE9",
+ "term": "F000:101A",
"pred": [
- 1171
+ 1890
],
"succ": [
- 1184
+ 386,
+ 1907
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B800000000|mov EAX,0",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B940000000|mov ECX,0x00000040",
- "F366A5|rep movs dword ptr ES:[DI],dword ptr DS:[SI]",
- "6683F900|cmp ECX,0",
- "0F852706|jne near 0x15C7"
- ]
- },
+ "FD|std",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "6683F900|cmp ECX,0",
+ "0F85A905|jne near 0x15C7"
+ ]
+ },
{
- "id": 233,
- "entry": "F000:02D3",
- "term": "F000:02D3",
+ "id": 1907,
+ "entry": "F000:101E",
+ "term": "F000:1025",
"pred": [
- 227
+ 1894
],
"succ": [
- 235
+ 386,
+ 1911
],
"asm": [
- "0F80A400|jo near 0x037B"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F859E05|jne near 0x15C7"
]
},
{
- "id": 1171,
- "entry": "F000:0F69",
- "term": "F000:0F70",
+ "id": 1911,
+ "entry": "F000:1029",
+ "term": "F000:1056",
"pred": [
- 1164
+ 1907
],
"succ": [
- 1174
+ 386,
+ 1927
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F855306|jne near 0x15C7"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B900010000|mov ECX,0x00000100",
+ "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F856D05|jne near 0x15C7"
]
},
{
- "id": 235,
- "entry": "F000:037B",
- "term": "F000:037B",
+ "id": 1927,
+ "entry": "F000:105A",
+ "term": "F000:1061",
"pred": [
- 233
+ 1911
],
"succ": [
- 237
+ 386,
+ 1931
],
"asm": [
- "0F8059FF|jo near 0x02D8"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F856205|jne near 0x15C7"
]
},
{
- "id": 1164,
- "entry": "F000:0F4C",
- "term": "F000:0F65",
+ "id": 1931,
+ "entry": "F000:1065",
+ "term": "F000:106C",
"pred": [
- 1161
+ 1927
],
"succ": [
- 1171
+ 386,
+ 1935
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66B940000000|mov ECX,0x00000040",
- "F366AF|repe scas EAX,dword ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F855E06|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F855705|jne near 0x15C7"
]
},
{
- "id": 237,
- "entry": "F000:02D8",
- "term": "F000:02D8",
+ "id": 1935,
+ "entry": "F000:1070",
+ "term": "F000:1088",
"pred": [
- 235
+ 1931
],
"succ": [
- 239
+ 386,
+ 1943
],
"asm": [
- "0F8C9E00|jl near 0x037A"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AE|repe scas AL,byte ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F853B05|jne near 0x15C7"
]
},
{
- "id": 1161,
- "entry": "F000:0F41",
- "term": "F000:0F48",
+ "id": 1943,
+ "entry": "F000:108C",
+ "term": "F000:1093",
"pred": [
- 1158
+ 1935
],
"succ": [
- 1164
+ 386,
+ 1947
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F857B06|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F853005|jne near 0x15C7"
]
},
{
- "id": 239,
- "entry": "F000:02DC",
- "term": "F000:02DC",
+ "id": 1947,
+ "entry": "F000:1097",
+ "term": "F000:10BD",
"pred": [
- 237
+ 1943
],
"succ": [
- 241
+ 386,
+ 1958
],
"asm": [
- "0F8D9F00|jge near 0x037F"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B800000000|mov EAX,0",
+ "66B900010000|mov ECX,0x00000100",
+ "F3AA|rep stos byte ptr ES:[DI],AL",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B900010000|mov ECX,0x00000100",
+ "F3A4|rep movs byte ptr ES:[DI],byte ptr DS:[SI]",
+ "6683F900|cmp ECX,0",
+ "0F850605|jne near 0x15C7"
]
},
{
- "id": 1158,
- "entry": "F000:0F36",
- "term": "F000:0F3D",
+ "id": 1958,
+ "entry": "F000:10C1",
+ "term": "F000:10C8",
"pred": [
- 1143
+ 1947
],
"succ": [
- 1161
+ 386,
+ 1962
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F858606|jne near 0x15C7"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85FB04|jne near 0x15C7"
]
},
{
- "id": 241,
- "entry": "F000:037F",
- "term": "F000:037F",
+ "id": 1962,
+ "entry": "F000:10CC",
+ "term": "F000:10D3",
"pred": [
- 239
+ 1958
],
"succ": [
- 243
+ 386,
+ 1966
],
"asm": [
- "0F8D5EFF|jge near 0x02E1"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85F004|jne near 0x15C7"
]
},
{
- "id": 1143,
- "entry": "F000:0F03",
- "term": "F000:0F32",
+ "id": 1966,
+ "entry": "F000:10D7",
+ "term": "F000:10EF",
"pred": [
- 1140
+ 1962
],
"succ": [
- 1158
+ 386,
+ 1974
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B940000000|mov ECX,0x00000040",
- "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "66B900010000|mov ECX,0x00000100",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
"6683F900|cmp ECX,0",
- "0F859106|jne near 0x15C7"
+ "0F85D404|jne near 0x15C7"
]
},
{
- "id": 243,
- "entry": "F000:02E1",
- "term": "F000:02E1",
+ "id": 1974,
+ "entry": "F000:10F3",
+ "term": "F000:10FA",
"pred": [
- 241
+ 1966
],
"succ": [
- 245
+ 386,
+ 1978
],
"asm": [
- "0F8E9500|jle near 0x037A"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85C904|jne near 0x15C7"
]
},
{
- "id": 1140,
- "entry": "F000:0EF8",
- "term": "F000:0EFF",
+ "id": 1978,
+ "entry": "F000:10FE",
+ "term": "F000:1105",
"pred": [
- 1128
+ 1974
],
"succ": [
- 1143
+ 386,
+ 1982
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85C406|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85BE04|jne near 0x15C7"
]
},
{
- "id": 245,
- "entry": "F000:02E5",
- "term": "F000:02E5",
+ "id": 1982,
+ "entry": "F000:1109",
+ "term": "F000:113A",
"pred": [
- 243
+ 1978
],
"succ": [
- 247
+ 386,
+ 1995
],
"asm": [
- "0F8F9A00|jg near 0x0383"
+ "FD|std",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "6683F900|cmp ECX,0",
+ "0F858904|jne near 0x15C7"
]
},
{
- "id": 1128,
- "entry": "F000:0EC2",
- "term": "F000:0EF4",
+ "id": 1995,
+ "entry": "F000:113E",
+ "term": "F000:1145",
"pred": [
- 1125
+ 1982
],
"succ": [
- 1140
+ 386,
+ 1999
],
"asm": [
- "FC|cld",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B940000000|mov ECX,0x00000040",
- "F366AB|rep stos dword ptr ES:[DI],EAX",
- "6683F900|cmp ECX,0",
- "0F85CF06|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F857E04|jne near 0x15C7"
]
},
{
- "id": 247,
- "entry": "F000:0383",
- "term": "F000:0383",
+ "id": 1999,
+ "entry": "F000:1149",
+ "term": "F000:1176",
"pred": [
- 245
+ 1995
],
"succ": [
- 249
+ 386,
+ 2015
],
"asm": [
- "0F8F63FF|jg near 0x02EA"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B980000000|mov ECX,0x00000080",
+ "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F854D04|jne near 0x15C7"
]
},
{
- "id": 1125,
- "entry": "F000:0EB7",
- "term": "F000:0EBE",
+ "id": 2015,
+ "entry": "F000:117A",
+ "term": "F000:1181",
"pred": [
- 1122
+ 1999
],
"succ": [
- 1128
+ 386,
+ 2019
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F850507|jne near 0x15C7"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F854204|jne near 0x15C7"
]
},
{
- "id": 249,
- "entry": "F000:02EA",
- "term": "F000:02ED",
+ "id": 2019,
+ "entry": "F000:1185",
+ "term": "F000:118C",
"pred": [
- 247
+ 2015
],
"succ": [
- 253
+ 386,
+ 2023
],
"asm": [
- "B440|mov AH,0x40",
- "9E|sahf",
- "0F8C9600|jl near 0x0387"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F853704|jne near 0x15C7"
]
},
{
- "id": 1122,
- "entry": "F000:0EAC",
- "term": "F000:0EB3",
+ "id": 2023,
+ "entry": "F000:1190",
+ "term": "F000:11A8",
"pred": [
- 1115
+ 2019
],
"succ": [
- 1125
+ 386,
+ 2031
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F851007|jne near 0x15C7"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66B980000000|mov ECX,0x00000080",
+ "F3AF|repe scas AX,word ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F851B04|jne near 0x15C7"
]
},
{
- "id": 253,
- "entry": "F000:0387",
- "term": "F000:0387",
+ "id": 2031,
+ "entry": "F000:11AC",
+ "term": "F000:11B3",
"pred": [
- 249
+ 2023
],
"succ": [
- 255
+ 386,
+ 2035
],
"asm": [
- "0F8C67FF|jl near 0x02F2"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F851004|jne near 0x15C7"
]
},
{
- "id": 1115,
- "entry": "F000:0E90",
- "term": "F000:0EA8",
+ "id": 2035,
+ "entry": "F000:11B7",
+ "term": "F000:11DD",
"pred": [
- 1112
+ 2031
],
"succ": [
- 1122
+ 386,
+ 2046
],
"asm": [
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B800000000|mov EAX,0",
"66B980000000|mov ECX,0x00000080",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "F3AB|rep stos word ptr ES:[DI],AX",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B980000000|mov ECX,0x00000080",
+ "F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
"6683F900|cmp ECX,0",
- "0F851B07|jne near 0x15C7"
+ "0F85E603|jne near 0x15C7"
]
},
{
- "id": 255,
- "entry": "F000:02F2",
- "term": "F000:02F2",
+ "id": 2046,
+ "entry": "F000:11E1",
+ "term": "F000:11E8",
"pred": [
- 253
+ 2035
],
"succ": [
- 257
+ 386,
+ 2050
],
"asm": [
- "0F8E9500|jle near 0x038B"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85DB03|jne near 0x15C7"
]
},
{
- "id": 1112,
- "entry": "F000:0E85",
- "term": "F000:0E8C",
+ "id": 2050,
+ "entry": "F000:11EC",
+ "term": "F000:11F3",
"pred": [
- 1109
+ 2046
],
"succ": [
- 1115
+ 386,
+ 2054
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F853707|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85D003|jne near 0x15C7"
]
},
{
- "id": 257,
- "entry": "F000:038B",
- "term": "F000:038B",
+ "id": 2054,
+ "entry": "F000:11F7",
+ "term": "F000:120F",
"pred": [
- 255
+ 2050
],
"succ": [
- 259
+ 386,
+ 2062
],
"asm": [
- "0F8E68FF|jle near 0x02F7"
+ "66B980000000|mov ECX,0x00000080",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F85B403|jne near 0x15C7"
]
},
{
- "id": 1109,
- "entry": "F000:0E7A",
- "term": "F000:0E81",
+ "id": 2062,
+ "entry": "F000:1213",
+ "term": "F000:121A",
"pred": [
- 1099
+ 2054
],
"succ": [
- 1112
+ 386,
+ 2066
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F854207|jne near 0x15C7"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85A903|jne near 0x15C7"
]
},
{
- "id": 259,
- "entry": "F000:02F7",
- "term": "F000:02F7",
+ "id": 2066,
+ "entry": "F000:121E",
+ "term": "F000:1225",
"pred": [
- 257
+ 2062
],
"succ": [
- 261
+ 386,
+ 2070
],
"asm": [
- "E99500|jmp near 0x038F"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F859E03|jne near 0x15C7"
]
},
{
- "id": 1099,
- "entry": "F000:0E50",
- "term": "F000:0E76",
+ "id": 2070,
+ "entry": "F000:1229",
+ "term": "F000:125B",
"pred": [
- 1096
+ 2066
],
"succ": [
- 1109
+ 386,
+ 2083
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B800000000|mov EAX,0",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B980000000|mov ECX,0x00000080",
- "F3A5|rep movs word ptr ES:[DI],word ptr DS:[SI]",
+ "FD|std",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B878563412|mov EAX,0x12345678",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
"6683F900|cmp ECX,0",
- "0F854D07|jne near 0x15C7"
+ "0F856803|jne near 0x15C7"
]
},
{
- "id": 261,
- "entry": "F000:038F",
- "term": "F000:0395",
+ "id": 2083,
+ "entry": "F000:125F",
+ "term": "F000:1266",
"pred": [
- 259
+ 2070
],
"succ": [
- 265
+ 386,
+ 2087
],
"asm": [
- "66B900000200|mov ECX,0x00020000",
- "66B800000000|mov EAX,0"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F855D03|jne near 0x15C7"
]
},
{
- "id": 1096,
- "entry": "F000:0E45",
- "term": "F000:0E4C",
+ "id": 2087,
+ "entry": "F000:126A",
+ "term": "F000:1299",
"pred": [
- 1089
+ 2083
],
"succ": [
- 1099
+ 386,
+ 2103
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F857707|jne near 0x15C7"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "8CC2|mov DX,ES",
+ "8CD9|mov CX,DS",
+ "87D1|xchg DX,CX",
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "6687FE|xchg EDI,ESI",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B940000000|mov ECX,0x00000040",
+ "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F852A03|jne near 0x15C7"
]
},
{
- "id": 265,
- "entry": "F000:039B",
- "term": "F000:039D",
+ "id": 2103,
+ "entry": "F000:129D",
+ "term": "F000:12A4",
"pred": [
- 261,
- 265
+ 2087
],
"succ": [
- 265,
- 267
+ 386,
+ 2107
],
"asm": [
- "6640|inc EAX",
- "E2FC|loop 0x039B"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F851F03|jne near 0x15C7"
]
},
{
- "id": 1089,
- "entry": "F000:0E29",
- "term": "F000:0E41",
+ "id": 2107,
+ "entry": "F000:12A8",
+ "term": "F000:12AF",
"pred": [
- 1086
+ 2103
],
"succ": [
- 1096
+ 386,
+ 2111
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66B980000000|mov ECX,0x00000080",
- "F3AF|repe scas AX,word ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F858207|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F851403|jne near 0x15C7"
]
},
{
- "id": 267,
- "entry": "F000:039F",
- "term": "F000:03A5",
+ "id": 2111,
+ "entry": "F000:12B3",
+ "term": "F000:12CC",
"pred": [
- 265
+ 2107
],
"succ": [
- 270
+ 386,
+ 2119
],
"asm": [
- "663D00000100|cmp EAX,0x00010000",
- "0F851E12|jne near 0x15C7"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AF|repe scas EAX,dword ptr ES:[DI]",
+ "6683F900|cmp ECX,0",
+ "0F85F702|jne near 0x15C7"
]
},
{
- "id": 1086,
- "entry": "F000:0E1E",
- "term": "F000:0E25",
+ "id": 2119,
+ "entry": "F000:12D0",
+ "term": "F000:12D7",
"pred": [
- 1083
+ 2111
],
"succ": [
- 1089
+ 386,
+ 2123
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F859E07|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85EC02|jne near 0x15C7"
]
},
{
- "id": 270,
- "entry": "F000:03A9",
- "term": "F000:03B0",
+ "id": 2123,
+ "entry": "F000:12DB",
+ "term": "F000:1303",
"pred": [
- 267
+ 2119
],
"succ": [
- 273
+ 386,
+ 2134
],
"asm": [
- "6681F900000200|cmp ECX,0x00020000",
- "0F851312|jne near 0x15C7"
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B800000000|mov EAX,0",
+ "66B940000000|mov ECX,0x00000040",
+ "F366AB|rep stos dword ptr ES:[DI],EAX",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66B940000000|mov ECX,0x00000040",
+ "F366A5|rep movs dword ptr ES:[DI],dword ptr DS:[SI]",
+ "6683F900|cmp ECX,0",
+ "0F85C002|jne near 0x15C7"
]
},
{
- "id": 1083,
- "entry": "F000:0E13",
- "term": "F000:0E1A",
+ "id": 2134,
+ "entry": "F000:1307",
+ "term": "F000:130E",
"pred": [
- 1068
+ 2123
],
"succ": [
- 1086
+ 386,
+ 2138
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85A907|jne near 0x15C7"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F85B502|jne near 0x15C7"
]
},
{
- "id": 273,
- "entry": "F000:03B4",
- "term": "F000:03BA",
+ "id": 2138,
+ "entry": "F000:1312",
+ "term": "F000:1319",
"pred": [
- 270
+ 2134
],
"succ": [
- 277
+ 386,
+ 2142
],
"asm": [
- "66B900000200|mov ECX,0x00020000",
- "66B800000000|mov EAX,0"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F85AA02|jne near 0x15C7"
]
},
{
- "id": 1068,
- "entry": "F000:0DE2",
- "term": "F000:0E0F",
+ "id": 2142,
+ "entry": "F000:131D",
+ "term": "F000:1336",
"pred": [
- 1065
+ 2138
],
"succ": [
- 1083
+ 386,
+ 2150
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B980000000|mov ECX,0x00000080",
- "F3A7|repe cmps word ptr DS:[SI],word ptr ES:[DI]",
+ "66B940000000|mov ECX,0x00000040",
+ "66BFFF000100|mov EDI,0x000100FF",
+ "66BEFF000100|mov ESI,0x000100FF",
+ "F366A7|repe cmps dword ptr DS:[SI],dword ptr ES:[DI]",
"6683F900|cmp ECX,0",
- "0F85B407|jne near 0x15C7"
+ "0F858D02|jne near 0x15C7"
]
},
{
- "id": 277,
- "entry": "F000:03C0",
- "term": "F000:03C2",
+ "id": 2150,
+ "entry": "F000:133A",
+ "term": "F000:1341",
"pred": [
- 273,
- 277
+ 2142
],
"succ": [
- 277,
- 279
+ 386,
+ 2154
],
"asm": [
- "6640|inc EAX",
- "67E2FB|loop 0x03C0"
+ "6681FEFFFF0100|cmp ESI,0x0001FFFF",
+ "0F858202|jne near 0x15C7"
]
},
{
- "id": 1065,
- "entry": "F000:0DD7",
- "term": "F000:0DDE",
+ "id": 2154,
+ "entry": "F000:1345",
+ "term": "F000:134C",
"pred": [
- 1053
+ 2150
],
"succ": [
- 1068
+ 386,
+ 2158
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85E507|jne near 0x15C7"
+ "6681FFFFFF0100|cmp EDI,0x0001FFFF",
+ "0F857702|jne near 0x15C7"
]
},
{
- "id": 279,
- "entry": "F000:03C5",
- "term": "F000:03CB",
+ "id": 2170,
+ "entry": "F000:136F",
+ "term": "F000:1374",
"pred": [
- 277
+ 2158,
+ 2944
],
"succ": [
- 282
+ 386,
+ 2175
],
"asm": [
- "663D00000200|cmp EAX,0x00020000",
- "0F85F811|jne near 0x15C7"
+ "83E802|sub AX,2",
+ "39C4|cmp SP,AX",
+ "0F854F02|jne near 0x15C7"
]
},
{
- "id": 1053,
- "entry": "F000:0DA2",
- "term": "F000:0DD3",
+ "id": 2914,
+ "entry": "F000:1369",
+ "term": "F000:1369",
"pred": [
- 1050
+ 2158,
+ 2175
],
"succ": [
- 1065
+ 386,
+ 2916
],
"asm": [
- "FC|cld",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B980000000|mov ECX,0x00000080",
- "F3AB|rep stos word ptr ES:[DI],AX",
- "6683F900|cmp ECX,0",
- "0F85F007|jne near 0x15C7"
+ "0F835A02|jae near 0x15C7"
]
},
{
- "id": 282,
- "entry": "F000:03CF",
- "term": "F000:03D3",
+ "id": 2921,
+ "entry": "F000:138D",
+ "term": "F000:1392",
"pred": [
- 279
+ 2918,
+ 2955
],
"succ": [
- 285
+ 386,
+ 2926
],
"asm": [
- "6683F900|cmp ECX,0",
- "0F85F011|jne near 0x15C7"
+ "83E804|sub AX,4",
+ "39C4|cmp SP,AX",
+ "0F853102|jne near 0x15C7"
]
},
{
- "id": 1050,
- "entry": "F000:0D97",
- "term": "F000:0D9E",
+ "id": 2940,
+ "entry": "F000:1387",
+ "term": "F000:1387",
"pred": [
- 1047
+ 2918,
+ 2926
],
"succ": [
- 1053
+ 386,
+ 2942
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F852508|jne near 0x15C7"
+ "0F833C02|jae near 0x15C7"
]
},
{
- "id": 285,
- "entry": "F000:03D7",
- "term": "F000:03DA",
+ "id": 2953,
+ "entry": "F000:13A5",
+ "term": "F000:13A5",
"pred": [
- 282
+ 2175,
+ 2944
],
"succ": [
- 290
+ 386,
+ 2955
],
"asm": [
- "B9FFFF|mov CX,0xFFFF",
- "B80000|mov AX,0"
+ "0F831E02|jae near 0x15C7"
]
},
{
- "id": 1047,
- "entry": "F000:0D8C",
- "term": "F000:0D93",
+ "id": 2963,
+ "entry": "F000:13B3",
+ "term": "F000:13B3",
"pred": [
- 1040
+ 2926,
+ 2955
],
"succ": [
- 1050
+ 386,
+ 2965
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F853008|jne near 0x15C7"
+ "0F831002|jae near 0x15C7"
]
},
{
- "id": 290,
- "entry": "F000:03DD",
- "term": "F000:03E1",
+ "id": 2969,
+ "entry": "F000:13C5",
+ "term": "F000:13CA",
"pred": [
- 285,
- 290
+ 2965,
+ 3018
],
"succ": [
- 290,
- 292
+ 386,
+ 2974
],
"asm": [
- "40|inc AX",
- "80FC00|cmp AH,0",
- "E1FA|loope 0x03DD"
+ "83E804|sub AX,4",
+ "39C4|cmp SP,AX",
+ "0F85F901|jne near 0x15C7"
]
},
{
- "id": 1040,
- "entry": "F000:0D70",
- "term": "F000:0D88",
+ "id": 2988,
+ "entry": "F000:13BF",
+ "term": "F000:13BF",
"pred": [
- 1037
+ 2965,
+ 2974
],
"succ": [
- 1047
+ 386,
+ 2990
],
"asm": [
- "66B900010000|mov ECX,0x00000100",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F853B08|jne near 0x15C7"
+ "0F830402|jae near 0x15C7"
]
},
{
- "id": 292,
- "entry": "F000:03E3",
- "term": "F000:03E6",
+ "id": 2995,
+ "entry": "F000:13E5",
+ "term": "F000:13EA",
"pred": [
- 290
+ 2992,
+ 3031
],
"succ": [
- 295
+ 386,
+ 3000
],
"asm": [
- "3D0001|cmp AX,0x0100",
- "0F85DD11|jne near 0x15C7"
+ "83E808|sub AX,8",
+ "39C4|cmp SP,AX",
+ "0F85D901|jne near 0x15C7"
]
},
{
- "id": 1037,
- "entry": "F000:0D65",
- "term": "F000:0D6C",
+ "id": 3014,
+ "entry": "F000:13DF",
+ "term": "F000:13DF",
"pred": [
- 1034
+ 2992,
+ 3000
],
"succ": [
- 1040
+ 386,
+ 3016
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F855708|jne near 0x15C7"
+ "0F83E401|jae near 0x15C7"
]
},
{
- "id": 295,
- "entry": "F000:03EA",
- "term": "F000:03EE",
+ "id": 3029,
+ "entry": "F000:1403",
+ "term": "F000:1403",
"pred": [
- 292
+ 2974,
+ 3018
],
"succ": [
- 298
+ 386,
+ 3031
],
"asm": [
- "81F9FFFE|cmp CX,0xFEFF",
- "0F85D511|jne near 0x15C7"
+ "0F83C001|jae near 0x15C7"
]
},
{
- "id": 1034,
- "entry": "F000:0D5A",
- "term": "F000:0D61",
+ "id": 3041,
+ "entry": "F000:1417",
+ "term": "F000:1417",
"pred": [
- 1024
+ 3000,
+ 3031
],
"succ": [
- 1037
+ 386,
+ 3043
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F856208|jne near 0x15C7"
+ "0F83AC01|jae near 0x15C7"
]
},
{
- "id": 298,
- "entry": "F000:03F2",
- "term": "F000:03F5",
+ "id": 3043,
+ "entry": "F000:141B",
+ "term": "F000:1446",
"pred": [
- 295
+ 3041
],
"succ": [
- 303
+ 386,
+ 3061
],
"asm": [
- "B9FF00|mov CX,0x00FF",
- "B80000|mov AX,0"
+ "BA0023|mov DX,0x2300",
+ "8EDA|mov DS,DX",
+ "BA0063|mov DX,0x6300",
+ "8EC2|mov ES,DX",
+ "B006|mov AL,6",
+ "BA9909|mov DX,0x0999",
+ "EE|out DX,AL",
+ "BF0000|mov DI,0",
+ "8CD1|mov CX,SS",
+ "8CC2|mov DX,ES",
+ "26C7053412|mov word ptr ES:[DI],0x1234",
+ "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
+ "260FB21D|lss BX,word ptr ES:[DI]",
+ "8CD0|mov AX,SS",
+ "3DCDAB|cmp AX,0xABCD",
+ "0F857D01|jne near 0x15C7"
]
},
{
- "id": 1024,
- "entry": "F000:0D30",
- "term": "F000:0D56",
+ "id": 3061,
+ "entry": "F000:144A",
+ "term": "F000:144E",
"pred": [
- 1021
+ 3043
],
"succ": [
- 1034
+ 386,
+ 3065
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B800000000|mov EAX,0",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B900010000|mov ECX,0x00000100",
- "F3A4|rep movs byte ptr ES:[DI],byte ptr DS:[SI]",
- "6683F900|cmp ECX,0",
- "0F856D08|jne near 0x15C7"
+ "81FB3412|cmp BX,0x1234",
+ "0F857501|jne near 0x15C7"
]
},
{
- "id": 303,
- "entry": "F000:03F8",
- "term": "F000:03FC",
+ "id": 3065,
+ "entry": "F000:1452",
+ "term": "F000:146C",
"pred": [
- 298,
- 303
+ 3061
],
"succ": [
- 303,
- 305
+ 386,
+ 3074
],
"asm": [
- "40|inc AX",
- "80FC00|cmp AH,0",
- "E1FA|loope 0x03F8"
+ "8EC2|mov ES,DX",
+ "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
+ "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
+ "26660FB21D|lss EBX,dword ptr ES:[DI]",
+ "8CD0|mov AX,SS",
+ "3DDEBC|cmp AX,0xBCDE",
+ "0F855701|jne near 0x15C7"
]
},
{
- "id": 1021,
- "entry": "F000:0D25",
- "term": "F000:0D2C",
+ "id": 3074,
+ "entry": "F000:1470",
+ "term": "F000:1477",
"pred": [
- 1014
+ 3065
],
"succ": [
- 1024
+ 386,
+ 3078
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F859708|jne near 0x15C7"
+ "6681FB78563412|cmp EBX,0x12345678",
+ "0F854C01|jne near 0x15C7"
]
},
{
- "id": 305,
- "entry": "F000:03FE",
- "term": "F000:0401",
+ "id": 3078,
+ "entry": "F000:147B",
+ "term": "F000:1496",
"pred": [
- 303
+ 3074
],
"succ": [
- 308
+ 386,
+ 3090
],
"asm": [
- "3DFF00|cmp AX,0x00FF",
- "0F85C211|jne near 0x15C7"
+ "8EC2|mov ES,DX",
+ "8ED1|mov SS,CX",
+ "8CD9|mov CX,DS",
+ "8CC2|mov DX,ES",
+ "26C7053412|mov word ptr ES:[DI],0x1234",
+ "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
+ "26C51D|lds BX,word ptr ES:[DI]",
+ "8CD8|mov AX,DS",
+ "3DCDAB|cmp AX,0xABCD",
+ "0F852D01|jne near 0x15C7"
]
},
{
- "id": 1014,
- "entry": "F000:0D09",
- "term": "F000:0D21",
+ "id": 3090,
+ "entry": "F000:149A",
+ "term": "F000:149E",
"pred": [
- 1011
+ 3078
],
"succ": [
- 1021
+ 386,
+ 3094
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66B900010000|mov ECX,0x00000100",
- "F3AE|repe scas AL,byte ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F85A208|jne near 0x15C7"
+ "81FB3412|cmp BX,0x1234",
+ "0F852501|jne near 0x15C7"
]
},
{
- "id": 308,
- "entry": "F000:0405",
- "term": "F000:0408",
+ "id": 3094,
+ "entry": "F000:14A2",
+ "term": "F000:14BB",
"pred": [
- 305
+ 3090
],
"succ": [
- 311
+ 386,
+ 3103
],
"asm": [
- "83F900|cmp CX,0",
- "0F85BB11|jne near 0x15C7"
+ "8EC2|mov ES,DX",
+ "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
+ "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
+ "2666C51D|lds EBX,dword ptr ES:[DI]",
+ "8CD8|mov AX,DS",
+ "3DDEBC|cmp AX,0xBCDE",
+ "0F850801|jne near 0x15C7"
]
},
{
- "id": 1011,
- "entry": "F000:0CFE",
- "term": "F000:0D05",
+ "id": 3103,
+ "entry": "F000:14BF",
+ "term": "F000:14C6",
"pred": [
- 1008
+ 3094
],
"succ": [
- 1014
+ 386,
+ 3107
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85BE08|jne near 0x15C7"
+ "6681FB78563412|cmp EBX,0x12345678",
+ "0F85FD00|jne near 0x15C7"
]
},
{
- "id": 311,
- "entry": "F000:040C",
- "term": "F000:0412",
+ "id": 3107,
+ "entry": "F000:14CA",
+ "term": "F000:14E5",
"pred": [
- 308
+ 3103
],
"succ": [
- 316
+ 386,
+ 3119
],
"asm": [
- "66B900000200|mov ECX,0x00020000",
- "66B800000000|mov EAX,0"
+ "8EC2|mov ES,DX",
+ "8ED9|mov DS,CX",
+ "8CC1|mov CX,ES",
+ "8CC2|mov DX,ES",
+ "26C7053412|mov word ptr ES:[DI],0x1234",
+ "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
+ "26C41D|les BX,word ptr ES:[DI]",
+ "8CC0|mov AX,ES",
+ "3DCDAB|cmp AX,0xABCD",
+ "0F85DE00|jne near 0x15C7"
]
},
{
- "id": 1008,
- "entry": "F000:0CF3",
- "term": "F000:0CFA",
+ "id": 3119,
+ "entry": "F000:14E9",
+ "term": "F000:14ED",
"pred": [
- 993
+ 3107
],
"succ": [
- 1011
+ 386,
+ 3123
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85C908|jne near 0x15C7"
+ "81FB3412|cmp BX,0x1234",
+ "0F85D600|jne near 0x15C7"
]
},
{
- "id": 316,
- "entry": "F000:0418",
- "term": "F000:0420",
+ "id": 3123,
+ "entry": "F000:14F1",
+ "term": "F000:150A",
"pred": [
- 311,
- 316
+ 3119
],
"succ": [
- 316,
- 318
+ 386,
+ 3132
],
"asm": [
- "6640|inc EAX",
- "66A900000100|test EAX,0x00010000",
- "67E1F5|loope 0x0418"
+ "8EC2|mov ES,DX",
+ "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
+ "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
+ "2666C41D|les EBX,dword ptr ES:[DI]",
+ "8CC0|mov AX,ES",
+ "3DDEBC|cmp AX,0xBCDE",
+ "0F85B900|jne near 0x15C7"
]
},
{
- "id": 993,
- "entry": "F000:0CC2",
- "term": "F000:0CEF",
+ "id": 3132,
+ "entry": "F000:150E",
+ "term": "F000:1515",
"pred": [
- 990
+ 3123
],
"succ": [
- 1008
+ 386,
+ 3136
],
"asm": [
- "66BF00FF0100|mov EDI,0x0001FF00",
- "8CC2|mov DX,ES",
- "8CD9|mov CX,DS",
- "87D1|xchg DX,CX",
- "8EC2|mov ES,DX",
- "8ED9|mov DS,CX",
- "6687FE|xchg EDI,ESI",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B900010000|mov ECX,0x00000100",
- "F3A6|repe cmps byte ptr DS:[SI],byte ptr ES:[DI]",
- "6683F900|cmp ECX,0",
- "0F85D408|jne near 0x15C7"
+ "6681FB78563412|cmp EBX,0x12345678",
+ "0F85AE00|jne near 0x15C7"
]
},
{
- "id": 318,
- "entry": "F000:0423",
- "term": "F000:0429",
+ "id": 3136,
+ "entry": "F000:1519",
+ "term": "F000:1535",
"pred": [
- 316
+ 3132
],
"succ": [
- 321
+ 386,
+ 3148
],
"asm": [
- "663D00000100|cmp EAX,0x00010000",
- "0F859A11|jne near 0x15C7"
+ "8EC2|mov ES,DX",
+ "8EC1|mov ES,CX",
+ "8CE1|mov CX,FS",
+ "8CC2|mov DX,ES",
+ "26C7053412|mov word ptr ES:[DI],0x1234",
+ "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
+ "260FB41D|lfs BX,word ptr ES:[DI]",
+ "8CE0|mov AX,FS",
+ "3DCDAB|cmp AX,0xABCD",
+ "0F858E00|jne near 0x15C7"
]
},
{
- "id": 990,
- "entry": "F000:0CB7",
- "term": "F000:0CBE",
- "pred": [
- 978
+ "id": 3148,
+ "entry": "F000:1539",
+ "term": "F000:153D",
+ "pred": [
+ 3136
],
"succ": [
- 993
+ 386,
+ 3152
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F850509|jne near 0x15C7"
+ "81FB3412|cmp BX,0x1234",
+ "0F858600|jne near 0x15C7"
]
},
{
- "id": 321,
- "entry": "F000:042D",
- "term": "F000:0434",
+ "id": 3152,
+ "entry": "F000:1541",
+ "term": "F000:155B",
"pred": [
- 318
+ 3148
],
"succ": [
- 324
+ 386,
+ 3161
],
"asm": [
- "6681F900000100|cmp ECX,0x00010000",
- "0F858F11|jne near 0x15C7"
+ "8EC2|mov ES,DX",
+ "2666C70578563412|mov dword ptr ES:[DI],0x12345678",
+ "26C74504DEBC|mov word ptr ES:[DI\u002B4],0xBCDE",
+ "26660FB41D|lfs EBX,dword ptr ES:[DI]",
+ "8CE0|mov AX,FS",
+ "3DDEBC|cmp AX,0xBCDE",
+ "756A|jne short 0x15C7"
]
},
{
- "id": 978,
- "entry": "F000:0C82",
- "term": "F000:0CB3",
+ "id": 3161,
+ "entry": "F000:155D",
+ "term": "F000:1564",
"pred": [
- 975
+ 3152
],
"succ": [
- 990
+ 386,
+ 3165
],
"asm": [
- "FC|cld",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B878563412|mov EAX,0x12345678",
- "66BE00FF0100|mov ESI,0x0001FF00",
- "66BF00FF0100|mov EDI,0x0001FF00",
- "66B900010000|mov ECX,0x00000100",
- "F3AA|rep stos byte ptr ES:[DI],AL",
- "6683F900|cmp ECX,0",
- "0F851009|jne near 0x15C7"
+ "6681FB78563412|cmp EBX,0x12345678",
+ "7561|jne short 0x15C7"
]
},
{
- "id": 324,
- "entry": "F000:0438",
- "term": "F000:043B",
+ "id": 3165,
+ "entry": "F000:1566",
+ "term": "F000:1582",
"pred": [
- 321
+ 3161
],
"succ": [
- 329
+ 386,
+ 3177
],
"asm": [
- "B9FFFF|mov CX,0xFFFF",
- "B80000|mov AX,0"
+ "8EC2|mov ES,DX",
+ "8EE1|mov FS,CX",
+ "8CE9|mov CX,GS",
+ "8CC2|mov DX,ES",
+ "26C7053412|mov word ptr ES:[DI],0x1234",
+ "26C74502CDAB|mov word ptr ES:[DI\u002B2],0xABCD",
+ "260FB51D|lgs BX,word ptr ES:[DI]",
+ "8CE8|mov AX,GS",
+ "3DCDAB|cmp AX,0xABCD",
+ "7543|jne short 0x15C7"
]
},
{
- "id": 975,
- "entry": "F000:0C77",
- "term": "F000:0C7E",
+ "id": 3177,
+ "entry": "F000:1584",
+ "term": "F000:1588",
"pred": [
- 967
+ 3165
],
"succ": [
- 978
+ 386,
+ 3181
],
"asm": [
- "6681FEFCFF0100|cmp ESI,0x0001FFFC",
- "0F854509|jne near 0x15C7"
+ "81FB3412|cmp BX,0x1234",
+ "753D|jne short 0x15C7"
]
},
{
- "id": 329,
- "entry": "F000:043E",
- "term": "F000:0441",
+ "id": 31,
+ "entry": "F000:00AC",
+ "dead": true,
+ "term": "F000:00AC",
"pred": [
- 324,
- 329
- ],
- "succ": [
- 329,
- 331
+ 17,
+ 39,
+ 44,
+ 50,
+ 59
],
+ "succ": [],
"asm": [
- "40|inc AX",
- "A8FF|test AL,0xFF",
- "E0FB|loopne 0x043E"
+ "F4|hlt"
]
},
{
- "id": 967,
- "entry": "F000:0C57",
- "term": "F000:0C73",
+ "id": 33,
+ "entry": "F000:0088",
+ "term": "F000:0088",
"pred": [
- 964
+ 17
],
"succ": [
- 975
+ 35,
+ 37
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66B878563412|mov EAX,0x12345678",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6631C0|xor EAX,EAX",
- "66AD|lods EAX,dword ptr DS:[SI]",
- "663D78563412|cmp EAX,0x12345678",
- "0F855009|jne near 0x15C7"
+ "7223|jb short 0x00AD"
]
},
{
- "id": 331,
- "entry": "F000:0443",
- "term": "F000:0446",
+ "id": 381,
+ "entry": "F000:039B",
+ "term": "F000:039D",
"pred": [
- 329
+ 372,
+ 381
],
"succ": [
- 334
+ 381,
+ 383
],
"asm": [
- "3D0001|cmp AX,0x0100",
- "0F857D11|jne near 0x15C7"
+ "6640|inc EAX",
+ "E2FC|loop 0x039B"
]
},
{
- "id": 964,
- "entry": "F000:0C4C",
- "term": "F000:0C53",
+ "id": 394,
+ "entry": "F000:03B4",
+ "term": "F000:03BA",
"pred": [
- 961
+ 388
],
"succ": [
- 967
+ 405
],
"asm": [
- "6681FEFCFF0100|cmp ESI,0x0001FFFC",
- "0F857009|jne near 0x15C7"
+ "66B900000200|mov ECX,0x00020000",
+ "66B800000000|mov EAX,0"
]
},
{
- "id": 334,
- "entry": "F000:044A",
- "term": "F000:044E",
+ "id": 405,
+ "entry": "F000:03C0",
+ "term": "F000:03C2",
"pred": [
- 331
+ 394,
+ 405
],
"succ": [
- 337
+ 405,
+ 407
],
"asm": [
- "81F9FFFE|cmp CX,0xFEFF",
- "0F857511|jne near 0x15C7"
+ "6640|inc EAX",
+ "67E2FB|loop 0x03C0"
]
},
{
- "id": 961,
- "entry": "F000:0C41",
- "term": "F000:0C48",
+ "id": 415,
+ "entry": "F000:03D7",
+ "term": "F000:03DA",
"pred": [
- 950
+ 411
],
"succ": [
- 964
+ 421
],
"asm": [
- "6681FFFCFF0100|cmp EDI,0x0001FFFC",
- "0F857B09|jne near 0x15C7"
+ "B9FFFF|mov CX,0xFFFF",
+ "B80000|mov AX,0"
]
},
{
- "id": 337,
- "entry": "F000:0452",
- "term": "F000:0455",
+ "id": 421,
+ "entry": "F000:03DD",
+ "term": "F000:03E1",
"pred": [
- 334
+ 415,
+ 421
],
"succ": [
- 342
+ 421,
+ 423
],
"asm": [
- "B9FF00|mov CX,0x00FF",
- "B80000|mov AX,0"
+ "40|inc AX",
+ "80FC00|cmp AH,0",
+ "E1FA|loope 0x03DD"
]
},
{
- "id": 950,
- "entry": "F000:0C0E",
- "term": "F000:0C3D",
+ "id": 431,
+ "entry": "F000:03F2",
+ "term": "F000:03F5",
"pred": [
- 947
+ 427
],
"succ": [
- 961
+ 437
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "66B878563412|mov EAX,0x12345678",
- "3E66678903|mov dword ptr DS:[EBX],EAX",
- "66B800000000|mov EAX,0",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "66A5|movs dword ptr ES:[DI],dword ptr DS:[SI]",
- "66B878563412|mov EAX,0x12345678",
- "2666673903|cmp dword ptr ES:[EBX],EAX",
- "0F858609|jne near 0x15C7"
+ "B9FF00|mov CX,0x00FF",
+ "B80000|mov AX,0"
]
},
{
- "id": 342,
- "entry": "F000:0458",
- "term": "F000:045B",
+ "id": 437,
+ "entry": "F000:03F8",
+ "term": "F000:03FC",
"pred": [
- 337,
- 342
+ 431,
+ 437
],
"succ": [
- 342,
- 344
+ 437,
+ 439
],
"asm": [
"40|inc AX",
- "A8FF|test AL,0xFF",
- "E0FB|loopne 0x0458"
+ "80FC00|cmp AH,0",
+ "E1FA|loope 0x03F8"
]
},
{
- "id": 947,
- "entry": "F000:0C03",
- "term": "F000:0C0A",
+ "id": 447,
+ "entry": "F000:040C",
+ "term": "F000:0412",
"pred": [
- 940
+ 443
],
"succ": [
- 950
+ 453
],
"asm": [
- "6681FFFCFF0100|cmp EDI,0x0001FFFC",
- "0F85B909|jne near 0x15C7"
+ "66B900000200|mov ECX,0x00020000",
+ "66B800000000|mov EAX,0"
]
},
{
- "id": 344,
- "entry": "F000:045D",
- "term": "F000:0460",
+ "id": 453,
+ "entry": "F000:0418",
+ "term": "F000:0420",
"pred": [
- 342
+ 447,
+ 453
],
"succ": [
- 347
+ 453,
+ 455
],
"asm": [
- "3DFF00|cmp AX,0x00FF",
- "0F856311|jne near 0x15C7"
+ "6640|inc EAX",
+ "66A900000100|test EAX,0x00010000",
+ "67E1F5|loope 0x0418"
]
},
{
- "id": 940,
- "entry": "F000:0BE8",
- "term": "F000:0BFF",
+ "id": 463,
+ "entry": "F000:0438",
+ "term": "F000:043B",
"pred": [
- 937
+ 459
],
"succ": [
- 947
+ 469
],
"asm": [
- "66BF00000100|mov EDI,0x00010000",
- "66B878563412|mov EAX,0x12345678",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6683F800|cmp EAX,0",
- "66AF|scas EAX,dword ptr ES:[DI]",
- "0F85C409|jne near 0x15C7"
+ "B9FFFF|mov CX,0xFFFF",
+ "B80000|mov AX,0"
]
},
{
- "id": 347,
- "entry": "F000:0464",
- "term": "F000:0467",
+ "id": 469,
+ "entry": "F000:043E",
+ "term": "F000:0441",
"pred": [
- 344
+ 463,
+ 469
],
"succ": [
- 350
+ 469,
+ 471
],
"asm": [
- "83F900|cmp CX,0",
- "0F855C11|jne near 0x15C7"
+ "40|inc AX",
+ "A8FF|test AL,0xFF",
+ "E0FB|loopne 0x043E"
]
},
{
- "id": 937,
- "entry": "F000:0BDD",
- "term": "F000:0BE4",
+ "id": 479,
+ "entry": "F000:0452",
+ "term": "F000:0455",
"pred": [
- 934
+ 475
],
"succ": [
- 940
+ 485
],
"asm": [
- "6681FEFCFF0100|cmp ESI,0x0001FFFC",
- "0F85DF09|jne near 0x15C7"
+ "B9FF00|mov CX,0x00FF",
+ "B80000|mov AX,0"
]
},
{
- "id": 350,
- "entry": "F000:046B",
- "term": "F000:0471",
+ "id": 485,
+ "entry": "F000:0458",
+ "term": "F000:045B",
"pred": [
- 347
+ 479,
+ 485
],
"succ": [
- 355
+ 485,
+ 487
],
"asm": [
- "66B900000100|mov ECX,0x00010000",
- "66B800000000|mov EAX,0"
+ "40|inc AX",
+ "A8FF|test AL,0xFF",
+ "E0FB|loopne 0x0458"
]
},
{
- "id": 934,
- "entry": "F000:0BD2",
- "term": "F000:0BD9",
+ "id": 495,
+ "entry": "F000:046B",
+ "term": "F000:0471",
"pred": [
- 931
+ 491
],
"succ": [
- 937
+ 501
],
"asm": [
- "6681FFFCFF0100|cmp EDI,0x0001FFFC",
- "0F85EA09|jne near 0x15C7"
+ "66B900000100|mov ECX,0x00010000",
+ "66B800000000|mov EAX,0"
]
},
{
- "id": 355,
+ "id": 501,
"entry": "F000:0477",
"term": "F000:047F",
"pred": [
- 350,
- 355
+ 495,
+ 501
],
"succ": [
- 355,
- 357
+ 501,
+ 503
],
"asm": [
"6640|inc EAX",
@@ -4233,1790 +4965,2119 @@
]
},
{
- "id": 931,
- "entry": "F000:0BCC",
- "term": "F000:0BCE",
+ "id": 674,
+ "entry": "F000:0612",
+ "term": "F000:062E",
"pred": [
- 923
+ 665
],
"succ": [
- 934
+ 1055
],
"asm": [
- "66A7|cmps dword ptr DS:[SI],dword ptr ES:[DI]",
- "0F85F509|jne near 0x15C7"
+ "BA00F0|mov DX,0xF000",
+ "B80000|mov AX,0",
+ "8ED8|mov DS,AX",
+ "C70618003306|mov word ptr DS:[0x0018],0x0633",
+ "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
+ "B80010|mov AX,0x1000",
+ "8ED0|mov SS,AX",
+ "BCFFFF|mov SP,0xFFFF",
+ "8ECA|invalid"
]
},
{
- "id": 357,
- "entry": "F000:0482",
- "term": "F000:0488",
+ "id": 1085,
+ "entry": "F000:0688",
+ "term": "F000:06A1",
"pred": [
- 355
+ 1079
],
"succ": [
- 360
+ 1125
],
"asm": [
- "663D00000100|cmp EAX,0x00010000",
- "0F853B11|jne near 0x15C7"
+ "B80000|mov AX,0",
+ "8ED8|mov DS,AX",
+ "C7061800A806|mov word ptr DS:[0x0018],0x06A8",
+ "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
+ "B80010|mov AX,0x1000",
+ "8ED0|mov SS,AX",
+ "BCFFFF|mov SP,0xFFFF",
+ "8E0E0000|invalid"
]
},
{
- "id": 923,
- "entry": "F000:0BA8",
- "term": "F000:0BC8",
+ "id": 2158,
+ "entry": "F000:1350",
+ "term": "F000:1366",
"pred": [
- 920
+ 2154
],
"succ": [
- 931
+ 2170,
+ 2914
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
- "3E66678903|mov dword ptr DS:[EBX],EAX",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6683F800|cmp EAX,0",
- "0F84FB09|je near 0x15C7"
+ "BA0022|mov DX,0x2200",
+ "8EDA|mov DS,DX",
+ "BA0062|mov DX,0x6200",
+ "8EC2|mov ES,DX",
+ "B005|mov AL,5",
+ "BA9909|mov DX,0x0999",
+ "EE|out DX,AL",
+ "BE0000|mov SI,0",
+ "89E0|mov AX,SP",
+ "F8|clc",
+ "E80600|call near 0x136F"
]
},
{
- "id": 360,
- "entry": "F000:048C",
- "term": "F000:0490",
+ "id": 2175,
+ "entry": "F000:1378",
+ "term": "F000:137C",
"pred": [
- 357
+ 2170
],
"succ": [
- 363
+ 2914,
+ 2953
],
"asm": [
- "6683F900|cmp ECX,0",
- "0F853311|jne near 0x15C7"
+ "83C002|add AX,2",
+ "F9|stc",
+ "C3|ret near"
]
},
{
- "id": 920,
- "entry": "F000:0B9D",
- "term": "F000:0BA4",
+ "id": 2944,
+ "entry": "F000:139F",
+ "term": "F000:13A3",
"pred": [
- 910
+ 2942
],
"succ": [
- 923
+ 2170,
+ 2953
],
"asm": [
- "6681FFFCFF0100|cmp EDI,0x0001FFFC",
- "0F851F0A|jne near 0x15C7"
+ "F8|clc",
+ "BB6F13|mov BX,0x136F",
+ "FFD3|call near BX"
]
},
{
- "id": 363,
- "entry": "F000:0494",
- "term": "F000:04BB",
+ "id": 2916,
+ "entry": "F000:136D",
+ "term": "F000:136D",
"pred": [
- 360
+ 2914
],
"succ": [
- 376
+ 2918
],
"asm": [
- "B002|mov AL,2",
- "BA9909|mov DX,0x0999",
- "EE|out DX,AL",
- "66B801000080|mov EAX,0x80000001",
- "66F7E8|imul EAX",
- "66B811223344|mov EAX,0x44332211",
- "6689C3|mov EBX,EAX",
- "66B955667788|mov ECX,0x88776655",
- "66F7E1|mul ECX",
- "66F7F1|div ECX",
- "6639D8|cmp EAX,EBX",
- "0F850811|jne near 0x15C7"
+ "EB11|jmp short 0x1380"
]
},
{
- "id": 910,
- "entry": "F000:0B74",
- "term": "F000:0B99",
+ "id": 2926,
+ "entry": "F000:1396",
+ "term": "F000:139A",
"pred": [
- 907
+ 2921
],
"succ": [
- 920
+ 2940,
+ 2963
],
"asm": [
- "FD|std",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
- "66B800000000|mov EAX,0",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "66B878563412|mov EAX,0x12345678",
- "66AB|stos dword ptr ES:[DI],EAX",
- "2666673903|cmp dword ptr ES:[EBX],EAX",
- "0F852A0A|jne near 0x15C7"
+ "83C004|add AX,4",
+ "F9|stc",
+ "66C3|ret near"
]
},
{
- "id": 376,
- "entry": "F000:04BF",
- "term": "F000:04D0",
+ "id": 2918,
+ "entry": "F000:1380",
+ "term": "F000:1381",
"pred": [
- 363
+ 2916
],
"succ": [
- 386
+ 2921,
+ 2940
],
"asm": [
- "B003|mov AL,3",
- "BA9909|mov DX,0x0999",
- "EE|out DX,AL",
- "BA0020|mov DX,0x2000",
- "8ED2|mov SS,DX",
- "31C0|xor AX,AX",
- "8CD0|mov AX,SS",
- "39D0|cmp AX,DX",
- "0F85F310|jne near 0x15C7"
+ "F8|clc",
+ "66E806000000|call near 0x138D"
]
},
{
- "id": 907,
- "entry": "F000:0B69",
- "term": "F000:0B70",
+ "id": 2955,
+ "entry": "F000:13A9",
+ "term": "F000:13B0",
"pred": [
- 899
+ 2953
],
"succ": [
- 910
+ 2921,
+ 2963
],
"asm": [
- "6681FEFEFF0100|cmp ESI,0x0001FFFE",
- "0F85530A|jne near 0x15C7"
+ "F8|clc",
+ "66BB8D130000|mov EBX,0x0000138D",
+ "66FFD3|call near EBX"
]
},
{
- "id": 386,
- "entry": "F000:04D4",
- "term": "F000:04DF",
+ "id": 2942,
+ "entry": "F000:138B",
+ "term": "F000:138B",
"pred": [
- 376
+ 2940
],
"succ": [
- 391
+ 2944
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CD0|mov EAX,SS",
- "39D0|cmp AX,DX",
- "0F85E410|jne near 0x15C7"
+ "EB12|jmp short 0x139F"
]
},
{
- "id": 899,
- "entry": "F000:0B51",
- "term": "F000:0B65",
+ "id": 2965,
+ "entry": "F000:13B7",
+ "term": "F000:13BA",
"pred": [
- 896
+ 2963
],
"succ": [
- 907
+ 2969,
+ 2988
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "B87856|mov AX,0x5678",
- "26678903|mov word ptr ES:[EBX],AX",
- "6631C0|xor EAX,EAX",
- "AD|lods AX,word ptr DS:[SI]",
- "3D7856|cmp AX,0x5678",
- "0F855E0A|jne near 0x15C7"
+ "89E0|mov AX,SP",
+ "F8|clc",
+ "9AC51300F0|call far F000:13C5"
]
},
{
- "id": 391,
- "entry": "F000:04E3",
- "term": "F000:04F1",
+ "id": 2974,
+ "entry": "F000:13CE",
+ "term": "F000:13D2",
"pred": [
- 386
+ 2969
],
"succ": [
- 396
+ 2988,
+ 3029
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C160000|mov word ptr DS:[0],SS",
- "39160000|cmp word ptr DS:[0],DX",
- "0F85D210|jne near 0x15C7"
+ "83C004|add AX,4",
+ "F9|stc",
+ "CB|ret far"
]
},
{
- "id": 896,
- "entry": "F000:0B46",
- "term": "F000:0B4D",
+ "id": 3018,
+ "entry": "F000:13F7",
+ "term": "F000:1401",
"pred": [
- 893
+ 3016
],
"succ": [
- 899
+ 2969,
+ 3029
],
"asm": [
- "6681FEFEFF0100|cmp ESI,0x0001FFFE",
- "0F85760A|jne near 0x15C7"
+ "F8|clc",
+ "C704C513|mov word ptr DS:[SI],0x13C5",
+ "C7440200F0|mov word ptr DS:[SI\u002B2],0xF000",
+ "FF1C|call far dword ptr DS:[SI]"
]
},
{
- "id": 396,
- "entry": "F000:04F5",
- "term": "F000:0503",
+ "id": 2990,
+ "entry": "F000:13C3",
+ "term": "F000:13C3",
"pred": [
- 391
+ 2988
],
"succ": [
- 404
+ 2992
],
"asm": [
- "8CD9|mov CX,DS",
- "31C0|xor AX,AX",
- "8ED0|mov SS,AX",
- "8E160000|mov SS,word ptr DS:[0]",
- "8CD0|mov AX,SS",
- "39D0|cmp AX,DX",
- "0F85C010|jne near 0x15C7"
+ "EB11|jmp short 0x13D6"
]
},
{
- "id": 893,
- "entry": "F000:0B3B",
- "term": "F000:0B42",
+ "id": 3000,
+ "entry": "F000:13EE",
+ "term": "F000:13F2",
"pred": [
- 882
+ 2995
],
"succ": [
- 896
+ 3014,
+ 3041
],
"asm": [
- "6681FFFEFF0100|cmp EDI,0x0001FFFE",
- "0F85810A|jne near 0x15C7"
+ "83C008|add AX,8",
+ "F9|stc",
+ "66CB|ret far"
]
},
{
- "id": 404,
- "entry": "F000:0507",
- "term": "F000:0512",
+ "id": 2992,
+ "entry": "F000:13D6",
+ "term": "F000:13D7",
"pred": [
- 396
+ 2990
],
"succ": [
- 411
+ 2995,
+ 3014
],
"asm": [
- "BA0020|mov DX,0x2000",
- "8EDA|mov DS,DX",
- "31C0|xor AX,AX",
- "8CD8|mov AX,DS",
- "39D0|cmp AX,DX",
- "0F85B110|jne near 0x15C7"
+ "F8|clc",
+ "669AE513000000F0|call far F000:13E5"
]
},
{
- "id": 882,
- "entry": "F000:0B15",
- "term": "F000:0B37",
+ "id": 3031,
+ "entry": "F000:1407",
+ "term": "F000:1414",
"pred": [
- 879
+ 3029
],
"succ": [
- 893
+ 2995,
+ 3041
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "B87856|mov AX,0x5678",
- "3E678903|mov word ptr DS:[EBX],AX",
- "B80000|mov AX,0",
- "26678903|mov word ptr ES:[EBX],AX",
- "A5|movs word ptr ES:[DI],word ptr DS:[SI]",
- "B87856|mov AX,0x5678",
- "26673903|cmp word ptr ES:[EBX],AX",
- "0F858C0A|jne near 0x15C7"
+ "F8|clc",
+ "66C704E5130000|mov dword ptr DS:[SI],0x000013E5",
+ "C7440400F0|mov word ptr DS:[SI\u002B4],0xF000",
+ "66FF1C|call far dword ptr DS:[SI]"
]
},
{
- "id": 411,
- "entry": "F000:0516",
- "term": "F000:0521",
+ "id": 3016,
+ "entry": "F000:13E3",
+ "term": "F000:13E3",
"pred": [
- 404
+ 3014
],
"succ": [
- 416
+ 3018
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CD8|mov EAX,DS",
- "39D0|cmp AX,DX",
- "0F85A210|jne near 0x15C7"
+ "EB12|jmp short 0x13F7"
]
},
{
- "id": 879,
- "entry": "F000:0B0A",
- "term": "F000:0B11",
+ "id": 39,
+ "entry": "F000:008B",
+ "term": "F000:008E",
"pred": [
- 872
+ 35
],
"succ": [
- 882
+ 31,
+ 55
],
"asm": [
- "6681FFFEFF0100|cmp EDI,0x0001FFFE",
- "0F85B20A|jne near 0x15C7"
+ "B440|mov AH,0x40",
+ "9E|sahf",
+ "751C|jne short 0x00AC"
]
},
{
- "id": 416,
- "entry": "F000:0525",
- "term": "F000:0533",
+ "id": 44,
+ "entry": "F000:0093",
+ "term": "F000:0096",
"pred": [
- 411
+ 41
],
"succ": [
- 421
+ 31,
+ 67
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C1E0000|mov word ptr DS:[0],DS",
- "39160000|cmp word ptr DS:[0],DX",
- "0F859010|jne near 0x15C7"
+ "B404|mov AH,4",
+ "9E|sahf",
+ "7B14|jnp short 0x00AC"
]
},
{
- "id": 872,
- "entry": "F000:0AF5",
- "term": "F000:0B06",
+ "id": 50,
+ "entry": "F000:009B",
+ "term": "F000:009E",
"pred": [
- 869
+ 46
],
"succ": [
- 879
+ 31,
+ 79
],
"asm": [
- "66BF00000100|mov EDI,0x00010000",
- "B87856|mov AX,0x5678",
- "26678903|mov word ptr ES:[EBX],AX",
- "83F800|cmp AX,0",
- "AF|scas AX,word ptr ES:[DI]",
- "0F85BD0A|jne near 0x15C7"
+ "B480|mov AH,0x80",
+ "9E|sahf",
+ "790C|jns short 0x00AC"
]
},
{
- "id": 421,
- "entry": "F000:0537",
- "term": "F000:0548",
+ "id": 59,
+ "entry": "F000:00A3",
+ "term": "F000:00A6",
"pred": [
- 416
+ 52
],
"succ": [
- 430
+ 31,
+ 88
],
"asm": [
- "8CD9|mov CX,DS",
- "31C0|xor AX,AX",
- "8ED8|mov DS,AX",
- "8EC1|mov ES,CX",
- "268E1E0000|mov DS,word ptr ES:[0]",
- "8CD8|mov AX,DS",
- "39D0|cmp AX,DX",
- "0F857B10|jne near 0x15C7"
+ "B441|mov AH,0x41",
+ "9E|sahf",
+ "7704|ja short 0x00AC"
]
},
{
- "id": 869,
- "entry": "F000:0AEA",
- "term": "F000:0AF1",
+ "id": 35,
+ "entry": "F000:00AD",
+ "term": "F000:00AD",
"pred": [
- 866
+ 33
],
"succ": [
- 872
+ 39,
+ 41
],
"asm": [
- "6681FEFEFF0100|cmp ESI,0x0001FFFE",
- "0F85D20A|jne near 0x15C7"
+ "72DC|jb short 0x008B"
]
},
{
- "id": 430,
- "entry": "F000:054C",
- "term": "F000:0557",
+ "id": 37,
+ "entry": "F000:008A",
+ "dead": true,
+ "term": "F000:008A",
"pred": [
- 421
+ 33
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 372,
+ "entry": "F000:038F",
+ "term": "F000:0395",
+ "pred": [
+ 364,
+ 370
],
"succ": [
- 437
+ 381
],
"asm": [
- "BA0020|mov DX,0x2000",
- "8EC2|mov ES,DX",
- "31C0|xor AX,AX",
- "8CC0|mov AX,ES",
- "39D0|cmp AX,DX",
- "0F856C10|jne near 0x15C7"
+ "66B900000200|mov ECX,0x00020000",
+ "66B800000000|mov EAX,0"
]
},
{
- "id": 866,
- "entry": "F000:0ADF",
- "term": "F000:0AE6",
+ "id": 55,
+ "entry": "F000:0090",
+ "term": "F000:0090",
"pred": [
- 863
+ 39
],
"succ": [
- 869
+ 41,
+ 64
],
"asm": [
- "6681FFFEFF0100|cmp EDI,0x0001FFFE",
- "0F85DD0A|jne near 0x15C7"
+ "741D|je short 0x00AF"
]
},
{
- "id": 437,
- "entry": "F000:055B",
- "term": "F000:0566",
+ "id": 67,
+ "entry": "F000:0098",
+ "term": "F000:0098",
"pred": [
- 430
+ 44
],
"succ": [
- 442
+ 46,
+ 76
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CC0|mov EAX,ES",
- "39D0|cmp AX,DX",
- "0F855D10|jne near 0x15C7"
+ "7A17|jp short 0x00B1"
]
},
{
- "id": 863,
- "entry": "F000:0ADA",
- "term": "F000:0ADB",
+ "id": 41,
+ "entry": "F000:00AF",
+ "term": "F000:00AF",
"pred": [
- 855
+ 35,
+ 55
],
"succ": [
- 866
+ 44,
+ 46
],
"asm": [
- "A7|cmps word ptr DS:[SI],word ptr ES:[DI]",
- "0F85E80A|jne near 0x15C7"
+ "74E2|je short 0x0093"
]
},
{
- "id": 442,
- "entry": "F000:056A",
- "term": "F000:0578",
+ "id": 79,
+ "entry": "F000:00A0",
+ "term": "F000:00A0",
"pred": [
- 437
+ 50
],
"succ": [
- 447
+ 52,
+ 85
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C060000|mov word ptr DS:[0],ES",
- "39160000|cmp word ptr DS:[0],DX",
- "0F854B10|jne near 0x15C7"
+ "7811|js short 0x00B3"
]
},
{
- "id": 855,
- "entry": "F000:0AB9",
- "term": "F000:0AD6",
+ "id": 46,
+ "entry": "F000:00B1",
+ "term": "F000:00B1",
"pred": [
- 852
+ 41,
+ 67
],
"succ": [
- 863
+ 50,
+ 52
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
- "3E678903|mov word ptr DS:[EBX],AX",
- "26678903|mov word ptr ES:[EBX],AX",
- "83F800|cmp AX,0",
- "0F84ED0A|je near 0x15C7"
+ "7AE8|jp short 0x009B"
]
},
{
- "id": 447,
- "entry": "F000:057C",
- "term": "F000:058A",
+ "id": 88,
+ "entry": "F000:00A8",
+ "term": "F000:00A8",
"pred": [
- 442
+ 59
],
"succ": [
- 455
+ 61,
+ 71
],
"asm": [
- "8CD9|mov CX,DS",
- "31C0|xor AX,AX",
- "8EC0|mov ES,AX",
- "8E060000|mov ES,word ptr DS:[0]",
- "8CC0|mov AX,ES",
- "39D0|cmp AX,DX",
- "0F853910|jne near 0x15C7"
+ "760B|jbe short 0x00B5"
]
},
{
- "id": 852,
- "entry": "F000:0AAE",
- "term": "F000:0AB5",
+ "id": 52,
+ "entry": "F000:00B3",
+ "term": "F000:00B3",
"pred": [
- 842
+ 46,
+ 79
],
"succ": [
- 855
+ 59,
+ 61
],
"asm": [
- "6681FFFEFF0100|cmp EDI,0x0001FFFE",
- "0F850E0B|jne near 0x15C7"
+ "78EE|js short 0x00A3"
]
},
{
- "id": 455,
- "entry": "F000:058E",
- "term": "F000:0599",
+ "id": 364,
+ "entry": "F000:038B",
+ "term": "F000:038B",
"pred": [
- 447
+ 356,
+ 362
],
"succ": [
- 462
+ 370,
+ 372
],
"asm": [
- "BA0020|mov DX,0x2000",
- "8EE2|mov FS,DX",
- "31C0|xor AX,AX",
- "8CE0|mov AX,FS",
- "39D0|cmp AX,DX",
- "0F852A10|jne near 0x15C7"
+ "0F8E68FF|jle near 0x02F7"
]
},
{
- "id": 842,
- "entry": "F000:0A8E",
- "term": "F000:0AAA",
+ "id": 370,
+ "entry": "F000:02F7",
+ "term": "F000:02F7",
"pred": [
- 839
+ 364
],
"succ": [
- 852
+ 372
],
"asm": [
- "FD|std",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
+ "E99500|jmp near 0x038F"
+ ]
+ },
+ {
+ "id": 64,
+ "entry": "F000:0092",
+ "dead": true,
+ "term": "F000:0092",
+ "pred": [
+ 55
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 76,
+ "entry": "F000:009A",
+ "dead": true,
+ "term": "F000:009A",
+ "pred": [
+ 67
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 85,
+ "entry": "F000:00A2",
+ "dead": true,
+ "term": "F000:00A2",
+ "pred": [
+ 79
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 61,
+ "entry": "F000:00B5",
+ "term": "F000:00B5",
+ "pred": [
+ 52,
+ 88
+ ],
+ "succ": [
+ 71,
+ 73
+ ],
+ "asm": [
+ "76F3|jbe short 0x00AA"
+ ]
+ },
+ {
+ "id": 71,
+ "entry": "F000:00AA",
+ "term": "F000:00AA",
+ "pred": [
+ 61,
+ 88
+ ],
+ "succ": [
+ 73
+ ],
+ "asm": [
+ "EB0B|jmp short 0x00B7"
+ ]
+ },
+ {
+ "id": 356,
+ "entry": "F000:0387",
+ "term": "F000:0387",
+ "pred": [
+ 346,
+ 354
+ ],
+ "succ": [
+ 362,
+ 364
+ ],
+ "asm": [
+ "0F8C67FF|jl near 0x02F2"
+ ]
+ },
+ {
+ "id": 362,
+ "entry": "F000:02F2",
+ "term": "F000:02F2",
+ "pred": [
+ 356
+ ],
+ "succ": [
+ 364,
+ 368
+ ],
+ "asm": [
+ "0F8E9500|jle near 0x038B"
+ ]
+ },
+ {
+ "id": 73,
+ "entry": "F000:00B7",
+ "term": "F000:00BE",
+ "pred": [
+ 61,
+ 71
+ ],
+ "succ": [
+ 95,
+ 97
+ ],
+ "asm": [
+ "B4D4|mov AH,0xD4",
+ "9E|sahf",
"B80000|mov AX,0",
- "26678903|mov word ptr ES:[EBX],AX",
- "B87856|mov AX,0x5678",
- "AB|stos word ptr ES:[DI],AX",
- "26673903|cmp word ptr ES:[EBX],AX",
- "0F85190B|jne near 0x15C7"
+ "9E|sahf",
+ "731B|jae short 0x00DB"
]
},
{
- "id": 462,
- "entry": "F000:059D",
- "term": "F000:05A8",
+ "id": 346,
+ "entry": "F000:0383",
+ "term": "F000:0383",
"pred": [
- 455
+ 339,
+ 352
],
"succ": [
- 467
+ 354,
+ 356
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CE0|mov EAX,FS",
- "39D0|cmp AX,DX",
- "0F851B10|jne near 0x15C7"
+ "0F8F63FF|jg near 0x02EA"
]
},
{
- "id": 839,
- "entry": "F000:0A83",
- "term": "F000:0A8A",
+ "id": 354,
+ "entry": "F000:02EA",
+ "term": "F000:02ED",
"pred": [
- 831
+ 346
],
"succ": [
- 842
+ 356,
+ 375
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85390B|jne near 0x15C7"
+ "B440|mov AH,0x40",
+ "9E|sahf",
+ "0F8C9600|jl near 0x0387"
]
},
{
- "id": 467,
- "entry": "F000:05AC",
- "term": "F000:05BA",
+ "id": 368,
+ "entry": "F000:02F6",
+ "dead": true,
+ "term": "F000:02F6",
+ "pred": [
+ 362
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 95,
+ "entry": "F000:00DB",
+ "term": "F000:00DB",
+ "pred": [
+ 73
+ ],
+ "succ": [
+ 99,
+ 101
+ ],
+ "asm": [
+ "73E4|jae short 0x00C1"
+ ]
+ },
+ {
+ "id": 97,
+ "entry": "F000:00C0",
+ "dead": true,
+ "term": "F000:00C0",
+ "pred": [
+ 73
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 339,
+ "entry": "F000:037F",
+ "term": "F000:037F",
+ "pred": [
+ 333,
+ 342
+ ],
+ "succ": [
+ 344,
+ 346
+ ],
+ "asm": [
+ "0F8D5EFF|jge near 0x02E1"
+ ]
+ },
+ {
+ "id": 352,
+ "entry": "F000:02E5",
+ "term": "F000:02E5",
+ "pred": [
+ 344
+ ],
+ "succ": [
+ 346,
+ 359
+ ],
+ "asm": [
+ "0F8F9A00|jg near 0x0383"
+ ]
+ },
+ {
+ "id": 375,
+ "entry": "F000:02F1",
+ "dead": true,
+ "term": "F000:02F1",
+ "pred": [
+ 354
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 99,
+ "entry": "F000:00C1",
+ "term": "F000:00C4",
+ "pred": [
+ 95
+ ],
+ "succ": [
+ 101,
+ 115
+ ],
+ "asm": [
+ "B495|mov AH,0x95",
+ "9E|sahf",
+ "7517|jne short 0x00DD"
+ ]
+ },
+ {
+ "id": 101,
+ "entry": "F000:00DD",
+ "term": "F000:00DD",
+ "pred": [
+ 95,
+ 99
+ ],
+ "succ": [
+ 104,
+ 106
+ ],
+ "asm": [
+ "75E8|jne short 0x00C7"
+ ]
+ },
+ {
+ "id": 344,
+ "entry": "F000:02E1",
+ "term": "F000:02E1",
+ "pred": [
+ 339
+ ],
+ "succ": [
+ 329,
+ 352
+ ],
+ "asm": [
+ "0F8E9500|jle near 0x037A"
+ ]
+ },
+ {
+ "id": 333,
+ "entry": "F000:037B",
+ "term": "F000:037B",
+ "pred": [
+ 331
+ ],
+ "succ": [
+ 337,
+ 339
+ ],
+ "asm": [
+ "0F8059FF|jo near 0x02D8"
+ ]
+ },
+ {
+ "id": 342,
+ "entry": "F000:02DC",
+ "term": "F000:02DC",
+ "pred": [
+ 337
+ ],
+ "succ": [
+ 339,
+ 349
+ ],
+ "asm": [
+ "0F8D9F00|jge near 0x037F"
+ ]
+ },
+ {
+ "id": 359,
+ "entry": "F000:02E9",
+ "dead": true,
+ "term": "F000:02E9",
+ "pred": [
+ 352
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 115,
+ "entry": "F000:00C6",
+ "dead": true,
+ "term": "F000:00C6",
+ "pred": [
+ 99
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 104,
+ "entry": "F000:00C7",
+ "term": "F000:00CA",
+ "pred": [
+ 101
+ ],
+ "succ": [
+ 106,
+ 124
+ ],
+ "asm": [
+ "B4D1|mov AH,0xD1",
+ "9E|sahf",
+ "7B13|jnp short 0x00DF"
+ ]
+ },
+ {
+ "id": 106,
+ "entry": "F000:00DF",
+ "term": "F000:00DF",
+ "pred": [
+ 101,
+ 104
+ ],
+ "succ": [
+ 110,
+ 112
+ ],
+ "asm": [
+ "7BEC|jnp short 0x00CD"
+ ]
+ },
+ {
+ "id": 329,
+ "entry": "F000:037A",
+ "dead": true,
+ "term": "F000:037A",
+ "pred": [
+ 315,
+ 337,
+ 344
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 337,
+ "entry": "F000:02D8",
+ "term": "F000:02D8",
+ "pred": [
+ 333
+ ],
+ "succ": [
+ 329,
+ 342
+ ],
+ "asm": [
+ "0F8C9E00|jl near 0x037A"
+ ]
+ },
+ {
+ "id": 331,
+ "entry": "F000:02D3",
+ "term": "F000:02D3",
+ "pred": [
+ 315
+ ],
+ "succ": [
+ 333,
+ 335
+ ],
+ "asm": [
+ "0F80A400|jo near 0x037B"
+ ]
+ },
+ {
+ "id": 349,
+ "entry": "F000:02E0",
+ "dead": true,
+ "term": "F000:02E0",
+ "pred": [
+ 342
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 124,
+ "entry": "F000:00CC",
+ "dead": true,
+ "term": "F000:00CC",
+ "pred": [
+ 104
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 110,
+ "entry": "F000:00CD",
+ "term": "F000:00D0",
+ "pred": [
+ 106
+ ],
+ "succ": [
+ 112,
+ 133
+ ],
+ "asm": [
+ "B455|mov AH,0x55",
+ "9E|sahf",
+ "790F|jns short 0x00E1"
+ ]
+ },
+ {
+ "id": 112,
+ "entry": "F000:00E1",
+ "term": "F000:00E1",
+ "pred": [
+ 106,
+ 110
+ ],
+ "succ": [
+ 119,
+ 121
+ ],
+ "asm": [
+ "79F0|jns short 0x00D3"
+ ]
+ },
+ {
+ "id": 315,
+ "entry": "F000:02C8",
+ "term": "F000:02CF",
+ "pred": [
+ 306,
+ 313
+ ],
+ "succ": [
+ 329,
+ 331
+ ],
+ "asm": [
+ "B400|mov AH,0",
+ "9E|sahf",
+ "B040|mov AL,0x40",
+ "D0E0|shl AL,1",
+ "0F81A700|jno near 0x037A"
+ ]
+ },
+ {
+ "id": 335,
+ "entry": "F000:02D7",
+ "dead": true,
+ "term": "F000:02D7",
+ "pred": [
+ 331
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 133,
+ "entry": "F000:00D2",
+ "dead": true,
+ "term": "F000:00D2",
+ "pred": [
+ 110
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 119,
+ "entry": "F000:00D3",
+ "term": "F000:00D6",
"pred": [
- 462
+ 112
],
"succ": [
- 472
+ 121,
+ 139
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C260000|mov word ptr DS:[0],FS",
- "39160000|cmp word ptr DS:[0],DX",
- "0F850910|jne near 0x15C7"
+ "B494|mov AH,0x94",
+ "9E|sahf",
+ "770B|ja short 0x00E3"
]
},
{
- "id": 831,
- "entry": "F000:0A6D",
- "term": "F000:0A7F",
+ "id": 121,
+ "entry": "F000:00E3",
+ "term": "F000:00E3",
"pred": [
- 828
+ 112,
+ 119
],
"succ": [
- 839
+ 128,
+ 130
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "B078|mov AL,0x78",
- "26678803|mov byte ptr ES:[EBX],AL",
- "6631C0|xor EAX,EAX",
- "AC|lods AL,byte ptr DS:[SI]",
- "3C78|cmp AL,0x78",
- "0F85440B|jne near 0x15C7"
+ "77F4|ja short 0x00D9"
]
},
{
- "id": 472,
- "entry": "F000:05BE",
- "term": "F000:05CC",
+ "id": 306,
+ "entry": "F000:02C4",
+ "term": "F000:02C4",
"pred": [
- 467
+ 297,
+ 304
],
"succ": [
- 480
+ 313,
+ 315
],
"asm": [
- "8CD9|mov CX,DS",
- "31C0|xor AX,AX",
- "8EE0|mov FS,AX",
- "8E260000|mov FS,word ptr DS:[0]",
- "8CE0|mov AX,FS",
- "39D0|cmp AX,DX",
- "0F85F70F|jne near 0x15C7"
+ "0F8769FF|ja near 0x0231"
]
},
{
- "id": 828,
- "entry": "F000:0A62",
- "term": "F000:0A69",
+ "id": 313,
+ "entry": "F000:0231",
+ "term": "F000:0231",
"pred": [
- 825
+ 306
],
"succ": [
- 831
+ 315
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F855A0B|jne near 0x15C7"
+ "E99400|jmp near 0x02C8"
]
},
{
- "id": 480,
- "entry": "F000:05D0",
- "term": "F000:05DB",
+ "id": 139,
+ "entry": "F000:00D8",
+ "dead": true,
+ "term": "F000:00D8",
"pred": [
- 472
- ],
- "succ": [
- 487
+ 119
],
+ "succ": [],
"asm": [
- "BA0020|mov DX,0x2000",
- "8EEA|mov GS,DX",
- "31C0|xor AX,AX",
- "8CE8|mov AX,GS",
- "39D0|cmp AX,DX",
- "0F85E80F|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 825,
- "entry": "F000:0A57",
- "term": "F000:0A5E",
+ "id": 128,
+ "entry": "F000:00D9",
+ "term": "F000:00D9",
"pred": [
- 814
+ 121
],
"succ": [
- 828
+ 130
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85650B|jne near 0x15C7"
+ "EB0A|jmp short 0x00E5"
]
},
{
- "id": 487,
- "entry": "F000:05DF",
- "term": "F000:05EA",
+ "id": 130,
+ "entry": "F000:00E5",
+ "term": "F000:00EC",
"pred": [
- 480
+ 121,
+ 128
],
"succ": [
- 492
+ 144,
+ 146
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CE8|mov EAX,GS",
- "39D0|cmp AX,DX",
- "0F85D90F|jne near 0x15C7"
+ "B400|mov AH,0",
+ "9E|sahf",
+ "B040|mov AL,0x40",
+ "D0E0|shl AL,1",
+ "7134|jno short 0x0122"
]
},
{
- "id": 814,
- "entry": "F000:0A34",
- "term": "F000:0A53",
+ "id": 297,
+ "entry": "F000:02C0",
+ "term": "F000:02C0",
"pred": [
- 811
+ 291,
+ 295
],
"succ": [
- 825
+ 304,
+ 306
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "B078|mov AL,0x78",
- "3E678803|mov byte ptr DS:[EBX],AL",
- "B000|mov AL,0",
- "26678803|mov byte ptr ES:[EBX],AL",
- "A4|movs byte ptr ES:[DI],byte ptr DS:[SI]",
- "B078|mov AL,0x78",
- "26673803|cmp byte ptr ES:[EBX],AL",
- "0F85700B|jne near 0x15C7"
+ "0F8965FF|jns near 0x0229"
]
},
{
- "id": 492,
- "entry": "F000:05EE",
- "term": "F000:05FC",
+ "id": 304,
+ "entry": "F000:0229",
+ "term": "F000:022C",
"pred": [
- 487
+ 297
],
"succ": [
- 497
+ 306,
+ 324
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C2E0000|mov word ptr DS:[0],GS",
- "39160000|cmp word ptr DS:[0],DX",
- "0F85C70F|jne near 0x15C7"
+ "B494|mov AH,0x94",
+ "9E|sahf",
+ "0F879400|ja near 0x02C4"
]
},
{
- "id": 811,
- "entry": "F000:0A29",
- "term": "F000:0A30",
+ "id": 144,
+ "entry": "F000:0122",
+ "dead": true,
+ "term": "F000:0122",
+ "pred": [
+ 130,
+ 152,
+ 159,
+ 185,
+ 193
+ ],
+ "succ": [],
+ "asm": [
+ "F4|hlt"
+ ]
+ },
+ {
+ "id": 146,
+ "entry": "F000:00EE",
+ "term": "F000:00EE",
"pred": [
- 804
+ 130
],
"succ": [
- 814
+ 148,
+ 150
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85930B|jne near 0x15C7"
+ "7033|jo short 0x0123"
]
},
{
- "id": 497,
- "entry": "F000:0600",
- "term": "F000:060E",
+ "id": 291,
+ "entry": "F000:02BC",
+ "term": "F000:02BC",
"pred": [
- 492
+ 286,
+ 289
],
"succ": [
- 505
+ 295,
+ 297
],
"asm": [
- "8CD9|mov CX,DS",
- "31C0|xor AX,AX",
- "8EE8|mov GS,AX",
- "8E2E0000|mov GS,word ptr DS:[0]",
- "8CE8|mov AX,GS",
- "39D0|cmp AX,DX",
- "0F85B50F|jne near 0x15C7"
+ "0F8B61FF|jnp near 0x0221"
]
},
{
- "id": 804,
- "entry": "F000:0A16",
- "term": "F000:0A25",
+ "id": 295,
+ "entry": "F000:0221",
+ "term": "F000:0224",
"pred": [
- 801
+ 291
],
"succ": [
- 811
+ 297,
+ 318
],
"asm": [
- "66BF00000100|mov EDI,0x00010000",
- "B078|mov AL,0x78",
- "26678803|mov byte ptr ES:[EBX],AL",
- "3C00|cmp AL,0",
- "AE|scas AL,byte ptr ES:[DI]",
- "0F859E0B|jne near 0x15C7"
+ "B455|mov AH,0x55",
+ "9E|sahf",
+ "0F899800|jns near 0x02C0"
]
},
{
- "id": 505,
- "entry": "F000:0612",
- "term": "F000:062E",
+ "id": 324,
+ "entry": "F000:0230",
+ "dead": true,
+ "term": "F000:0230",
"pred": [
- 497
- ],
- "succ": [
- 516
+ 304
],
+ "succ": [],
"asm": [
- "BA00F0|mov DX,0xF000",
- "B80000|mov AX,0",
- "8ED8|mov DS,AX",
- "C70618003306|mov word ptr DS:[0x0018],0x0633",
- "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
- "B80010|mov AX,0x1000",
- "8ED0|mov SS,AX",
- "BCFFFF|mov SP,0xFFFF",
- "8ECA|invalid"
+ "F4|hlt"
]
},
{
- "id": 801,
- "entry": "F000:0A0B",
- "term": "F000:0A12",
+ "id": 152,
+ "entry": "F000:00F1",
+ "term": "F000:00F1",
"pred": [
- 798
+ 148
],
"succ": [
- 804
+ 144,
+ 157
],
"asm": [
- "6681FEFFFF0100|cmp ESI,0x0001FFFF",
- "0F85B10B|jne near 0x15C7"
+ "7C2F|jl short 0x0122"
]
},
{
- "id": 516,
- "entry": "F000:0633",
- "term": "F000:0636",
+ "id": 159,
+ "entry": "F000:00F6",
+ "term": "F000:00F6",
"pred": [
- 505
+ 154
],
"succ": [
- 519
+ 144,
+ 167
],
"asm": [
- "83FCF9|cmp SP,-7",
- "0F858D0F|jne near 0x15C7"
+ "7E2A|jle short 0x0122"
]
},
{
- "id": 798,
- "entry": "F000:0A00",
- "term": "F000:0A07",
+ "id": 185,
+ "entry": "F000:0104",
+ "term": "F000:010A",
"pred": [
- 795
+ 179
],
"succ": [
- 801
+ 144,
+ 198
],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85BC0B|jne near 0x15C7"
+ "66B901000000|mov ECX,1",
+ "E316|jcxz short 0x0122"
]
},
{
- "id": 519,
- "entry": "F000:063A",
- "term": "F000:0648",
+ "id": 193,
+ "entry": "F000:0114",
+ "term": "F000:0114",
"pred": [
- 516
+ 187,
+ 198
],
"succ": [
- 523
+ 144,
+ 201
],
"asm": [
- "36813EFBFF00F0|cmp word ptr SS:[0xFFFB],0xF000",
- "36813EF9FF2E06|cmp word ptr SS:[0xFFF9],0x062E",
- "0F857B0F|jne near 0x15C7"
+ "67E30B|jecxz short 0x0122"
]
},
{
- "id": 795,
- "entry": "F000:09FB",
- "term": "F000:09FC",
+ "id": 148,
+ "entry": "F000:0123",
+ "term": "F000:0123",
"pred": [
- 787
+ 146
],
"succ": [
- 798
+ 152,
+ 154
],
"asm": [
- "A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
- "0F85C70B|jne near 0x15C7"
+ "70CC|jo short 0x00F1"
]
},
{
- "id": 523,
- "entry": "F000:064C",
- "term": "F000:0663",
+ "id": 150,
+ "entry": "F000:00F0",
+ "dead": true,
+ "term": "F000:00F0",
"pred": [
- 519
- ],
- "succ": [
- 532
+ 146
],
+ "succ": [],
"asm": [
- "B80000|mov AX,0",
- "8ED8|mov DS,AX",
- "C7061800C715|mov word ptr DS:[0x0018],0x15C7",
- "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
- "31C0|xor AX,AX",
- "8CC8|mov AX,CS",
- "39D0|cmp AX,DX",
- "0F85600F|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 787,
- "entry": "F000:09DB",
- "term": "F000:09F7",
+ "id": 286,
+ "entry": "F000:02B8",
+ "term": "F000:02B8",
"pred": [
- 784
+ 280,
+ 284
],
"succ": [
- 795
+ 289,
+ 291
],
"asm": [
- "66BE00000100|mov ESI,0x00010000",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
- "3E678803|mov byte ptr DS:[EBX],AL",
- "26678803|mov byte ptr ES:[EBX],AL",
- "3C00|cmp AL,0",
- "0F84CC0B|je near 0x15C7"
+ "0F855DFF|jne near 0x0219"
]
},
{
- "id": 532,
- "entry": "F000:0667",
- "term": "F000:0672",
+ "id": 289,
+ "entry": "F000:0219",
+ "term": "F000:021C",
"pred": [
- 523
+ 286
],
"succ": [
- 537
+ 291,
+ 309
],
"asm": [
- "66B8FFFFFFFF|mov EAX,0xFFFFFFFF",
- "668CC8|mov EAX,CS",
- "39D0|cmp AX,DX",
- "0F85510F|jne near 0x15C7"
+ "B4D1|mov AH,0xD1",
+ "9E|sahf",
+ "0F8B9C00|jnp near 0x02BC"
]
},
{
- "id": 784,
- "entry": "F000:09D0",
- "term": "F000:09D7",
+ "id": 318,
+ "entry": "F000:0228",
+ "dead": true,
+ "term": "F000:0228",
"pred": [
- 774
- ],
- "succ": [
- 787
+ 295
],
+ "succ": [],
"asm": [
- "6681FFFFFF0100|cmp EDI,0x0001FFFF",
- "0F85EC0B|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 537,
- "entry": "F000:0676",
- "term": "F000:0684",
+ "id": 157,
+ "entry": "F000:00F3",
+ "term": "F000:00F3",
"pred": [
- 532
+ 152
],
"succ": [
- 542
+ 154,
+ 164
],
"asm": [
- "C7060000EFBE|mov word ptr DS:[0],0xBEEF",
- "8C0E0000|mov word ptr DS:[0],CS",
- "39160000|cmp word ptr DS:[0],DX",
- "0F853F0F|jne near 0x15C7"
+ "7D30|jge short 0x0125"
]
},
{
- "id": 774,
- "entry": "F000:09B2",
- "term": "F000:09CC",
+ "id": 167,
+ "entry": "F000:00F8",
+ "term": "F000:00F8",
"pred": [
- 771
+ 159
],
"succ": [
- 784
+ 161,
+ 174
],
"asm": [
- "FD|std",
- "66BF00000100|mov EDI,0x00010000",
- "66BB00000000|mov EBX,0",
- "B000|mov AL,0",
- "26678803|mov byte ptr ES:[EBX],AL",
- "B078|mov AL,0x78",
- "AA|stos byte ptr ES:[DI],AL",
- "26673803|cmp byte ptr ES:[EBX],AL",
- "0F85F70B|jne near 0x15C7"
+ "7F2D|jg short 0x0127"
]
},
{
- "id": 542,
- "entry": "F000:0688",
- "term": "F000:06A1",
+ "id": 154,
+ "entry": "F000:0125",
+ "term": "F000:0125",
"pred": [
- 537
+ 148,
+ 157
],
"succ": [
- 552
+ 159,
+ 161
],
"asm": [
- "B80000|mov AX,0",
- "8ED8|mov DS,AX",
- "C7061800A806|mov word ptr DS:[0x0018],0x06A8",
- "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
- "B80010|mov AX,0x1000",
- "8ED0|mov SS,AX",
- "BCFFFF|mov SP,0xFFFF",
- "8E0E0000|invalid"
+ "7DCF|jge short 0x00F6"
]
},
{
- "id": 771,
- "entry": "F000:09A7",
- "term": "F000:09AE",
+ "id": 198,
+ "entry": "F000:010C",
+ "term": "F000:0112",
"pred": [
- 763
+ 185
],
"succ": [
- 774
+ 187,
+ 193
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85150C|jne near 0x15C7"
+ "66B900000100|mov ECX,0x00010000",
+ "E319|jcxz short 0x012D"
]
},
{
- "id": 552,
- "entry": "F000:06A8",
- "term": "F000:06AB",
+ "id": 179,
+ "entry": "F000:012B",
+ "term": "F000:012B",
"pred": [
- 542
+ 171,
+ 177
],
"succ": [
- 555
+ 185,
+ 187
],
"asm": [
- "83FCF9|cmp SP,-7",
- "0F85180F|jne near 0x15C7"
+ "7ED7|jle short 0x0104"
]
},
{
- "id": 763,
- "entry": "F000:0987",
- "term": "F000:09A3",
+ "id": 201,
+ "entry": "F000:0117",
+ "term": "F000:011D",
"pred": [
- 760
+ 193
],
"succ": [
- 771
+ 195,
+ 203
],
"asm": [
- "66BEFCFF0100|mov ESI,0x0001FFFC",
- "66B878563412|mov EAX,0x12345678",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6631C0|xor EAX,EAX",
- "66AD|lods EAX,dword ptr DS:[SI]",
- "663D78563412|cmp EAX,0x12345678",
- "0F85200C|jne near 0x15C7"
+ "66B900000000|mov ECX,0",
+ "67E30F|jecxz short 0x012F"
]
},
{
- "id": 555,
- "entry": "F000:06AF",
- "term": "F000:06BD",
+ "id": 187,
+ "entry": "F000:012D",
+ "term": "F000:012D",
"pred": [
- 552
+ 179,
+ 198
],
"succ": [
- 559
+ 193,
+ 195
],
"asm": [
- "36813EFBFF00F0|cmp word ptr SS:[0xFFFB],0xF000",
- "36813EF9FFA106|cmp word ptr SS:[0xFFF9],0x06A1",
- "0F85060F|jne near 0x15C7"
+ "E3E5|jcxz short 0x0114"
]
},
{
- "id": 760,
- "entry": "F000:097C",
- "term": "F000:0983",
+ "id": 280,
+ "entry": "F000:02B4",
+ "term": "F000:02B4",
"pred": [
- 757
+ 258
],
"succ": [
- 763
+ 284,
+ 286
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85400C|jne near 0x15C7"
+ "0F8359FF|jae near 0x0211"
]
},
{
- "id": 559,
- "entry": "F000:06C1",
- "term": "F000:06FC",
+ "id": 284,
+ "entry": "F000:0211",
+ "term": "F000:0214",
"pred": [
- 555
+ 280
],
"succ": [
- 580
+ 286,
+ 300
],
"asm": [
- "B80000|mov AX,0",
- "8ED8|mov DS,AX",
- "C7061800C715|mov word ptr DS:[0x0018],0x15C7",
- "C7061A0000F0|mov word ptr DS:[0x001A],0xF000",
- "BA0021|mov DX,0x2100",
- "8EDA|mov DS,DX",
- "BA0061|mov DX,0x6100",
- "8EC2|mov ES,DX",
- "B004|mov AL,4",
- "BA9909|mov DX,0x0999",
- "EE|out DX,AL",
- "FC|cld",
- "66BFFFFF0100|mov EDI,0x0001FFFF",
- "66BBFFFF0000|mov EBX,0x0000FFFF",
- "B000|mov AL,0",
- "26678803|mov byte ptr ES:[EBX],AL",
- "B078|mov AL,0x78",
- "AA|stos byte ptr ES:[DI],AL",
- "26673803|cmp byte ptr ES:[EBX],AL",
- "0F85C70E|jne near 0x15C7"
+ "B495|mov AH,0x95",
+ "9E|sahf",
+ "0F85A000|jne near 0x02B8"
]
},
{
- "id": 757,
- "entry": "F000:0971",
- "term": "F000:0978",
+ "id": 309,
+ "entry": "F000:0220",
+ "dead": true,
+ "term": "F000:0220",
"pred": [
- 746
- ],
- "succ": [
- 760
+ 289
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F854B0C|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 580,
- "entry": "F000:0700",
- "term": "F000:0707",
+ "id": 164,
+ "entry": "F000:00F5",
+ "dead": true,
+ "term": "F000:00F5",
"pred": [
- 559
- ],
- "succ": [
- 583
+ 157
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85BC0E|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 746,
- "entry": "F000:093E",
- "term": "F000:096D",
+ "id": 161,
+ "entry": "F000:0127",
+ "term": "F000:0127",
"pred": [
- 743
+ 154,
+ 167
],
"succ": [
- 757
+ 169,
+ 171
],
"asm": [
- "66BEFCFF0100|mov ESI,0x0001FFFC",
- "66BFFCFF0100|mov EDI,0x0001FFFC",
- "66B878563412|mov EAX,0x12345678",
- "3E66678903|mov dword ptr DS:[EBX],EAX",
- "66B800000000|mov EAX,0",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "66A5|movs dword ptr ES:[DI],dword ptr DS:[SI]",
- "66B878563412|mov EAX,0x12345678",
- "2666673903|cmp dword ptr ES:[EBX],EAX",
- "0F85560C|jne near 0x15C7"
+ "7FD2|jg short 0x00FB"
]
},
{
- "id": 583,
- "entry": "F000:070B",
- "term": "F000:0727",
+ "id": 174,
+ "entry": "F000:00FA",
+ "dead": true,
+ "term": "F000:00FA",
"pred": [
- 580
- ],
- "succ": [
- 591
+ 167
],
+ "succ": [],
"asm": [
- "66BEFFFF0100|mov ESI,0x0001FFFF",
- "66BFFFFF0100|mov EDI,0x0001FFFF",
- "66BBFFFF0000|mov EBX,0x0000FFFF",
- "3E678803|mov byte ptr DS:[EBX],AL",
- "26678803|mov byte ptr ES:[EBX],AL",
- "3C00|cmp AL,0",
- "0F849C0E|je near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 743,
- "entry": "F000:0933",
- "term": "F000:093A",
+ "id": 171,
+ "entry": "F000:0129",
+ "term": "F000:0129",
"pred": [
- 736
+ 161,
+ 169
],
"succ": [
- 746
+ 177,
+ 179
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85890C|jne near 0x15C7"
+ "7CD6|jl short 0x0101"
]
},
{
- "id": 591,
- "entry": "F000:072B",
- "term": "F000:072C",
+ "id": 177,
+ "entry": "F000:0101",
+ "term": "F000:0101",
"pred": [
- 583
+ 171
],
"succ": [
- 594
+ 179,
+ 183
],
"asm": [
- "A6|cmps byte ptr DS:[SI],byte ptr ES:[DI]",
- "0F85970E|jne near 0x15C7"
+ "7E28|jle short 0x012B"
]
},
{
- "id": 736,
- "entry": "F000:0918",
- "term": "F000:092F",
+ "id": 195,
+ "entry": "F000:012F",
+ "term": "F000:012F",
"pred": [
- 733
+ 187,
+ 201
],
"succ": [
- 743
+ 203,
+ 205
],
"asm": [
- "66BFFCFF0100|mov EDI,0x0001FFFC",
- "66B878563412|mov EAX,0x12345678",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6683F800|cmp EAX,0",
- "66AF|scas EAX,dword ptr ES:[DI]",
- "0F85940C|jne near 0x15C7"
+ "67E3EE|jecxz short 0x0120"
]
},
{
- "id": 594,
- "entry": "F000:0730",
- "term": "F000:0737",
+ "id": 203,
+ "entry": "F000:0120",
+ "term": "F000:0120",
"pred": [
- 591
+ 195,
+ 201
],
"succ": [
- 597
+ 205
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F858C0E|jne near 0x15C7"
+ "EB10|jmp short 0x0132"
]
},
{
- "id": 733,
- "entry": "F000:090D",
- "term": "F000:0914",
+ "id": 258,
+ "entry": "F000:0205",
+ "term": "F000:020C",
"pred": [
- 730
+ 246,
+ 256
],
"succ": [
- 736
+ 280,
+ 282
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85AF0C|jne near 0x15C7"
+ "B4D4|mov AH,0xD4",
+ "9E|sahf",
+ "B80000|mov AX,0",
+ "9E|sahf",
+ "0F83A400|jae near 0x02B4"
]
},
{
- "id": 597,
- "entry": "F000:073B",
- "term": "F000:0742",
+ "id": 300,
+ "entry": "F000:0218",
+ "dead": true,
+ "term": "F000:0218",
"pred": [
- 594
- ],
- "succ": [
- 600
+ 284
],
+ "succ": [],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85810E|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 730,
- "entry": "F000:0902",
- "term": "F000:0909",
+ "id": 169,
+ "entry": "F000:00FB",
+ "term": "F000:00FE",
"pred": [
- 727
+ 161
],
"succ": [
- 733
+ 171,
+ 190
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85BA0C|jne near 0x15C7"
+ "B440|mov AH,0x40",
+ "9E|sahf",
+ "7C29|jl short 0x0129"
]
},
{
- "id": 600,
- "entry": "F000:0746",
- "term": "F000:0755",
+ "id": 183,
+ "entry": "F000:0103",
+ "dead": true,
+ "term": "F000:0103",
"pred": [
- 597
- ],
- "succ": [
- 607
+ 177
],
+ "succ": [],
"asm": [
- "66BFFFFF0100|mov EDI,0x0001FFFF",
- "B078|mov AL,0x78",
- "26678803|mov byte ptr ES:[EBX],AL",
- "3C00|cmp AL,0",
- "AE|scas AL,byte ptr ES:[DI]",
- "0F856E0E|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 727,
- "entry": "F000:08FC",
- "term": "F000:08FE",
+ "id": 205,
+ "entry": "F000:0132",
+ "term": "F000:0135",
"pred": [
- 719
+ 195,
+ 203
],
"succ": [
- 730
+ 216,
+ 218
],
"asm": [
- "66A7|cmps dword ptr DS:[SI],dword ptr ES:[DI]",
- "0F85C50C|jne near 0x15C7"
+ "B401|mov AH,1",
+ "9E|sahf",
+ "0F83B700|jae near 0x01F0"
]
},
{
- "id": 607,
- "entry": "F000:0759",
- "term": "F000:0760",
+ "id": 282,
+ "entry": "F000:0210",
+ "dead": true,
+ "term": "F000:0210",
"pred": [
- 600
- ],
- "succ": [
- 610
+ 258
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85630E|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 719,
- "entry": "F000:08D8",
- "term": "F000:08F8",
+ "id": 246,
+ "entry": "F000:0201",
+ "term": "F000:0201",
"pred": [
- 716
+ 237,
+ 273
],
"succ": [
- 727
+ 256,
+ 258
],
"asm": [
- "66BEFCFF0100|mov ESI,0x0001FFFC",
- "66BFFCFF0100|mov EDI,0x0001FFFC",
- "66BBFCFF0000|mov EBX,0x0000FFFC",
- "3E66678903|mov dword ptr DS:[EBX],EAX",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "6683F800|cmp EAX,0",
- "0F84CB0C|je near 0x15C7"
+ "0F8668FF|jbe near 0x016D"
]
},
{
- "id": 610,
- "entry": "F000:0764",
- "term": "F000:0783",
+ "id": 256,
+ "entry": "F000:016D",
+ "term": "F000:016D",
"pred": [
- 607
+ 246,
+ 273
],
"succ": [
- 621
+ 258
],
"asm": [
- "66BEFFFF0100|mov ESI,0x0001FFFF",
- "66BFFFFF0100|mov EDI,0x0001FFFF",
- "B078|mov AL,0x78",
- "3E678803|mov byte ptr DS:[EBX],AL",
- "B000|mov AL,0",
- "26678803|mov byte ptr ES:[EBX],AL",
- "A4|movs byte ptr ES:[DI],byte ptr DS:[SI]",
- "B078|mov AL,0x78",
- "26673803|cmp byte ptr ES:[EBX],AL",
- "0F85400E|jne near 0x15C7"
+ "E99500|jmp near 0x0205"
]
},
{
- "id": 716,
- "entry": "F000:08CD",
- "term": "F000:08D4",
+ "id": 190,
+ "entry": "F000:0100",
+ "dead": true,
+ "term": "F000:0100",
"pred": [
- 706
- ],
- "succ": [
- 719
+ 169
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85EF0C|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 621,
- "entry": "F000:0787",
- "term": "F000:078E",
+ "id": 216,
+ "entry": "F000:01F0",
+ "dead": true,
+ "term": "F000:01F0",
"pred": [
- 610
- ],
- "succ": [
- 624
+ 205,
+ 224,
+ 229,
+ 235,
+ 244
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85350E|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 706,
- "entry": "F000:08A4",
- "term": "F000:08C9",
+ "id": 218,
+ "entry": "F000:0139",
+ "term": "F000:0139",
"pred": [
- 703
+ 205
],
"succ": [
- 716
+ 220,
+ 222
],
"asm": [
- "FC|cld",
- "66BFFCFF0100|mov EDI,0x0001FFFC",
- "66BBFCFF0000|mov EBX,0x0000FFFC",
- "66B800000000|mov EAX,0",
- "2666678903|mov dword ptr ES:[EBX],EAX",
- "66B878563412|mov EAX,0x12345678",
- "66AB|stos dword ptr ES:[DI],EAX",
- "2666673903|cmp dword ptr ES:[EBX],EAX",
- "0F85FA0C|jne near 0x15C7"
+ "0F82B400|jb near 0x01F1"
]
},
{
- "id": 624,
- "entry": "F000:0792",
- "term": "F000:0799",
+ "id": 237,
+ "entry": "F000:01FD",
+ "term": "F000:01FD",
"pred": [
- 621
+ 231,
+ 264
],
"succ": [
- 627
+ 244,
+ 246
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F852A0E|jne near 0x15C7"
+ "0F8861FF|js near 0x0162"
]
},
{
- "id": 703,
- "entry": "F000:0899",
- "term": "F000:08A0",
+ "id": 273,
+ "entry": "F000:0169",
+ "term": "F000:0169",
"pred": [
- 695
+ 244
],
"succ": [
- 706
+ 246,
+ 256
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85230D|jne near 0x15C7"
+ "0F869400|jbe near 0x0201"
]
},
{
- "id": 627,
- "entry": "F000:079D",
- "term": "F000:07AF",
+ "id": 224,
+ "entry": "F000:013E",
+ "term": "F000:0141",
"pred": [
- 624
+ 220
],
"succ": [
- 635
+ 216,
+ 240
],
"asm": [
- "66BEFFFF0100|mov ESI,0x0001FFFF",
- "B078|mov AL,0x78",
- "26678803|mov byte ptr ES:[EBX],AL",
- "6631C0|xor EAX,EAX",
- "AC|lods AL,byte ptr DS:[SI]",
- "3C78|cmp AL,0x78",
- "0F85140E|jne near 0x15C7"
+ "B440|mov AH,0x40",
+ "9E|sahf",
+ "0F85AB00|jne near 0x01F0"
]
},
{
- "id": 695,
- "entry": "F000:0881",
- "term": "F000:0895",
+ "id": 229,
+ "entry": "F000:014A",
+ "term": "F000:014D",
"pred": [
- 692
+ 226
],
"succ": [
- 703
+ 216,
+ 252
],
"asm": [
- "66BEFEFF0100|mov ESI,0x0001FFFE",
- "B87856|mov AX,0x5678",
- "26678903|mov word ptr ES:[EBX],AX",
- "6631C0|xor EAX,EAX",
- "AD|lods AX,word ptr DS:[SI]",
- "3D7856|cmp AX,0x5678",
- "0F852E0D|jne near 0x15C7"
+ "B404|mov AH,4",
+ "9E|sahf",
+ "0F8B9F00|jnp near 0x01F0"
]
},
{
- "id": 635,
- "entry": "F000:07B3",
- "term": "F000:07BA",
+ "id": 235,
+ "entry": "F000:0156",
+ "term": "F000:0159",
"pred": [
- 627
+ 231
],
"succ": [
- 638
+ 216,
+ 264
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85090E|jne near 0x15C7"
+ "B480|mov AH,0x80",
+ "9E|sahf",
+ "0F899300|jns near 0x01F0"
]
},
{
- "id": 692,
- "entry": "F000:0876",
- "term": "F000:087D",
+ "id": 244,
+ "entry": "F000:0162",
+ "term": "F000:0165",
"pred": [
- 689
+ 237
],
"succ": [
- 695
+ 216,
+ 273
],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85460D|jne near 0x15C7"
+ "B441|mov AH,0x41",
+ "9E|sahf",
+ "0F878700|ja near 0x01F0"
]
},
{
- "id": 638,
- "entry": "F000:07BE",
- "term": "F000:07DA",
+ "id": 220,
+ "entry": "F000:01F1",
+ "term": "F000:01F1",
"pred": [
- 635
+ 218
],
"succ": [
- 648
+ 224,
+ 226
],
"asm": [
- "FC|cld",
- "66BFFEFF0100|mov EDI,0x0001FFFE",
- "66BBFEFF0000|mov EBX,0x0000FFFE",
- "B80000|mov AX,0",
- "26678903|mov word ptr ES:[EBX],AX",
- "B87856|mov AX,0x5678",
- "AB|stos word ptr ES:[DI],AX",
- "26673903|cmp word ptr ES:[EBX],AX",
- "0F85E90D|jne near 0x15C7"
+ "0F8249FF|jb near 0x013E"
]
},
{
- "id": 689,
- "entry": "F000:086B",
- "term": "F000:0872",
+ "id": 222,
+ "entry": "F000:013D",
+ "dead": true,
+ "term": "F000:013D",
"pred": [
- 678
- ],
- "succ": [
- 692
+ 218
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85510D|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 648,
- "entry": "F000:07DE",
- "term": "F000:07E5",
+ "id": 231,
+ "entry": "F000:01F9",
+ "term": "F000:01F9",
"pred": [
- 638
+ 226,
+ 252
],
"succ": [
- 651
+ 235,
+ 237
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85DE0D|jne near 0x15C7"
+ "0F8A59FF|jp near 0x0156"
]
},
{
- "id": 678,
- "entry": "F000:0845",
- "term": "F000:0867",
+ "id": 264,
+ "entry": "F000:015D",
+ "term": "F000:015D",
"pred": [
- 675
+ 235
],
"succ": [
- 689
+ 237,
+ 270
],
"asm": [
- "66BEFEFF0100|mov ESI,0x0001FFFE",
- "66BFFEFF0100|mov EDI,0x0001FFFE",
- "B87856|mov AX,0x5678",
- "3E678903|mov word ptr DS:[EBX],AX",
- "B80000|mov AX,0",
- "26678903|mov word ptr ES:[EBX],AX",
- "A5|movs word ptr ES:[DI],word ptr DS:[SI]",
- "B87856|mov AX,0x5678",
- "26673903|cmp word ptr ES:[EBX],AX",
- "0F855C0D|jne near 0x15C7"
+ "0F889C00|js near 0x01FD"
]
},
{
- "id": 651,
- "entry": "F000:07E9",
- "term": "F000:0806",
+ "id": 240,
+ "entry": "F000:0145",
+ "term": "F000:0145",
"pred": [
- 648
+ 224
],
"succ": [
- 659
+ 226,
+ 249
],
"asm": [
- "66BEFEFF0100|mov ESI,0x0001FFFE",
- "66BFFEFF0100|mov EDI,0x0001FFFE",
- "66BBFEFF0000|mov EBX,0x0000FFFE",
- "3E678903|mov word ptr DS:[EBX],AX",
- "26678903|mov word ptr ES:[EBX],AX",
- "83F800|cmp AX,0",
- "0F84BD0D|je near 0x15C7"
+ "0F84AC00|je near 0x01F5"
]
},
{
- "id": 675,
- "entry": "F000:083A",
- "term": "F000:0841",
+ "id": 252,
+ "entry": "F000:0151",
+ "term": "F000:0151",
"pred": [
- 668
+ 229
],
"succ": [
- 678
+ 231,
+ 261
],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85820D|jne near 0x15C7"
+ "0F8AA400|jp near 0x01F9"
]
},
{
- "id": 659,
- "entry": "F000:080A",
- "term": "F000:080B",
+ "id": 226,
+ "entry": "F000:01F5",
+ "term": "F000:01F5",
"pred": [
- 651
+ 220,
+ 240
],
"succ": [
- 662
+ 229,
+ 231
],
"asm": [
- "A7|cmps word ptr DS:[SI],word ptr ES:[DI]",
- "0F85B80D|jne near 0x15C7"
+ "0F8451FF|je near 0x014A"
]
},
{
- "id": 668,
- "entry": "F000:0825",
- "term": "F000:0836",
+ "id": 270,
+ "entry": "F000:0161",
+ "dead": true,
+ "term": "F000:0161",
"pred": [
- 665
- ],
- "succ": [
- 675
+ 264
],
+ "succ": [],
"asm": [
- "66BFFEFF0100|mov EDI,0x0001FFFE",
- "B87856|mov AX,0x5678",
- "26678903|mov word ptr ES:[EBX],AX",
- "83F800|cmp AX,0",
- "AF|scas AX,word ptr ES:[DI]",
- "0F858D0D|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 662,
- "entry": "F000:080F",
- "term": "F000:0816",
+ "id": 249,
+ "entry": "F000:0149",
+ "dead": true,
+ "term": "F000:0149",
"pred": [
- 659
- ],
- "succ": [
- 665
+ 240
],
+ "succ": [],
"asm": [
- "6681FF00000100|cmp EDI,0x00010000",
- "0F85AD0D|jne near 0x15C7"
+ "F4|hlt"
]
},
{
- "id": 665,
- "entry": "F000:081A",
- "term": "F000:0821",
+ "id": 261,
+ "entry": "F000:0155",
+ "dead": true,
+ "term": "F000:0155",
"pred": [
- 662
- ],
- "succ": [
- 668
+ 252
],
+ "succ": [],
"asm": [
- "6681FE00000100|cmp ESI,0x00010000",
- "0F85A20D|jne near 0x15C7"
+ "F4|hlt"
]
}
],
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bcdcnv.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bcdcnv.json
index 89882ac3a7..9ac7cdbffe 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bcdcnv.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bcdcnv.json
@@ -7,21 +7,21 @@
"blocks": [
3,
16,
- 18,
- 40,
- 42,
- 64,
- 66,
- 72,
- 74,
- 80,
- 82,
- 104,
- 106,
- 112,
- 114,
- 120,
+ 32,
+ 53,
+ 77,
+ 98,
122,
+ 127,
+ 135,
+ 140,
+ 148,
+ 169,
+ 193,
+ 198,
+ 206,
+ 211,
+ 219,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bitwise.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bitwise.json
index d30d14703c..801df41d37 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bitwise.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/bitwise.json
@@ -7,7 +7,7 @@
"blocks": [
3,
164,
- 166,
+ 328,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/control.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/control.json
index cde2aee55f..6483ffe713 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/control.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/control.json
@@ -7,11 +7,11 @@
"blocks": [
3,
9,
- 11,
- 14,
- 21,
- 23,
- 28,
+ 18,
+ 20,
+ 27,
+ 38,
+ 46,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/datatrnf.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/datatrnf.json
index 640e4745b9..798fd0ee65 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/datatrnf.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/datatrnf.json
@@ -7,7 +7,7 @@
"blocks": [
3,
39,
- 41,
+ 78,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/div.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/div.json
index 9d263124d0..39b0569c6e 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/div.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/div.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 47
+ 297
],
"entries": [
{
- "block": 47,
+ "block": 297,
"address": "F000:0061",
"kind": "returnTargetEntry"
}
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 58
+ 321
],
"entries": [
{
- "block": 58,
+ "block": 321,
"address": "F000:0085",
"kind": "returnTargetEntry"
}
@@ -35,11 +35,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 68
+ 331
],
"entries": [
{
- "block": 68,
+ "block": 331,
"address": "F000:00A0",
"kind": "returnTargetEntry"
}
@@ -50,11 +50,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 104
+ 367
],
"entries": [
{
- "block": 104,
+ "block": 367,
"address": "F000:010F",
"kind": "returnTargetEntry"
}
@@ -65,11 +65,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 113
+ 376
],
"entries": [
{
- "block": 113,
+ "block": 376,
"address": "F000:012C",
"kind": "returnTargetEntry"
}
@@ -80,11 +80,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 149
+ 412
],
"entries": [
{
- "block": 149,
+ "block": 412,
"address": "F000:019B",
"kind": "returnTargetEntry"
}
@@ -95,11 +95,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 187
+ 450
],
"entries": [
{
- "block": 187,
+ "block": 450,
"address": "F000:0211",
"kind": "returnTargetEntry"
}
@@ -110,11 +110,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 206
+ 469
],
"entries": [
{
- "block": 206,
+ "block": 469,
"address": "F000:024D",
"kind": "returnTargetEntry"
}
@@ -125,11 +125,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 239
+ 502
],
"entries": [
{
- "block": 239,
+ "block": 502,
"address": "F000:02B1",
"kind": "returnTargetEntry"
}
@@ -140,11 +140,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 247
+ 510
],
"entries": [
{
- "block": 247,
+ "block": 510,
"address": "F000:02C6",
"kind": "returnTargetEntry"
}
@@ -155,11 +155,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 265
+ 528
],
"entries": [
{
- "block": 265,
+ "block": 528,
"address": "F000:02F6",
"kind": "returnTargetEntry"
}
@@ -170,11 +170,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 32
+ 284
],
"entries": [
{
- "block": 32,
+ "block": 284,
"address": "F000:1000",
"kind": "executionContextEntry"
}
@@ -202,8 +202,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 47,
- "toBlock": 58,
+ "fromBlock": 297,
+ "toBlock": 321,
"from": "F000:0081",
"target": "F000:0085"
},
@@ -211,8 +211,8 @@
"kind": "cpuFault",
"fromPartition": 1,
"toPartition": 12,
- "fromBlock": 47,
- "toBlock": 32,
+ "fromBlock": 297,
+ "toBlock": 284,
"from": "F000:0081",
"target": "F000:1000"
},
@@ -220,8 +220,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 58,
- "toBlock": 68,
+ "fromBlock": 321,
+ "toBlock": 331,
"from": "F000:009E",
"target": "F000:00A0"
},
@@ -229,8 +229,8 @@
"kind": "cpuFault",
"fromPartition": 2,
"toPartition": 12,
- "fromBlock": 58,
- "toBlock": 32,
+ "fromBlock": 321,
+ "toBlock": 284,
"from": "F000:009E",
"target": "F000:1000"
},
@@ -238,8 +238,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 3,
"toPartition": 4,
- "fromBlock": 68,
- "toBlock": 104,
+ "fromBlock": 331,
+ "toBlock": 367,
"from": "F000:010D",
"target": "F000:010F"
},
@@ -247,8 +247,8 @@
"kind": "cpuFault",
"fromPartition": 3,
"toPartition": 12,
- "fromBlock": 68,
- "toBlock": 32,
+ "fromBlock": 331,
+ "toBlock": 284,
"from": "F000:010D",
"target": "F000:1000"
},
@@ -256,8 +256,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 4,
"toPartition": 5,
- "fromBlock": 104,
- "toBlock": 113,
+ "fromBlock": 367,
+ "toBlock": 376,
"from": "F000:0128",
"target": "F000:012C"
},
@@ -265,8 +265,8 @@
"kind": "cpuFault",
"fromPartition": 4,
"toPartition": 12,
- "fromBlock": 104,
- "toBlock": 32,
+ "fromBlock": 367,
+ "toBlock": 284,
"from": "F000:0128",
"target": "F000:1000"
},
@@ -274,8 +274,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 5,
"toPartition": 6,
- "fromBlock": 113,
- "toBlock": 149,
+ "fromBlock": 376,
+ "toBlock": 412,
"from": "F000:0197",
"target": "F000:019B"
},
@@ -283,8 +283,8 @@
"kind": "cpuFault",
"fromPartition": 5,
"toPartition": 12,
- "fromBlock": 113,
- "toBlock": 32,
+ "fromBlock": 376,
+ "toBlock": 284,
"from": "F000:0197",
"target": "F000:1000"
},
@@ -292,8 +292,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 6,
"toPartition": 7,
- "fromBlock": 149,
- "toBlock": 187,
+ "fromBlock": 412,
+ "toBlock": 450,
"from": "F000:020F",
"target": "F000:0211"
},
@@ -301,8 +301,8 @@
"kind": "cpuFault",
"fromPartition": 6,
"toPartition": 12,
- "fromBlock": 149,
- "toBlock": 32,
+ "fromBlock": 412,
+ "toBlock": 284,
"from": "F000:020F",
"target": "F000:1000"
},
@@ -310,8 +310,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 7,
"toPartition": 8,
- "fromBlock": 187,
- "toBlock": 206,
+ "fromBlock": 450,
+ "toBlock": 469,
"from": "F000:0249",
"target": "F000:024D"
},
@@ -319,8 +319,8 @@
"kind": "cpuFault",
"fromPartition": 7,
"toPartition": 12,
- "fromBlock": 187,
- "toBlock": 32,
+ "fromBlock": 450,
+ "toBlock": 284,
"from": "F000:0249",
"target": "F000:1000"
},
@@ -328,8 +328,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 8,
"toPartition": 9,
- "fromBlock": 206,
- "toBlock": 239,
+ "fromBlock": 469,
+ "toBlock": 502,
"from": "F000:02AF",
"target": "F000:02B1"
},
@@ -337,8 +337,8 @@
"kind": "cpuFault",
"fromPartition": 8,
"toPartition": 12,
- "fromBlock": 206,
- "toBlock": 32,
+ "fromBlock": 469,
+ "toBlock": 284,
"from": "F000:02AF",
"target": "F000:1000"
},
@@ -346,8 +346,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 9,
"toPartition": 10,
- "fromBlock": 239,
- "toBlock": 247,
+ "fromBlock": 502,
+ "toBlock": 510,
"from": "F000:02C4",
"target": "F000:02C6"
},
@@ -355,8 +355,8 @@
"kind": "cpuFault",
"fromPartition": 9,
"toPartition": 12,
- "fromBlock": 239,
- "toBlock": 32,
+ "fromBlock": 502,
+ "toBlock": 284,
"from": "F000:02C4",
"target": "F000:1000"
},
@@ -364,8 +364,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 10,
"toPartition": 11,
- "fromBlock": 247,
- "toBlock": 265,
+ "fromBlock": 510,
+ "toBlock": 528,
"from": "F000:02F4",
"target": "F000:02F6"
},
@@ -373,8 +373,8 @@
"kind": "cpuFault",
"fromPartition": 10,
"toPartition": 12,
- "fromBlock": 247,
- "toBlock": 32,
+ "fromBlock": 510,
+ "toBlock": 284,
"from": "F000:02F4",
"target": "F000:1000"
},
@@ -382,8 +382,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 1,
- "fromBlock": 32,
- "toBlock": 47,
+ "fromBlock": 284,
+ "toBlock": 297,
"from": "F000:101A",
"target": "F000:0061"
},
@@ -391,8 +391,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 2,
- "fromBlock": 32,
- "toBlock": 58,
+ "fromBlock": 284,
+ "toBlock": 321,
"from": "F000:101A",
"target": "F000:0085"
},
@@ -400,8 +400,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 3,
- "fromBlock": 32,
- "toBlock": 68,
+ "fromBlock": 284,
+ "toBlock": 331,
"from": "F000:101A",
"target": "F000:00A0"
},
@@ -409,8 +409,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 4,
- "fromBlock": 32,
- "toBlock": 104,
+ "fromBlock": 284,
+ "toBlock": 367,
"from": "F000:101A",
"target": "F000:010F"
},
@@ -418,8 +418,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 5,
- "fromBlock": 32,
- "toBlock": 113,
+ "fromBlock": 284,
+ "toBlock": 376,
"from": "F000:101A",
"target": "F000:012C"
},
@@ -427,8 +427,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 6,
- "fromBlock": 32,
- "toBlock": 149,
+ "fromBlock": 284,
+ "toBlock": 412,
"from": "F000:101A",
"target": "F000:019B"
},
@@ -436,8 +436,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 7,
- "fromBlock": 32,
- "toBlock": 187,
+ "fromBlock": 284,
+ "toBlock": 450,
"from": "F000:101A",
"target": "F000:0211"
},
@@ -445,8 +445,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 8,
- "fromBlock": 32,
- "toBlock": 206,
+ "fromBlock": 284,
+ "toBlock": 469,
"from": "F000:101A",
"target": "F000:024D"
},
@@ -454,8 +454,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 9,
- "fromBlock": 32,
- "toBlock": 239,
+ "fromBlock": 284,
+ "toBlock": 502,
"from": "F000:101A",
"target": "F000:02B1"
},
@@ -463,8 +463,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 10,
- "fromBlock": 32,
- "toBlock": 247,
+ "fromBlock": 284,
+ "toBlock": 510,
"from": "F000:101A",
"target": "F000:02C6"
},
@@ -472,8 +472,8 @@
"kind": "dynamicReturn",
"fromPartition": 12,
"toPartition": 11,
- "fromBlock": 32,
- "toBlock": 265,
+ "fromBlock": 284,
+ "toBlock": 528,
"from": "F000:101A",
"target": "F000:02F6"
},
@@ -482,7 +482,7 @@
"fromPartition": 13,
"toPartition": 1,
"fromBlock": 3,
- "toBlock": 47,
+ "toBlock": 297,
"from": "F000:005F",
"target": "F000:0061"
},
@@ -491,7 +491,7 @@
"fromPartition": 13,
"toPartition": 12,
"fromBlock": 3,
- "toBlock": 32,
+ "toBlock": 284,
"from": "F000:005F",
"target": "F000:1000"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/divfaultloop.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/divfaultloop.json
index 55e397b117..c664e95b19 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/divfaultloop.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/divfaultloop.json
@@ -5,12 +5,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 27,
- 31
+ 56,
+ 44
],
"entries": [
{
- "block": 27,
+ "block": 56,
"address": "F000:0020",
"kind": "returnTargetEntry"
}
@@ -21,13 +21,13 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 14,
29,
- 23
+ 39,
+ 37
],
"entries": [
{
- "block": 14,
+ "block": 29,
"address": "F000:0037",
"kind": "executionContextEntry"
}
@@ -55,8 +55,8 @@
"kind": "cpuFault",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 27,
- "toBlock": 14,
+ "fromBlock": 56,
+ "toBlock": 29,
"from": "F000:0028",
"target": "F000:0037"
},
@@ -64,8 +64,8 @@
"kind": "dynamicReturn",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 23,
- "toBlock": 27,
+ "fromBlock": 37,
+ "toBlock": 56,
"from": "F000:0059",
"target": "F000:0020"
},
@@ -74,7 +74,7 @@
"fromPartition": 3,
"toPartition": 1,
"fromBlock": 3,
- "toBlock": 27,
+ "toBlock": 56,
"from": "F000:0019",
"target": "F000:0020"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/externalint.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/externalint.json
index eff2077da6..76dd0fb39c 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/externalint.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/externalint.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 27
+ 53
],
"entries": [
{
- "block": 27,
+ "block": 53,
"address": "F000:003F",
"kind": "executionContextEntry"
}
@@ -21,9 +21,9 @@
"name": "unknown",
"blocks": [
3,
- 19,
- 24,
- 33,
+ 35,
+ 40,
+ 42,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/interrupt.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/interrupt.json
index ca705a423e..f1dc638d5f 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/interrupt.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/interrupt.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 17
+ 28
],
"entries": [
{
- "block": 17,
+ "block": 28,
"address": "E342:EBE0",
"kind": "functionEntry"
}
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 38
+ 62
],
"entries": [
{
- "block": 38,
+ "block": 62,
"address": "F000:3001",
"kind": "functionEntry"
}
@@ -35,13 +35,13 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 44,
- 48,
- 50
+ 72,
+ 75,
+ 81
],
"entries": [
{
- "block": 44,
+ "block": 72,
"address": "F000:4002",
"kind": "returnTargetEntry"
}
@@ -52,11 +52,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 53
+ 85
],
"entries": [
{
- "block": 53,
+ "block": 85,
"address": "F000:4013",
"kind": "functionEntry"
}
@@ -69,10 +69,10 @@
"blocks": [
3,
12,
- 14,
- 23,
- 26,
- 33,
+ 24,
+ 38,
+ 42,
+ 54,
2
],
"entries": [
@@ -89,8 +89,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 5,
- "fromBlock": 17,
- "toBlock": 23,
+ "fromBlock": 28,
+ "toBlock": 38,
"from": "E342:EBE8",
"target": "F000:0022"
},
@@ -98,8 +98,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 5,
- "fromBlock": 17,
- "toBlock": 33,
+ "fromBlock": 28,
+ "toBlock": 54,
"from": "E342:EBE8",
"target": "F000:0CEC"
},
@@ -107,8 +107,8 @@
"kind": "dynamicReturn",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 38,
- "toBlock": 44,
+ "fromBlock": 62,
+ "toBlock": 72,
"from": "F000:300B",
"target": "F000:4002"
},
@@ -116,8 +116,8 @@
"kind": "callOut",
"fromPartition": 3,
"toPartition": 4,
- "fromBlock": 50,
- "toBlock": 53,
+ "fromBlock": 81,
+ "toBlock": 85,
"from": "F000:4012",
"target": "F000:4013"
},
@@ -125,41 +125,41 @@
"kind": "callOut",
"fromPartition": 5,
"toPartition": 1,
- "fromBlock": 14,
- "toBlock": 17,
+ "fromBlock": 24,
+ "toBlock": 28,
"from": "F000:0020",
"target": "E342:EBE0",
- "callContinuationBlock": 23,
+ "callContinuationBlock": 38,
"callContinuationAddress": "F000:0022"
},
{
"kind": "callOut",
"fromPartition": 5,
"toPartition": 1,
- "fromBlock": 26,
- "toBlock": 17,
+ "fromBlock": 42,
+ "toBlock": 28,
"from": "F000:0CEA",
"target": "E342:EBE0",
- "callContinuationBlock": 33,
+ "callContinuationBlock": 54,
"callContinuationAddress": "F000:0CEC"
},
{
"kind": "callOut",
"fromPartition": 5,
"toPartition": 2,
- "fromBlock": 33,
- "toBlock": 38,
+ "fromBlock": 54,
+ "toBlock": 62,
"from": "F000:0CFD",
"target": "F000:3001",
- "callContinuationBlock": 44,
+ "callContinuationBlock": 72,
"callContinuationAddress": "F000:4002"
},
{
"kind": "crossPartitionFlow",
"fromPartition": 5,
"toPartition": 3,
- "fromBlock": 33,
- "toBlock": 44,
+ "fromBlock": 54,
+ "toBlock": 72,
"from": "F000:0CFD",
"target": "F000:4002"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jmpmov.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jmpmov.json
index e8d7076745..e9a2e3cb9b 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jmpmov.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jmpmov.json
@@ -7,10 +7,10 @@
"blocks": [
3,
5,
- 10,
- 12,
- 26,
- 42,
+ 16,
+ 17,
+ 45,
+ 75,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump1.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump1.json
index 64b5e22069..c18c8f4ec6 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump1.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump1.json
@@ -6,79 +6,97 @@
"name": "unknown",
"blocks": [
3,
- 10,
13,
- 16,
- 19,
+ 10,
+ 15,
22,
- 24,
+ 20,
27,
- 30,
32,
- 34,
- 36,
- 38,
- 40,
- 42,
- 44,
+ 30,
+ 35,
+ 41,
+ 39,
46,
- 49,
- 51,
- 53,
- 56,
- 58,
- 60,
- 63,
- 65,
- 67,
69,
- 71,
- 73,
- 75,
- 78,
+ 72,
+ 70,
+ 74,
80,
82,
84,
- 86,
- 88,
- 90,
+ 89,
92,
- 94,
+ 90,
96,
- 99,
- 101,
- 103,
- 106,
- 108,
- 110,
+ 102,
+ 104,
+ 107,
113,
- 115,
- 117,
+ 116,
+ 114,
120,
- 122,
- 124,
126,
128,
130,
- 133,
135,
- 137,
- 139,
+ 138,
+ 136,
141,
- 143,
145,
- 148,
- 150,
152,
- 154,
+ 155,
+ 153,
157,
- 159,
- 161,
163,
- 166,
- 168,
- 170,
- 2
+ 165,
+ 167,
+ 172,
+ 175,
+ 173,
+ 178,
+ 182,
+ 189,
+ 192,
+ 190,
+ 196,
+ 202,
+ 204,
+ 207,
+ 213,
+ 216,
+ 214,
+ 220,
+ 226,
+ 228,
+ 230,
+ 235,
+ 238,
+ 236,
+ 242,
+ 248,
+ 250,
+ 252,
+ 257,
+ 260,
+ 258,
+ 263,
+ 267,
+ 274,
+ 277,
+ 275,
+ 280,
+ 284,
+ 291,
+ 294,
+ 292,
+ 297,
+ 301,
+ 308,
+ 311,
+ 309,
+ 2,
+ 17
],
"entries": [
{
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump2.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump2.json
index 7884354874..9eb088fd20 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump2.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/jump2.json
@@ -6,9 +6,9 @@
"name": "unknown",
"blocks": [
5,
- 11,
- 13,
- 57
+ 12,
+ 14,
+ 93
],
"entries": [
{
@@ -23,18 +23,20 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 15,
- 18,
- 21,
- 23,
- 25,
- 50,
- 52,
- 55
+ 26,
+ 29,
+ 32,
+ 39,
+ 42,
+ 40,
+ 81,
+ 83,
+ 88,
+ 86
],
"entries": [
{
- "block": 15,
+ "block": 26,
"address": "F000:1290",
"kind": "functionEntry"
}
@@ -45,16 +47,17 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 27,
- 38,
- 41,
- 43,
- 46,
- 48
+ 44,
+ 61,
+ 63,
+ 68,
+ 71,
+ 75,
+ 73
],
"entries": [
{
- "block": 27,
+ "block": 44,
"address": "E342:EBE0",
"kind": "functionEntry"
}
@@ -65,12 +68,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 29,
- 36
+ 49,
+ 58
],
"entries": [
{
- "block": 29,
+ "block": 49,
"address": "E342:FDE0",
"kind": "functionEntry"
}
@@ -81,11 +84,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 34
+ 56
],
"entries": [
{
- "block": 34,
+ "block": 56,
"address": "F000:4000",
"kind": "functionEntry"
}
@@ -97,7 +100,7 @@
"name": "unknown",
"blocks": [
2,
- 59
+ 96
],
"entries": [
{
@@ -113,19 +116,19 @@
"kind": "callOut",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 13,
- "toBlock": 15,
+ "fromBlock": 14,
+ "toBlock": 26,
"from": "F000:000E",
"target": "F000:1290",
- "callContinuationBlock": 57,
+ "callContinuationBlock": 93,
"callContinuationAddress": "F000:0010"
},
{
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 6,
- "fromBlock": 57,
- "toBlock": 59,
+ "fromBlock": 93,
+ "toBlock": 96,
"from": "F000:0010",
"target": "F000:FFF8"
},
@@ -133,8 +136,8 @@
"kind": "alignedReturn",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 55,
- "toBlock": 57,
+ "fromBlock": 86,
+ "toBlock": 93,
"from": "F000:12AA",
"target": "F000:0010"
},
@@ -142,19 +145,19 @@
"kind": "callOut",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 25,
- "toBlock": 27,
+ "fromBlock": 40,
+ "toBlock": 44,
"from": "F000:129D",
"target": "E342:EBE0",
- "callContinuationBlock": 50,
+ "callContinuationBlock": 81,
"callContinuationAddress": "F000:12A2"
},
{
"kind": "alignedReturn",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 48,
- "toBlock": 50,
+ "fromBlock": 73,
+ "toBlock": 81,
"from": "E342:EBF1",
"target": "F000:12A2"
},
@@ -162,19 +165,19 @@
"kind": "callOut",
"fromPartition": 3,
"toPartition": 4,
- "fromBlock": 27,
- "toBlock": 29,
+ "fromBlock": 44,
+ "toBlock": 49,
"from": "E342:EBE0",
"target": "E342:FDE0",
- "callContinuationBlock": 38,
+ "callContinuationBlock": 61,
"callContinuationAddress": "E342:EBE4"
},
{
"kind": "alignedReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 36,
- "toBlock": 38,
+ "fromBlock": 58,
+ "toBlock": 61,
"from": "E342:FDEA",
"target": "E342:EBE4"
},
@@ -182,19 +185,19 @@
"kind": "callOut",
"fromPartition": 4,
"toPartition": 5,
- "fromBlock": 29,
- "toBlock": 34,
+ "fromBlock": 49,
+ "toBlock": 56,
"from": "E342:FDE7",
"target": "F000:4000",
- "callContinuationBlock": 36,
+ "callContinuationBlock": 58,
"callContinuationAddress": "E342:FDEA"
},
{
"kind": "alignedReturn",
"fromPartition": 5,
"toPartition": 4,
- "fromBlock": 34,
- "toBlock": 36,
+ "fromBlock": 56,
+ "toBlock": 58,
"from": "F000:4000",
"target": "E342:FDEA"
},
@@ -206,7 +209,7 @@
"toBlock": 5,
"from": "F000:FFF5",
"target": "F000:0000",
- "callContinuationBlock": 59,
+ "callContinuationBlock": 96,
"callContinuationAddress": "F000:FFF8"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/linearsamesegmenteddifferent.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/linearsamesegmenteddifferent.json
index 6910e553e1..ff5fccb466 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/linearsamesegmenteddifferent.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/linearsamesegmenteddifferent.json
@@ -5,12 +5,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 18,
- 20
+ 30,
+ 31
],
"entries": [
{
- "block": 18,
+ "block": 30,
"address": "FFEF:000D",
"kind": "returnTargetEntry"
}
@@ -21,12 +21,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 13,
- 11
+ 20,
+ 19
],
"entries": [
{
- "block": 11,
+ "block": 19,
"address": "F000:FEFD",
"kind": "returnTargetEntry"
}
@@ -54,8 +54,8 @@
"kind": "dynamicReturn",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 13,
- "toBlock": 18,
+ "fromBlock": 20,
+ "toBlock": 30,
"from": "F000:0007",
"target": "FFEF:000D"
},
@@ -64,7 +64,7 @@
"fromPartition": 3,
"toPartition": 2,
"fromBlock": 3,
- "toBlock": 11,
+ "toBlock": 19,
"from": "F000:0019",
"target": "F000:FEFD"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/lockprefix.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/lockprefix.json
index 59d854d3bb..9e5edd4c91 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/lockprefix.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/lockprefix.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 28
+ 52
],
"entries": [
{
- "block": 28,
+ "block": 52,
"address": "F000:0045",
"kind": "returnTargetEntry"
}
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 32
+ 58
],
"entries": [
{
- "block": 32,
+ "block": 58,
"address": "F000:004E",
"kind": "returnTargetEntry"
}
@@ -35,11 +35,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 36
+ 62
],
"entries": [
{
- "block": 36,
+ "block": 62,
"address": "F000:0057",
"kind": "returnTargetEntry"
}
@@ -50,11 +50,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 20
+ 37
],
"entries": [
{
- "block": 20,
+ "block": 37,
"address": "F000:005A",
"kind": "executionContextEntry"
}
@@ -82,8 +82,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 28,
- "toBlock": 32,
+ "fromBlock": 52,
+ "toBlock": 58,
"from": "F000:004B",
"target": "F000:004E"
},
@@ -91,8 +91,8 @@
"kind": "cpuFault",
"fromPartition": 1,
"toPartition": 4,
- "fromBlock": 28,
- "toBlock": 20,
+ "fromBlock": 52,
+ "toBlock": 37,
"from": "F000:004B",
"target": "F000:005A"
},
@@ -100,8 +100,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 32,
- "toBlock": 36,
+ "fromBlock": 58,
+ "toBlock": 62,
"from": "F000:0054",
"target": "F000:0057"
},
@@ -109,8 +109,8 @@
"kind": "cpuFault",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 32,
- "toBlock": 20,
+ "fromBlock": 58,
+ "toBlock": 37,
"from": "F000:0054",
"target": "F000:005A"
},
@@ -118,8 +118,8 @@
"kind": "dynamicReturn",
"fromPartition": 4,
"toPartition": 1,
- "fromBlock": 20,
- "toBlock": 28,
+ "fromBlock": 37,
+ "toBlock": 52,
"from": "F000:0068",
"target": "F000:0045"
},
@@ -127,8 +127,8 @@
"kind": "dynamicReturn",
"fromPartition": 4,
"toPartition": 2,
- "fromBlock": 20,
- "toBlock": 32,
+ "fromBlock": 37,
+ "toBlock": 58,
"from": "F000:0068",
"target": "F000:004E"
},
@@ -136,8 +136,8 @@
"kind": "dynamicReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 20,
- "toBlock": 36,
+ "fromBlock": 37,
+ "toBlock": 62,
"from": "F000:0068",
"target": "F000:0057"
},
@@ -146,7 +146,7 @@
"fromPartition": 5,
"toPartition": 1,
"fromBlock": 3,
- "toBlock": 28,
+ "toBlock": 52,
"from": "F000:0040",
"target": "F000:0045"
},
@@ -155,7 +155,7 @@
"fromPartition": 5,
"toPartition": 4,
"fromBlock": 3,
- "toBlock": 20,
+ "toBlock": 37,
"from": "F000:0040",
"target": "F000:005A"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/ownerlesscallcycle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/ownerlesscallcycle.json
index 10dfb29f01..6ba8fcb109 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/ownerlesscallcycle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/ownerlesscallcycle.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 18
+ 27
],
"entries": [
{
- "block": 18,
+ "block": 27,
"address": "F000:0024",
"kind": "functionEntry"
}
@@ -36,13 +36,13 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 16,
- 11,
- 20
+ 23,
+ 19,
+ 25
],
"entries": [
{
- "block": 11,
+ "block": 19,
"address": "F000:0016",
"kind": "returnTargetEntry"
}
@@ -54,8 +54,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 3,
- "fromBlock": 18,
- "toBlock": 11,
+ "fromBlock": 27,
+ "toBlock": 19,
"from": "F000:0024",
"target": "F000:0016"
},
@@ -64,7 +64,7 @@
"fromPartition": 2,
"toPartition": 3,
"fromBlock": 3,
- "toBlock": 11,
+ "toBlock": 19,
"from": "F000:0012",
"target": "F000:0016"
},
@@ -72,11 +72,11 @@
"kind": "callOut",
"fromPartition": 3,
"toPartition": 1,
- "fromBlock": 16,
- "toBlock": 18,
+ "fromBlock": 23,
+ "toBlock": 27,
"from": "F000:0013",
"target": "F000:0024",
- "callContinuationBlock": 11,
+ "callContinuationBlock": 19,
"callContinuationAddress": "F000:0016"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_cross_function_loop.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_cross_function_loop.json
index 0ecb72fa62..ab89969848 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_cross_function_loop.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_cross_function_loop.json
@@ -6,8 +6,8 @@
"name": "unknown",
"blocks": [
9,
- 12,
- 24
+ 14,
+ 12
],
"entries": [
{
@@ -22,13 +22,13 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 14,
- 22,
- 17
+ 16,
+ 21,
+ 19
],
"entries": [
{
- "block": 14,
+ "block": 16,
"address": "F000:001B",
"kind": "functionEntry"
}
@@ -40,8 +40,8 @@
"name": "unknown",
"blocks": [
3,
- 19,
- 26,
+ 36,
+ 42,
2
],
"entries": [
@@ -58,8 +58,8 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 12,
- "toBlock": 14,
+ "fromBlock": 14,
+ "toBlock": 16,
"from": "F000:0018",
"target": "F000:001B"
},
@@ -67,8 +67,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 3,
- "fromBlock": 24,
- "toBlock": 26,
+ "fromBlock": 12,
+ "toBlock": 42,
"from": "F000:001A",
"target": "F000:0014"
},
@@ -76,7 +76,7 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 22,
+ "fromBlock": 21,
"toBlock": 9,
"from": "F000:001E",
"target": "F000:0015"
@@ -85,8 +85,8 @@
"kind": "alignedReturn",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 17,
- "toBlock": 19,
+ "fromBlock": 19,
+ "toBlock": 36,
"from": "F000:0020",
"target": "F000:000E"
},
@@ -98,18 +98,18 @@
"toBlock": 9,
"from": "F000:000B",
"target": "F000:0015",
- "callContinuationBlock": 19,
+ "callContinuationBlock": 36,
"callContinuationAddress": "F000:000E"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 19,
- "toBlock": 14,
+ "fromBlock": 36,
+ "toBlock": 16,
"from": "F000:0011",
"target": "F000:001B",
- "callContinuationBlock": 26,
+ "callContinuationBlock": 42,
"callContinuationAddress": "F000:0014"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_indirect_call_jump.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_indirect_call_jump.json
index 378151ddfc..d1164dd666 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_indirect_call_jump.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_indirect_call_jump.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 9
+ 15
],
"entries": [
{
- "block": 9,
+ "block": 15,
"address": "F000:0012",
"kind": "functionEntry"
}
@@ -21,8 +21,8 @@
"name": "unknown",
"blocks": [
3,
- 12,
- 15,
+ 19,
+ 22,
2
],
"entries": [
@@ -39,8 +39,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 9,
- "toBlock": 12,
+ "fromBlock": 15,
+ "toBlock": 19,
"from": "F000:0015",
"target": "F000:000D"
},
@@ -49,10 +49,10 @@
"fromPartition": 2,
"toPartition": 1,
"fromBlock": 3,
- "toBlock": 9,
+ "toBlock": 15,
"from": "F000:000B",
"target": "F000:0012",
- "callContinuationBlock": 12,
+ "callContinuationBlock": 19,
"callContinuationAddress": "F000:000D"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_jump_into_function_middle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_jump_into_function_middle.json
index 25bebcf497..61227806d3 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_jump_into_function_middle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_jump_into_function_middle.json
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 14
+ 21
],
"entries": [
{
- "block": 14,
+ "block": 21,
"address": "F000:0016",
"kind": "functionEntry"
}
@@ -36,8 +36,8 @@
"name": "unknown",
"blocks": [
3,
- 12,
- 17,
+ 20,
+ 26,
2
],
"entries": [
@@ -53,11 +53,11 @@
"kind": "synthetic",
"name": "unknown",
"blocks": [
- 15
+ 23
],
"entries": [
{
- "block": 15,
+ "block": 23,
"address": "F000:0012",
"kind": "sharedEntry"
}
@@ -70,7 +70,7 @@
"fromPartition": 1,
"toPartition": 4,
"fromBlock": 8,
- "toBlock": 15,
+ "toBlock": 23,
"from": "F000:000F",
"target": "F000:0012"
},
@@ -78,8 +78,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 14,
- "toBlock": 15,
+ "fromBlock": 21,
+ "toBlock": 23,
"from": "F000:0016",
"target": "F000:0012"
},
@@ -91,26 +91,26 @@
"toBlock": 8,
"from": "F000:0008",
"target": "F000:000F",
- "callContinuationBlock": 12,
+ "callContinuationBlock": 20,
"callContinuationAddress": "F000:000B"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 12,
- "toBlock": 14,
+ "fromBlock": 20,
+ "toBlock": 21,
"from": "F000:000B",
"target": "F000:0016",
- "callContinuationBlock": 17,
+ "callContinuationBlock": 26,
"callContinuationAddress": "F000:000E"
},
{
"kind": "alignedReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 12,
+ "fromBlock": 23,
+ "toBlock": 20,
"from": "F000:0015",
"target": "F000:000B"
},
@@ -118,8 +118,8 @@
"kind": "alignedReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 17,
+ "fromBlock": 23,
+ "toBlock": 26,
"from": "F000:0015",
"target": "F000:000E"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_mixed_activation_cycle.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_mixed_activation_cycle.json
index 26daad8b32..dd2ad7cc84 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_mixed_activation_cycle.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_mixed_activation_cycle.json
@@ -6,7 +6,7 @@
"name": "unknown",
"blocks": [
13,
- 23,
+ 18,
16
],
"entries": [
@@ -23,7 +23,7 @@
"name": "unknown",
"blocks": [
9,
- 18
+ 31
],
"entries": [
{
@@ -54,8 +54,8 @@
"name": "unknown",
"blocks": [
3,
- 20,
- 25,
+ 34,
+ 39,
2
],
"entries": [
@@ -73,7 +73,7 @@
"fromPartition": 1,
"toPartition": 2,
"fromBlock": 16,
- "toBlock": 18,
+ "toBlock": 31,
"from": "F000:001A",
"target": "F000:001E"
},
@@ -81,7 +81,7 @@
"kind": "cyclicCrossPartitionFlow",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 23,
+ "fromBlock": 18,
"toBlock": 9,
"from": "F000:0018",
"target": "F000:001B"
@@ -94,15 +94,15 @@
"toBlock": 11,
"from": "F000:001B",
"target": "F000:001F",
- "callContinuationBlock": 18,
+ "callContinuationBlock": 31,
"callContinuationAddress": "F000:001E"
},
{
"kind": "alignedReturn",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 18,
- "toBlock": 20,
+ "fromBlock": 31,
+ "toBlock": 34,
"from": "F000:001E",
"target": "F000:000E"
},
@@ -110,8 +110,8 @@
"kind": "alignedReturn",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 18,
- "toBlock": 25,
+ "fromBlock": 31,
+ "toBlock": 39,
"from": "F000:001E",
"target": "F000:0014"
},
@@ -128,11 +128,11 @@
"kind": "callOut",
"fromPartition": 4,
"toPartition": 1,
- "fromBlock": 20,
+ "fromBlock": 34,
"toBlock": 13,
"from": "F000:0011",
"target": "F000:0015",
- "callContinuationBlock": 25,
+ "callContinuationBlock": 39,
"callContinuationAddress": "F000:0014"
},
{
@@ -143,7 +143,7 @@
"toBlock": 9,
"from": "F000:000B",
"target": "F000:001B",
- "callContinuationBlock": 20,
+ "callContinuationBlock": 34,
"callContinuationAddress": "F000:000E"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_dominated_shared.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_dominated_shared.json
index 18efbbbe54..a44d473d4f 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_dominated_shared.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_dominated_shared.json
@@ -6,7 +6,7 @@
"name": "unknown",
"blocks": [
9,
- 21
+ 14
],
"entries": [
{
@@ -21,12 +21,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 29,
- 35
+ 46,
+ 50
],
"entries": [
{
- "block": 29,
+ "block": 46,
"address": "F000:0023",
"kind": "functionEntry"
}
@@ -38,10 +38,10 @@
"name": "unknown",
"blocks": [
3,
- 18,
- 26,
- 32,
- 37,
+ 36,
+ 44,
+ 57,
+ 62,
2
],
"entries": [
@@ -72,11 +72,11 @@
"kind": "synthetic",
"name": "unknown",
"blocks": [
- 23
+ 17
],
"entries": [
{
- "block": 23,
+ "block": 17,
"address": "F000:002E",
"kind": "sharedEntry"
}
@@ -87,11 +87,11 @@
"kind": "synthetic",
"name": "unknown",
"blocks": [
- 15
+ 19
],
"entries": [
{
- "block": 15,
+ "block": 19,
"address": "F000:0033",
"kind": "sharedEntry"
}
@@ -112,8 +112,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 1,
"toPartition": 5,
- "fromBlock": 21,
- "toBlock": 23,
+ "fromBlock": 14,
+ "toBlock": 17,
"from": "F000:0021",
"target": "F000:002E"
},
@@ -121,7 +121,7 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 29,
+ "fromBlock": 46,
"toBlock": 12,
"from": "F000:0025",
"target": "F000:0029"
@@ -130,8 +130,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 5,
- "fromBlock": 35,
- "toBlock": 23,
+ "fromBlock": 50,
+ "toBlock": 17,
"from": "F000:0027",
"target": "F000:002E"
},
@@ -143,40 +143,40 @@
"toBlock": 9,
"from": "F000:000A",
"target": "F000:001D",
- "callContinuationBlock": 18,
+ "callContinuationBlock": 36,
"callContinuationAddress": "F000:000D"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 1,
- "fromBlock": 18,
+ "fromBlock": 36,
"toBlock": 9,
"from": "F000:000F",
"target": "F000:001D",
- "callContinuationBlock": 26,
+ "callContinuationBlock": 44,
"callContinuationAddress": "F000:0012"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 26,
- "toBlock": 29,
+ "fromBlock": 44,
+ "toBlock": 46,
"from": "F000:0014",
"target": "F000:0023",
- "callContinuationBlock": 32,
+ "callContinuationBlock": 57,
"callContinuationAddress": "F000:0017"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 32,
- "toBlock": 29,
+ "fromBlock": 57,
+ "toBlock": 46,
"from": "F000:0019",
"target": "F000:0023",
- "callContinuationBlock": 37,
+ "callContinuationBlock": 62,
"callContinuationAddress": "F000:001C"
},
{
@@ -184,7 +184,7 @@
"fromPartition": 4,
"toPartition": 6,
"fromBlock": 12,
- "toBlock": 15,
+ "toBlock": 19,
"from": "F000:002C",
"target": "F000:0033"
},
@@ -192,8 +192,8 @@
"kind": "crossPartitionFlow",
"fromPartition": 5,
"toPartition": 6,
- "fromBlock": 23,
- "toBlock": 15,
+ "fromBlock": 17,
+ "toBlock": 19,
"from": "F000:0031",
"target": "F000:0033"
},
@@ -201,8 +201,8 @@
"kind": "alignedReturn",
"fromPartition": 6,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 18,
+ "fromBlock": 19,
+ "toBlock": 36,
"from": "F000:0036",
"target": "F000:000D"
},
@@ -210,8 +210,8 @@
"kind": "alignedReturn",
"fromPartition": 6,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 26,
+ "fromBlock": 19,
+ "toBlock": 44,
"from": "F000:0036",
"target": "F000:0012"
},
@@ -219,8 +219,8 @@
"kind": "alignedReturn",
"fromPartition": 6,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 32,
+ "fromBlock": 19,
+ "toBlock": 57,
"from": "F000:0036",
"target": "F000:0017"
},
@@ -228,8 +228,8 @@
"kind": "alignedReturn",
"fromPartition": 6,
"toPartition": 3,
- "fromBlock": 15,
- "toBlock": 37,
+ "fromBlock": 19,
+ "toBlock": 62,
"from": "F000:0036",
"target": "F000:001C"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_irreducible_shared.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_irreducible_shared.json
index 6352d721c9..87f195574d 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_irreducible_shared.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_multi_entry_irreducible_shared.json
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 22
+ 36
],
"entries": [
{
- "block": 22,
+ "block": 36,
"address": "F000:0017",
"kind": "functionEntry"
}
@@ -36,8 +36,8 @@
"name": "unknown",
"blocks": [
3,
- 19,
- 26,
+ 34,
+ 42,
2
],
"entries": [
@@ -54,9 +54,9 @@
"name": "unknown",
"blocks": [
11,
- 24,
+ 16,
14,
- 17
+ 20
],
"entries": [
{
@@ -86,7 +86,7 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 22,
+ "fromBlock": 36,
"toBlock": 14,
"from": "F000:0017",
"target": "F000:001F"
@@ -99,37 +99,37 @@
"toBlock": 9,
"from": "F000:000B",
"target": "F000:0015",
- "callContinuationBlock": 19,
+ "callContinuationBlock": 34,
"callContinuationAddress": "F000:000E"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 19,
- "toBlock": 22,
+ "fromBlock": 34,
+ "toBlock": 36,
"from": "F000:0011",
"target": "F000:0017",
- "callContinuationBlock": 26,
+ "callContinuationBlock": 42,
"callContinuationAddress": "F000:0014"
},
{
"kind": "alignedReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 17,
- "toBlock": 19,
- "from": "F000:0024",
- "target": "F000:000E"
+ "fromBlock": 16,
+ "toBlock": 42,
+ "from": "F000:001E",
+ "target": "F000:0014"
},
{
"kind": "alignedReturn",
"fromPartition": 4,
"toPartition": 3,
- "fromBlock": 24,
- "toBlock": 26,
- "from": "F000:001E",
- "target": "F000:0014"
+ "fromBlock": 20,
+ "toBlock": 34,
+ "from": "F000:0024",
+ "target": "F000:000E"
}
]
}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_shared_tail.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_shared_tail.json
index 07939cb564..3ec1b4a336 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_shared_tail.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/partition_shared_tail.json
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 16
+ 24
],
"entries": [
{
- "block": 16,
+ "block": 24,
"address": "F000:0014",
"kind": "functionEntry"
}
@@ -36,8 +36,8 @@
"name": "unknown",
"blocks": [
3,
- 14,
- 19,
+ 23,
+ 30,
2
],
"entries": [
@@ -78,7 +78,7 @@
"kind": "crossPartitionFlow",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 16,
+ "fromBlock": 24,
"toBlock": 11,
"from": "F000:0017",
"target": "F000:0019"
@@ -91,18 +91,18 @@
"toBlock": 8,
"from": "F000:0008",
"target": "F000:000F",
- "callContinuationBlock": 14,
+ "callContinuationBlock": 23,
"callContinuationAddress": "F000:000B"
},
{
"kind": "callOut",
"fromPartition": 3,
"toPartition": 2,
- "fromBlock": 14,
- "toBlock": 16,
+ "fromBlock": 23,
+ "toBlock": 24,
"from": "F000:000B",
"target": "F000:0014",
- "callContinuationBlock": 19,
+ "callContinuationBlock": 30,
"callContinuationAddress": "F000:000E"
},
{
@@ -110,7 +110,7 @@
"fromPartition": 4,
"toPartition": 3,
"fromBlock": 11,
- "toBlock": 14,
+ "toBlock": 23,
"from": "F000:001C",
"target": "F000:000B"
},
@@ -119,7 +119,7 @@
"fromPartition": 4,
"toPartition": 3,
"fromBlock": 11,
- "toBlock": 19,
+ "toBlock": 30,
"from": "F000:001C",
"target": "F000:000E"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/rep.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/rep.json
index 3908b5e9f6..6c90efa99b 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/rep.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/rep.json
@@ -7,55 +7,61 @@
"blocks": [
5,
23,
- 25,
- 125,
- 104,
- 84,
- 49,
- 43,
- 39,
- 45,
- 51,
- 53,
- 57,
- 59,
- 167,
- 156,
- 64,
- 66,
- 68,
- 82,
- 86,
+ 46,
+ 213,
+ 180,
+ 145,
+ 87,
+ 78,
+ 72,
+ 79,
+ 88,
92,
- 94,
- 102,
- 106,
+ 95,
+ 101,
+ 281,
+ 265,
+ 109,
+ 110,
114,
- 117,
- 119,
+ 129,
127,
- 134,
- 137,
- 139,
- 143,
146,
- 148,
- 158,
+ 152,
161,
- 163,
- 169,
- 176,
- 178,
- 184,
- 188,
- 192,
- 196,
- 199,
- 201,
- 210,
- 212,
- 216,
- 112,
+ 170,
+ 168,
+ 181,
+ 194,
+ 197,
+ 203,
+ 214,
+ 228,
+ 230,
+ 235,
+ 240,
+ 238,
+ 243,
+ 251,
+ 266,
+ 269,
+ 275,
+ 282,
+ 289,
+ 299,
+ 309,
+ 314,
+ 312,
+ 320,
+ 318,
+ 326,
+ 324,
+ 329,
+ 343,
+ 351,
+ 362,
+ 368,
+ 193,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/segpr.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/segpr.json
index 50037f5bc4..1650ac5108 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/segpr.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/segpr.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 40
+ 72
],
"entries": [
{
- "block": 40,
+ "block": 72,
"address": "F000:0065",
"kind": "returnTargetEntry"
}
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 32
+ 66
],
"entries": [
{
- "block": 32,
+ "block": 66,
"address": "F000:1100",
"kind": "executionContextEntry"
}
@@ -35,11 +35,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 46
+ 86
],
"entries": [
{
- "block": 46,
+ "block": 86,
"address": "F000:1200",
"kind": "functionEntry"
}
@@ -67,8 +67,8 @@
"kind": "callOut",
"fromPartition": 1,
"toPartition": 3,
- "fromBlock": 40,
- "toBlock": 46,
+ "fromBlock": 72,
+ "toBlock": 86,
"from": "F000:0074",
"target": "F000:1200"
},
@@ -76,8 +76,8 @@
"kind": "dynamicReturn",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 32,
- "toBlock": 40,
+ "fromBlock": 66,
+ "toBlock": 72,
"from": "F000:1111",
"target": "F000:0065"
},
@@ -86,7 +86,7 @@
"fromPartition": 4,
"toPartition": 1,
"fromBlock": 3,
- "toBlock": 40,
+ "toBlock": 72,
"from": "F000:005F",
"target": "F000:0065"
},
@@ -95,7 +95,7 @@
"fromPartition": 4,
"toPartition": 2,
"fromBlock": 3,
- "toBlock": 32,
+ "toBlock": 66,
"from": "F000:005F",
"target": "F000:1100"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifycall.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifycall.json
index d0b45a1aef..df556c18e3 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifycall.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifycall.json
@@ -6,7 +6,7 @@
"name": "unknown",
"blocks": [
9,
- 19,
+ 14,
12
],
"entries": [
@@ -23,10 +23,10 @@
"name": "unknown",
"blocks": [
3,
- 17,
- 14,
- 24,
- 26,
+ 31,
+ 28,
+ 39,
+ 41,
2
],
"entries": [
@@ -44,7 +44,7 @@
"fromPartition": 1,
"toPartition": 2,
"fromBlock": 12,
- "toBlock": 26,
+ "toBlock": 41,
"from": "F000:002A",
"target": "F000:0010"
},
@@ -52,8 +52,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 19,
- "toBlock": 26,
+ "fromBlock": 14,
+ "toBlock": 41,
"from": "F000:0029",
"target": "F000:0010"
},
@@ -61,11 +61,11 @@
"kind": "callOut",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 17,
+ "fromBlock": 31,
"toBlock": 9,
"from": "F000:000D",
"target": "F000:0017",
- "callContinuationBlock": 26,
+ "callContinuationBlock": 41,
"callContinuationAddress": "F000:0010"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyinstructions.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyinstructions.json
index a99b2e38fa..979602acc0 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyinstructions.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyinstructions.json
@@ -6,11 +6,12 @@
"name": "unknown",
"blocks": [
3,
- 19,
- 15,
- 22,
- 23,
- 29,
+ 30234,
+ 30228,
+ 30239,
+ 30245,
+ 30252,
+ 30231,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyje.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyje.json
index d3391eac9c..32f73efe5a 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyje.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyje.json
@@ -7,9 +7,9 @@
"blocks": [
3,
15,
- 18,
+ 9,
7,
- 13,
+ 17,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyterminator.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyterminator.json
index 36d03ab848..e5709593ba 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyterminator.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyterminator.json
@@ -6,7 +6,7 @@
"name": "unknown",
"blocks": [
11,
- 22,
+ 16,
14
],
"entries": [
@@ -23,11 +23,12 @@
"name": "unknown",
"blocks": [
3,
- 20,
- 17,
- 29,
- 30,
- 32,
+ 38,
+ 35,
+ 52,
+ 56,
+ 40,
+ 53,
2
],
"entries": [
@@ -45,7 +46,7 @@
"fromPartition": 1,
"toPartition": 2,
"fromBlock": 14,
- "toBlock": 17,
+ "toBlock": 35,
"from": "F000:003F",
"target": "F000:0014"
},
@@ -53,8 +54,8 @@
"kind": "alignedReturn",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 22,
- "toBlock": 17,
+ "fromBlock": 16,
+ "toBlock": 35,
"from": "F000:003A",
"target": "F000:0014"
},
@@ -62,11 +63,11 @@
"kind": "callOut",
"fromPartition": 2,
"toPartition": 1,
- "fromBlock": 20,
+ "fromBlock": 38,
"toBlock": 11,
"from": "F000:0011",
"target": "F000:0022",
- "callContinuationBlock": 17,
+ "callContinuationBlock": 35,
"callContinuationAddress": "F000:0014"
}
]
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyvalue.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyvalue.json
index f078abf918..2c08597c84 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyvalue.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/selfmodifyvalue.json
@@ -6,7 +6,7 @@
"name": "unknown",
"blocks": [
3,
- 15,
+ 16,
9,
18,
2
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_branch.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_branch.json
new file mode 100644
index 0000000000..f71a40f765
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_branch.json
@@ -0,0 +1,24 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 14,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_call_entry.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_call_entry.json
new file mode 100644
index 0000000000..d88553914e
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_call_entry.json
@@ -0,0 +1,49 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 12
+ ],
+ "entries": [
+ {
+ "block": 12,
+ "address": "F000:001B",
+ "kind": "functionEntry"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 16,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": [
+ {
+ "kind": "callOut",
+ "fromPartition": 2,
+ "toPartition": 1,
+ "fromBlock": 8,
+ "toBlock": 12,
+ "from": "F000:0013",
+ "target": "F000:001B"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_closure.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_closure.json
new file mode 100644
index 0000000000..7704b4dc23
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_closure.json
@@ -0,0 +1,26 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 20,
+ 22,
+ 15,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_convergence.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_convergence.json
new file mode 100644
index 0000000000..f71a40f765
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_convergence.json
@@ -0,0 +1,24 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 14,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_discard.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_discard.json
new file mode 100644
index 0000000000..f71a40f765
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_discard.json
@@ -0,0 +1,24 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 14,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_invalid_opcode.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_invalid_opcode.json
new file mode 100644
index 0000000000..597c5a908c
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_invalid_opcode.json
@@ -0,0 +1,23 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 9,
+ 12,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_mixed_block.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_mixed_block.json
new file mode 100644
index 0000000000..06765fd365
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_mixed_block.json
@@ -0,0 +1,24 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 15,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_smc_guard.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_smc_guard.json
new file mode 100644
index 0000000000..06765fd365
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_smc_guard.json
@@ -0,0 +1,24 @@
+{
+ "partitions": [
+ {
+ "id": 1,
+ "kind": "observed",
+ "name": "unknown",
+ "blocks": [
+ 3,
+ 10,
+ 8,
+ 15,
+ 2
+ ],
+ "entries": [
+ {
+ "block": 2,
+ "address": "F000:FFF0",
+ "kind": "executionContextEntry"
+ }
+ ]
+ }
+ ],
+ "transfers": []
+}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/sticli.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/sticli.json
index 93814f929a..7d05ed467e 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/sticli.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/sticli.json
@@ -6,8 +6,8 @@
"name": "unknown",
"blocks": [
3,
- 7,
- 9,
+ 11,
+ 12,
2
],
"entries": [
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/strings.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/strings.json
index 784f812673..ea47241201 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/strings.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/strings.json
@@ -5,11 +5,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 16
+ 27
],
"entries": [
{
- "block": 16,
+ "block": 27,
"address": "F000:0046",
"kind": "returnTargetEntry"
}
@@ -20,11 +20,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 20
+ 33
],
"entries": [
{
- "block": 20,
+ "block": 33,
"address": "F000:0082",
"kind": "returnTargetEntry"
}
@@ -35,11 +35,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 12
+ 21
],
"entries": [
{
- "block": 12,
+ "block": 21,
"address": "F000:0097",
"kind": "returnTargetEntry"
}
@@ -50,11 +50,11 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 24
+ 39
],
"entries": [
{
- "block": 24,
+ "block": 39,
"address": "F000:0812",
"kind": "returnTargetEntry"
}
@@ -65,19 +65,20 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 28,
- 33,
- 56,
- 61,
- 66,
- 44,
- 49,
- 36,
- 70
+ 45,
+ 53,
+ 91,
+ 97,
+ 95,
+ 110,
+ 71,
+ 79,
+ 57,
+ 116
],
"entries": [
{
- "block": 28,
+ "block": 45,
"address": "F000:0883",
"kind": "returnTargetEntry"
}
@@ -105,8 +106,8 @@
"kind": "dynamicReturn",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 16,
- "toBlock": 20,
+ "fromBlock": 27,
+ "toBlock": 33,
"from": "F000:0048",
"target": "F000:0082"
},
@@ -114,8 +115,8 @@
"kind": "dynamicReturn",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 20,
- "toBlock": 24,
+ "fromBlock": 33,
+ "toBlock": 39,
"from": "F000:0084",
"target": "F000:0812"
},
@@ -123,8 +124,8 @@
"kind": "dynamicReturn",
"fromPartition": 3,
"toPartition": 1,
- "fromBlock": 12,
- "toBlock": 16,
+ "fromBlock": 21,
+ "toBlock": 27,
"from": "F000:0099",
"target": "F000:0046"
},
@@ -132,8 +133,8 @@
"kind": "dynamicReturn",
"fromPartition": 4,
"toPartition": 5,
- "fromBlock": 24,
- "toBlock": 28,
+ "fromBlock": 39,
+ "toBlock": 45,
"from": "F000:0814",
"target": "F000:0883"
},
@@ -142,7 +143,7 @@
"fromPartition": 6,
"toPartition": 3,
"fromBlock": 3,
- "toBlock": 12,
+ "toBlock": 21,
"from": "F000:000F",
"target": "F000:0097"
}
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/test386.json b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/test386.json
index 37265d1e37..1eab23be1a 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/test386.json
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/test386.json
@@ -5,16 +5,16 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 516,
- 519,
- 523,
- 532,
- 537,
- 542
+ 1055,
+ 1058,
+ 1063,
+ 1073,
+ 1079,
+ 1085
],
"entries": [
{
- "block": 516,
+ "block": 1055,
"address": "F000:0633",
"kind": "executionContextEntry"
}
@@ -25,210 +25,210 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 552,
- 555,
- 559,
- 580,
- 583,
- 591,
- 594,
- 597,
- 600,
- 607,
- 610,
- 621,
- 624,
- 627,
- 635,
- 638,
- 648,
- 651,
- 659,
- 662,
- 665,
- 668,
- 675,
- 678,
- 689,
- 692,
- 695,
- 703,
- 706,
- 716,
- 719,
- 727,
- 730,
- 733,
- 736,
- 743,
- 746,
- 757,
- 760,
- 763,
- 771,
- 774,
- 784,
- 787,
- 795,
- 798,
- 801,
- 804,
- 811,
- 814,
- 825,
- 828,
- 831,
- 839,
- 842,
- 852,
- 855,
- 863,
- 866,
- 869,
- 872,
- 879,
- 882,
- 893,
- 896,
- 899,
- 907,
- 910,
- 920,
- 923,
- 931,
- 934,
- 937,
- 940,
- 947,
- 950,
- 961,
- 964,
- 967,
- 975,
- 978,
- 990,
- 993,
- 1008,
- 1011,
- 1014,
- 1021,
- 1024,
- 1034,
- 1037,
- 1040,
- 1047,
- 1050,
- 1053,
- 1065,
- 1068,
- 1083,
- 1086,
- 1089,
- 1096,
- 1099,
- 1109,
- 1112,
- 1115,
- 1122,
1125,
1128,
- 1140,
- 1143,
- 1158,
- 1161,
- 1164,
- 1171,
- 1174,
- 1184,
- 1187,
- 1190,
- 1197,
- 1200,
- 1203,
- 1215,
- 1218,
- 1233,
+ 1133,
+ 1155,
+ 1159,
+ 1168,
+ 1172,
+ 1176,
+ 1180,
+ 1188,
+ 1192,
+ 1204,
+ 1208,
+ 1212,
+ 1221,
+ 1225,
1236,
- 1239,
- 1246,
+ 1240,
1249,
- 1259,
- 1262,
- 1265,
- 1272,
- 1275,
- 1278,
- 1290,
+ 1253,
+ 1257,
+ 1261,
+ 1269,
+ 1273,
+ 1285,
+ 1289,
1293,
- 1308,
- 1311,
- 1314,
+ 1302,
+ 1306,
+ 1317,
1321,
- 1324,
+ 1330,
1334,
- 1337,
- 1340,
- 1347,
+ 1338,
+ 1342,
1350,
- 1353,
- 1365,
- 1368,
+ 1354,
+ 1366,
+ 1370,
+ 1374,
1383,
- 1386,
- 1389,
- 1396,
- 1399,
- 1409,
- 1412,
+ 1387,
+ 1398,
+ 1402,
+ 1411,
1415,
- 1422,
- 1425,
- 1428,
- 1448,
- 1450,
- 1452,
- 1463,
- 1465,
- 1467,
- 1471,
- 1473,
- 1477,
+ 1419,
+ 1423,
+ 1431,
+ 1435,
+ 1447,
+ 1451,
+ 1455,
+ 1464,
+ 1468,
1479,
- 1491,
- 1493,
- 1495,
- 1506,
- 1508,
- 1510,
- 1515,
- 1517,
- 1522,
- 1524,
- 1541,
- 1544,
- 1552,
- 1555,
- 1566,
- 1569,
+ 1483,
+ 1492,
+ 1496,
+ 1500,
+ 1504,
+ 1512,
+ 1516,
+ 1528,
+ 1532,
+ 1536,
+ 1545,
+ 1549,
+ 1560,
+ 1564,
+ 1573,
1577,
- 1580,
- 1591,
- 1594,
- 1602,
- 1605,
- 1616,
- 1619,
- 1627,
+ 1581,
+ 1585,
+ 1593,
+ 1597,
+ 1609,
+ 1613,
+ 1617,
+ 1626,
1630,
- 1641,
- 1644,
- 1652,
- 1655,
- 1665
+ 1643,
+ 1647,
+ 1663,
+ 1667,
+ 1671,
+ 1679,
+ 1683,
+ 1694,
+ 1698,
+ 1702,
+ 1710,
+ 1714,
+ 1718,
+ 1731,
+ 1735,
+ 1751,
+ 1755,
+ 1759,
+ 1767,
+ 1771,
+ 1782,
+ 1786,
+ 1790,
+ 1798,
+ 1802,
+ 1806,
+ 1819,
+ 1823,
+ 1839,
+ 1843,
+ 1847,
+ 1855,
+ 1859,
+ 1870,
+ 1874,
+ 1878,
+ 1886,
+ 1890,
+ 1894,
+ 1907,
+ 1911,
+ 1927,
+ 1931,
+ 1935,
+ 1943,
+ 1947,
+ 1958,
+ 1962,
+ 1966,
+ 1974,
+ 1978,
+ 1982,
+ 1995,
+ 1999,
+ 2015,
+ 2019,
+ 2023,
+ 2031,
+ 2035,
+ 2046,
+ 2050,
+ 2054,
+ 2062,
+ 2066,
+ 2070,
+ 2083,
+ 2087,
+ 2103,
+ 2107,
+ 2111,
+ 2119,
+ 2123,
+ 2134,
+ 2138,
+ 2142,
+ 2150,
+ 2154,
+ 2158,
+ 2914,
+ 2916,
+ 2918,
+ 2940,
+ 2942,
+ 2944,
+ 2953,
+ 2955,
+ 2963,
+ 2965,
+ 2988,
+ 2990,
+ 2992,
+ 3014,
+ 3016,
+ 3018,
+ 3029,
+ 3031,
+ 3041,
+ 3043,
+ 3061,
+ 3065,
+ 3074,
+ 3078,
+ 3090,
+ 3094,
+ 3103,
+ 3107,
+ 3119,
+ 3123,
+ 3132,
+ 3136,
+ 3148,
+ 3152,
+ 3161,
+ 3165,
+ 3177,
+ 3181,
+ 3190,
+ 3194,
+ 3204
],
"entries": [
{
- "block": 552,
+ "block": 1125,
"address": "F000:06A8",
"kind": "executionContextEntry"
}
@@ -239,12 +239,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 1440,
- 1444
+ 2170,
+ 2175
],
"entries": [
{
- "block": 1440,
+ "block": 2170,
"address": "F000:136F",
"kind": "functionEntry"
}
@@ -255,12 +255,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 1455,
- 1459
+ 2921,
+ 2926
],
"entries": [
{
- "block": 1455,
+ "block": 2921,
"address": "F000:138D",
"kind": "functionEntry"
}
@@ -271,12 +271,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 1483,
- 1487
+ 2969,
+ 2974
],
"entries": [
{
- "block": 1483,
+ "block": 2969,
"address": "F000:13C5",
"kind": "functionEntry"
}
@@ -287,12 +287,12 @@
"kind": "observed",
"name": "unknown",
"blocks": [
- 1498,
- 1502
+ 2995,
+ 3000
],
"entries": [
{
- "block": 1498,
+ "block": 2995,
"address": "F000:13E5",
"kind": "functionEntry"
}
@@ -304,149 +304,181 @@
"name": "unknown",
"blocks": [
3,
- 14,
- 16,
- 30,
- 34,
- 38,
- 42,
- 46,
- 50,
- 54,
- 58,
- 62,
- 66,
- 32,
- 40,
- 48,
- 56,
+ 15,
+ 17,
+ 33,
+ 37,
+ 39,
+ 55,
64,
- 68,
+ 44,
+ 67,
76,
- 82,
+ 50,
+ 79,
+ 85,
+ 59,
88,
- 94,
- 100,
- 74,
- 80,
- 86,
- 92,
- 98,
- 102,
- 108,
- 112,
- 114,
- 118,
- 120,
+ 71,
+ 31,
+ 35,
+ 41,
+ 46,
+ 52,
+ 61,
+ 73,
+ 97,
+ 99,
+ 115,
+ 104,
124,
- 130,
- 134,
- 137,
- 142,
- 144,
- 149,
110,
- 116,
- 122,
+ 133,
+ 119,
+ 139,
128,
- 132,
- 140,
- 147,
- 151,
- 155,
+ 95,
+ 101,
+ 106,
+ 112,
+ 121,
+ 130,
+ 146,
+ 150,
+ 152,
+ 157,
+ 164,
159,
- 163,
167,
- 171,
- 175,
- 179,
+ 174,
+ 169,
+ 190,
+ 177,
183,
- 187,
- 191,
- 157,
- 165,
- 173,
- 181,
- 189,
+ 185,
+ 198,
193,
201,
- 207,
- 213,
- 219,
- 225,
- 199,
+ 203,
+ 144,
+ 148,
+ 154,
+ 161,
+ 171,
+ 179,
+ 187,
+ 195,
205,
- 211,
- 217,
- 223,
- 227,
- 233,
- 237,
- 239,
- 243,
- 245,
+ 218,
+ 222,
+ 224,
+ 240,
249,
- 255,
- 259,
- 235,
- 241,
- 247,
- 253,
- 257,
+ 229,
+ 252,
261,
- 265,
- 267,
+ 235,
+ 264,
270,
+ 244,
273,
- 277,
- 279,
+ 256,
+ 216,
+ 220,
+ 226,
+ 231,
+ 237,
+ 246,
+ 258,
282,
- 285,
- 290,
- 292,
+ 284,
+ 300,
+ 289,
+ 309,
295,
- 298,
- 303,
- 305,
- 308,
- 311,
- 316,
318,
- 321,
+ 304,
324,
- 329,
+ 313,
+ 280,
+ 286,
+ 291,
+ 297,
+ 306,
+ 315,
331,
- 334,
+ 335,
337,
342,
+ 349,
344,
- 347,
- 350,
- 355,
- 357,
- 360,
- 363,
- 376,
- 386,
- 391,
- 396,
- 404,
+ 352,
+ 359,
+ 354,
+ 375,
+ 362,
+ 368,
+ 370,
+ 329,
+ 333,
+ 339,
+ 346,
+ 356,
+ 364,
+ 372,
+ 381,
+ 383,
+ 388,
+ 394,
+ 405,
+ 407,
411,
- 416,
+ 415,
421,
- 430,
+ 423,
+ 427,
+ 431,
437,
- 442,
+ 439,
+ 443,
447,
+ 453,
455,
- 462,
- 467,
- 472,
- 480,
+ 459,
+ 463,
+ 469,
+ 471,
+ 475,
+ 479,
+ 485,
487,
- 492,
- 497,
- 505,
+ 491,
+ 495,
+ 501,
+ 503,
+ 507,
+ 511,
+ 525,
+ 536,
+ 542,
+ 548,
+ 557,
+ 565,
+ 571,
+ 577,
+ 587,
+ 595,
+ 601,
+ 607,
+ 616,
+ 624,
+ 630,
+ 636,
+ 645,
+ 653,
+ 659,
+ 665,
+ 674,
2
],
"entries": [
@@ -456,6 +488,23 @@
"kind": "executionContextEntry"
}
]
+ },
+ {
+ "id": 8,
+ "kind": "synthetic",
+ "name": "unknown",
+ "blocks": [
+ 386,
+ 396,
+ 398
+ ],
+ "entries": [
+ {
+ "block": 386,
+ "address": "F000:15C7",
+ "kind": "sharedEntry"
+ }
+ ]
}
],
"transfers": [
@@ -463,179 +512,2267 @@
"kind": "cpuFault",
"fromPartition": 1,
"toPartition": 2,
- "fromBlock": 542,
- "toBlock": 552,
+ "fromBlock": 1085,
+ "toBlock": 1125,
"from": "F000:06A1",
"target": "F000:06A8"
},
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 1,
+ "toPartition": 8,
+ "fromBlock": 1055,
+ "toBlock": 386,
+ "from": "F000:0636",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 1,
+ "toPartition": 8,
+ "fromBlock": 1058,
+ "toBlock": 386,
+ "from": "F000:0648",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 1,
+ "toPartition": 8,
+ "fromBlock": 1063,
+ "toBlock": 386,
+ "from": "F000:0663",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 1,
+ "toPartition": 8,
+ "fromBlock": 1073,
+ "toBlock": 386,
+ "from": "F000:0672",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 1,
+ "toPartition": 8,
+ "fromBlock": 1079,
+ "toBlock": 386,
+ "from": "F000:0684",
+ "target": "F000:15C7"
+ },
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 1428,
- "toBlock": 1440,
+ "fromBlock": 2158,
+ "toBlock": 2170,
"from": "F000:1366",
"target": "F000:136F",
- "callContinuationBlock": 1448,
+ "callContinuationBlock": 2914,
"callContinuationAddress": "F000:1369"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 3,
- "fromBlock": 1467,
- "toBlock": 1440,
+ "fromBlock": 2944,
+ "toBlock": 2170,
"from": "F000:13A3",
"target": "F000:136F",
- "callContinuationBlock": 1471,
+ "callContinuationBlock": 2953,
"callContinuationAddress": "F000:13A5"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 1452,
- "toBlock": 1455,
+ "fromBlock": 2918,
+ "toBlock": 2921,
"from": "F000:1381",
"target": "F000:138D",
- "callContinuationBlock": 1463,
+ "callContinuationBlock": 2940,
"callContinuationAddress": "F000:1387"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 4,
- "fromBlock": 1473,
- "toBlock": 1455,
+ "fromBlock": 2955,
+ "toBlock": 2921,
"from": "F000:13B0",
"target": "F000:138D",
- "callContinuationBlock": 1477,
+ "callContinuationBlock": 2963,
"callContinuationAddress": "F000:13B3"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 5,
- "fromBlock": 1479,
- "toBlock": 1483,
+ "fromBlock": 2965,
+ "toBlock": 2969,
"from": "F000:13BA",
"target": "F000:13C5",
- "callContinuationBlock": 1491,
+ "callContinuationBlock": 2988,
"callContinuationAddress": "F000:13BF"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 5,
- "fromBlock": 1510,
- "toBlock": 1483,
+ "fromBlock": 3018,
+ "toBlock": 2969,
"from": "F000:1401",
"target": "F000:13C5",
- "callContinuationBlock": 1515,
+ "callContinuationBlock": 3029,
"callContinuationAddress": "F000:1403"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 6,
- "fromBlock": 1495,
- "toBlock": 1498,
+ "fromBlock": 2992,
+ "toBlock": 2995,
"from": "F000:13D7",
"target": "F000:13E5",
- "callContinuationBlock": 1506,
+ "callContinuationBlock": 3014,
"callContinuationAddress": "F000:13DF"
},
{
"kind": "callOut",
"fromPartition": 2,
"toPartition": 6,
- "fromBlock": 1517,
- "toBlock": 1498,
+ "fromBlock": 3031,
+ "toBlock": 2995,
"from": "F000:1414",
"target": "F000:13E5",
- "callContinuationBlock": 1522,
+ "callContinuationBlock": 3041,
"callContinuationAddress": "F000:1417"
},
{
- "kind": "alignedReturn",
- "fromPartition": 3,
- "toPartition": 2,
- "fromBlock": 1444,
- "toBlock": 1448,
- "from": "F000:137C",
- "target": "F000:1369"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1125,
+ "toBlock": 386,
+ "from": "F000:06AB",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 3,
- "toPartition": 2,
- "fromBlock": 1444,
- "toBlock": 1471,
- "from": "F000:137C",
- "target": "F000:13A5"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1128,
+ "toBlock": 386,
+ "from": "F000:06BD",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 4,
- "toPartition": 2,
- "fromBlock": 1459,
- "toBlock": 1463,
- "from": "F000:139A",
- "target": "F000:1387"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1133,
+ "toBlock": 386,
+ "from": "F000:06FC",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 4,
- "toPartition": 2,
- "fromBlock": 1459,
- "toBlock": 1477,
- "from": "F000:139A",
- "target": "F000:13B3"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1155,
+ "toBlock": 386,
+ "from": "F000:0707",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 5,
- "toPartition": 2,
- "fromBlock": 1487,
- "toBlock": 1491,
- "from": "F000:13D2",
- "target": "F000:13BF"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1159,
+ "toBlock": 386,
+ "from": "F000:0727",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 5,
- "toPartition": 2,
- "fromBlock": 1487,
- "toBlock": 1515,
- "from": "F000:13D2",
- "target": "F000:1403"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1168,
+ "toBlock": 386,
+ "from": "F000:072C",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 6,
- "toPartition": 2,
- "fromBlock": 1502,
- "toBlock": 1506,
- "from": "F000:13F2",
- "target": "F000:13DF"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1172,
+ "toBlock": 386,
+ "from": "F000:0737",
+ "target": "F000:15C7"
},
{
- "kind": "alignedReturn",
- "fromPartition": 6,
- "toPartition": 2,
- "fromBlock": 1502,
- "toBlock": 1522,
- "from": "F000:13F2",
- "target": "F000:1417"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1176,
+ "toBlock": 386,
+ "from": "F000:0742",
+ "target": "F000:15C7"
},
{
- "kind": "cpuFault",
- "fromPartition": 7,
- "toPartition": 1,
- "fromBlock": 505,
- "toBlock": 516,
- "from": "F000:062E",
- "target": "F000:0633"
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1180,
+ "toBlock": 386,
+ "from": "F000:0755",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1188,
+ "toBlock": 386,
+ "from": "F000:0760",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1192,
+ "toBlock": 386,
+ "from": "F000:0783",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1204,
+ "toBlock": 386,
+ "from": "F000:078E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1208,
+ "toBlock": 386,
+ "from": "F000:0799",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1212,
+ "toBlock": 386,
+ "from": "F000:07AF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1221,
+ "toBlock": 386,
+ "from": "F000:07BA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1225,
+ "toBlock": 386,
+ "from": "F000:07DA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1236,
+ "toBlock": 386,
+ "from": "F000:07E5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1240,
+ "toBlock": 386,
+ "from": "F000:0806",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1249,
+ "toBlock": 386,
+ "from": "F000:080B",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1253,
+ "toBlock": 386,
+ "from": "F000:0816",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1257,
+ "toBlock": 386,
+ "from": "F000:0821",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1261,
+ "toBlock": 386,
+ "from": "F000:0836",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1269,
+ "toBlock": 386,
+ "from": "F000:0841",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1273,
+ "toBlock": 386,
+ "from": "F000:0867",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1285,
+ "toBlock": 386,
+ "from": "F000:0872",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1289,
+ "toBlock": 386,
+ "from": "F000:087D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1293,
+ "toBlock": 386,
+ "from": "F000:0895",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1302,
+ "toBlock": 386,
+ "from": "F000:08A0",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1306,
+ "toBlock": 386,
+ "from": "F000:08C9",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1317,
+ "toBlock": 386,
+ "from": "F000:08D4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1321,
+ "toBlock": 386,
+ "from": "F000:08F8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1330,
+ "toBlock": 386,
+ "from": "F000:08FE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1334,
+ "toBlock": 386,
+ "from": "F000:0909",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1338,
+ "toBlock": 386,
+ "from": "F000:0914",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1342,
+ "toBlock": 386,
+ "from": "F000:092F",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1350,
+ "toBlock": 386,
+ "from": "F000:093A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1354,
+ "toBlock": 386,
+ "from": "F000:096D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1366,
+ "toBlock": 386,
+ "from": "F000:0978",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1370,
+ "toBlock": 386,
+ "from": "F000:0983",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1374,
+ "toBlock": 386,
+ "from": "F000:09A3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1383,
+ "toBlock": 386,
+ "from": "F000:09AE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1387,
+ "toBlock": 386,
+ "from": "F000:09CC",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1398,
+ "toBlock": 386,
+ "from": "F000:09D7",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1402,
+ "toBlock": 386,
+ "from": "F000:09F7",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1411,
+ "toBlock": 386,
+ "from": "F000:09FC",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1415,
+ "toBlock": 386,
+ "from": "F000:0A07",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1419,
+ "toBlock": 386,
+ "from": "F000:0A12",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1423,
+ "toBlock": 386,
+ "from": "F000:0A25",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1431,
+ "toBlock": 386,
+ "from": "F000:0A30",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1435,
+ "toBlock": 386,
+ "from": "F000:0A53",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1447,
+ "toBlock": 386,
+ "from": "F000:0A5E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1451,
+ "toBlock": 386,
+ "from": "F000:0A69",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1455,
+ "toBlock": 386,
+ "from": "F000:0A7F",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1464,
+ "toBlock": 386,
+ "from": "F000:0A8A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1468,
+ "toBlock": 386,
+ "from": "F000:0AAA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1479,
+ "toBlock": 386,
+ "from": "F000:0AB5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1483,
+ "toBlock": 386,
+ "from": "F000:0AD6",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1492,
+ "toBlock": 386,
+ "from": "F000:0ADB",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1496,
+ "toBlock": 386,
+ "from": "F000:0AE6",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1500,
+ "toBlock": 386,
+ "from": "F000:0AF1",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1504,
+ "toBlock": 386,
+ "from": "F000:0B06",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1512,
+ "toBlock": 386,
+ "from": "F000:0B11",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1516,
+ "toBlock": 386,
+ "from": "F000:0B37",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1528,
+ "toBlock": 386,
+ "from": "F000:0B42",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1532,
+ "toBlock": 386,
+ "from": "F000:0B4D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1536,
+ "toBlock": 386,
+ "from": "F000:0B65",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1545,
+ "toBlock": 386,
+ "from": "F000:0B70",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1549,
+ "toBlock": 386,
+ "from": "F000:0B99",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1560,
+ "toBlock": 386,
+ "from": "F000:0BA4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1564,
+ "toBlock": 386,
+ "from": "F000:0BC8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1573,
+ "toBlock": 386,
+ "from": "F000:0BCE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1577,
+ "toBlock": 386,
+ "from": "F000:0BD9",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1581,
+ "toBlock": 386,
+ "from": "F000:0BE4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1585,
+ "toBlock": 386,
+ "from": "F000:0BFF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1593,
+ "toBlock": 386,
+ "from": "F000:0C0A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1597,
+ "toBlock": 386,
+ "from": "F000:0C3D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1609,
+ "toBlock": 386,
+ "from": "F000:0C48",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1613,
+ "toBlock": 386,
+ "from": "F000:0C53",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1617,
+ "toBlock": 386,
+ "from": "F000:0C73",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1626,
+ "toBlock": 386,
+ "from": "F000:0C7E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1630,
+ "toBlock": 386,
+ "from": "F000:0CB3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1643,
+ "toBlock": 386,
+ "from": "F000:0CBE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1647,
+ "toBlock": 386,
+ "from": "F000:0CEF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1663,
+ "toBlock": 386,
+ "from": "F000:0CFA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1667,
+ "toBlock": 386,
+ "from": "F000:0D05",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1671,
+ "toBlock": 386,
+ "from": "F000:0D21",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1679,
+ "toBlock": 386,
+ "from": "F000:0D2C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1683,
+ "toBlock": 386,
+ "from": "F000:0D56",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1694,
+ "toBlock": 386,
+ "from": "F000:0D61",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1698,
+ "toBlock": 386,
+ "from": "F000:0D6C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1702,
+ "toBlock": 386,
+ "from": "F000:0D88",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1710,
+ "toBlock": 386,
+ "from": "F000:0D93",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1714,
+ "toBlock": 386,
+ "from": "F000:0D9E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1718,
+ "toBlock": 386,
+ "from": "F000:0DD3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1731,
+ "toBlock": 386,
+ "from": "F000:0DDE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1735,
+ "toBlock": 386,
+ "from": "F000:0E0F",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1751,
+ "toBlock": 386,
+ "from": "F000:0E1A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1755,
+ "toBlock": 386,
+ "from": "F000:0E25",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1759,
+ "toBlock": 386,
+ "from": "F000:0E41",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1767,
+ "toBlock": 386,
+ "from": "F000:0E4C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1771,
+ "toBlock": 386,
+ "from": "F000:0E76",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1782,
+ "toBlock": 386,
+ "from": "F000:0E81",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1786,
+ "toBlock": 386,
+ "from": "F000:0E8C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1790,
+ "toBlock": 386,
+ "from": "F000:0EA8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1798,
+ "toBlock": 386,
+ "from": "F000:0EB3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1802,
+ "toBlock": 386,
+ "from": "F000:0EBE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1806,
+ "toBlock": 386,
+ "from": "F000:0EF4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1819,
+ "toBlock": 386,
+ "from": "F000:0EFF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1823,
+ "toBlock": 386,
+ "from": "F000:0F32",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1839,
+ "toBlock": 386,
+ "from": "F000:0F3D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1843,
+ "toBlock": 386,
+ "from": "F000:0F48",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1847,
+ "toBlock": 386,
+ "from": "F000:0F65",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1855,
+ "toBlock": 386,
+ "from": "F000:0F70",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1859,
+ "toBlock": 386,
+ "from": "F000:0F9C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1870,
+ "toBlock": 386,
+ "from": "F000:0FA7",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1874,
+ "toBlock": 386,
+ "from": "F000:0FB2",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1878,
+ "toBlock": 386,
+ "from": "F000:0FCF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1886,
+ "toBlock": 386,
+ "from": "F000:0FDA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1890,
+ "toBlock": 386,
+ "from": "F000:0FE5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1894,
+ "toBlock": 386,
+ "from": "F000:101A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1907,
+ "toBlock": 386,
+ "from": "F000:1025",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1911,
+ "toBlock": 386,
+ "from": "F000:1056",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1927,
+ "toBlock": 386,
+ "from": "F000:1061",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1931,
+ "toBlock": 386,
+ "from": "F000:106C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1935,
+ "toBlock": 386,
+ "from": "F000:1088",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1943,
+ "toBlock": 386,
+ "from": "F000:1093",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1947,
+ "toBlock": 386,
+ "from": "F000:10BD",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1958,
+ "toBlock": 386,
+ "from": "F000:10C8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1962,
+ "toBlock": 386,
+ "from": "F000:10D3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1966,
+ "toBlock": 386,
+ "from": "F000:10EF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1974,
+ "toBlock": 386,
+ "from": "F000:10FA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1978,
+ "toBlock": 386,
+ "from": "F000:1105",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1982,
+ "toBlock": 386,
+ "from": "F000:113A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1995,
+ "toBlock": 386,
+ "from": "F000:1145",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 1999,
+ "toBlock": 386,
+ "from": "F000:1176",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2015,
+ "toBlock": 386,
+ "from": "F000:1181",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2019,
+ "toBlock": 386,
+ "from": "F000:118C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2023,
+ "toBlock": 386,
+ "from": "F000:11A8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2031,
+ "toBlock": 386,
+ "from": "F000:11B3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2035,
+ "toBlock": 386,
+ "from": "F000:11DD",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2046,
+ "toBlock": 386,
+ "from": "F000:11E8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2050,
+ "toBlock": 386,
+ "from": "F000:11F3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2054,
+ "toBlock": 386,
+ "from": "F000:120F",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2062,
+ "toBlock": 386,
+ "from": "F000:121A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2066,
+ "toBlock": 386,
+ "from": "F000:1225",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2070,
+ "toBlock": 386,
+ "from": "F000:125B",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2083,
+ "toBlock": 386,
+ "from": "F000:1266",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2087,
+ "toBlock": 386,
+ "from": "F000:1299",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2103,
+ "toBlock": 386,
+ "from": "F000:12A4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2107,
+ "toBlock": 386,
+ "from": "F000:12AF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2111,
+ "toBlock": 386,
+ "from": "F000:12CC",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2119,
+ "toBlock": 386,
+ "from": "F000:12D7",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2123,
+ "toBlock": 386,
+ "from": "F000:1303",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2134,
+ "toBlock": 386,
+ "from": "F000:130E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2138,
+ "toBlock": 386,
+ "from": "F000:1319",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2142,
+ "toBlock": 386,
+ "from": "F000:1336",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2150,
+ "toBlock": 386,
+ "from": "F000:1341",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2154,
+ "toBlock": 386,
+ "from": "F000:134C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2914,
+ "toBlock": 386,
+ "from": "F000:1369",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2940,
+ "toBlock": 386,
+ "from": "F000:1387",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2953,
+ "toBlock": 386,
+ "from": "F000:13A5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2963,
+ "toBlock": 386,
+ "from": "F000:13B3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 2988,
+ "toBlock": 386,
+ "from": "F000:13BF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3014,
+ "toBlock": 386,
+ "from": "F000:13DF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3029,
+ "toBlock": 386,
+ "from": "F000:1403",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3041,
+ "toBlock": 386,
+ "from": "F000:1417",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3043,
+ "toBlock": 386,
+ "from": "F000:1446",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3061,
+ "toBlock": 386,
+ "from": "F000:144E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3065,
+ "toBlock": 386,
+ "from": "F000:146C",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3074,
+ "toBlock": 386,
+ "from": "F000:1477",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3078,
+ "toBlock": 386,
+ "from": "F000:1496",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3090,
+ "toBlock": 386,
+ "from": "F000:149E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3094,
+ "toBlock": 386,
+ "from": "F000:14BB",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3103,
+ "toBlock": 386,
+ "from": "F000:14C6",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3107,
+ "toBlock": 386,
+ "from": "F000:14E5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3119,
+ "toBlock": 386,
+ "from": "F000:14ED",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3123,
+ "toBlock": 386,
+ "from": "F000:150A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3132,
+ "toBlock": 386,
+ "from": "F000:1515",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3136,
+ "toBlock": 386,
+ "from": "F000:1535",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3148,
+ "toBlock": 386,
+ "from": "F000:153D",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3152,
+ "toBlock": 386,
+ "from": "F000:155B",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3161,
+ "toBlock": 386,
+ "from": "F000:1564",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3165,
+ "toBlock": 386,
+ "from": "F000:1582",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3177,
+ "toBlock": 386,
+ "from": "F000:1588",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3181,
+ "toBlock": 386,
+ "from": "F000:15A4",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 2,
+ "toPartition": 8,
+ "fromBlock": 3190,
+ "toBlock": 386,
+ "from": "F000:15AD",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 3,
+ "toPartition": 2,
+ "fromBlock": 2175,
+ "toBlock": 2914,
+ "from": "F000:137C",
+ "target": "F000:1369"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 3,
+ "toPartition": 2,
+ "fromBlock": 2175,
+ "toBlock": 2953,
+ "from": "F000:137C",
+ "target": "F000:13A5"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 3,
+ "toPartition": 8,
+ "fromBlock": 2170,
+ "toBlock": 386,
+ "from": "F000:1374",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 4,
+ "toPartition": 2,
+ "fromBlock": 2926,
+ "toBlock": 2940,
+ "from": "F000:139A",
+ "target": "F000:1387"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 4,
+ "toPartition": 2,
+ "fromBlock": 2926,
+ "toBlock": 2963,
+ "from": "F000:139A",
+ "target": "F000:13B3"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 4,
+ "toPartition": 8,
+ "fromBlock": 2921,
+ "toBlock": 386,
+ "from": "F000:1392",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 5,
+ "toPartition": 2,
+ "fromBlock": 2974,
+ "toBlock": 2988,
+ "from": "F000:13D2",
+ "target": "F000:13BF"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 5,
+ "toPartition": 2,
+ "fromBlock": 2974,
+ "toBlock": 3029,
+ "from": "F000:13D2",
+ "target": "F000:1403"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 5,
+ "toPartition": 8,
+ "fromBlock": 2969,
+ "toBlock": 386,
+ "from": "F000:13CA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 6,
+ "toPartition": 2,
+ "fromBlock": 3000,
+ "toBlock": 3014,
+ "from": "F000:13F2",
+ "target": "F000:13DF"
+ },
+ {
+ "kind": "alignedReturn",
+ "fromPartition": 6,
+ "toPartition": 2,
+ "fromBlock": 3000,
+ "toBlock": 3041,
+ "from": "F000:13F2",
+ "target": "F000:1417"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 6,
+ "toPartition": 8,
+ "fromBlock": 2995,
+ "toBlock": 386,
+ "from": "F000:13EA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "cpuFault",
+ "fromPartition": 7,
+ "toPartition": 1,
+ "fromBlock": 674,
+ "toBlock": 1055,
+ "from": "F000:062E",
+ "target": "F000:0633"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 383,
+ "toBlock": 386,
+ "from": "F000:03A5",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 388,
+ "toBlock": 386,
+ "from": "F000:03B0",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 407,
+ "toBlock": 386,
+ "from": "F000:03CB",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 411,
+ "toBlock": 386,
+ "from": "F000:03D3",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 423,
+ "toBlock": 386,
+ "from": "F000:03E6",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 427,
+ "toBlock": 386,
+ "from": "F000:03EE",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 439,
+ "toBlock": 386,
+ "from": "F000:0401",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 443,
+ "toBlock": 386,
+ "from": "F000:0408",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 455,
+ "toBlock": 386,
+ "from": "F000:0429",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 459,
+ "toBlock": 386,
+ "from": "F000:0434",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 471,
+ "toBlock": 386,
+ "from": "F000:0446",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 475,
+ "toBlock": 386,
+ "from": "F000:044E",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 487,
+ "toBlock": 386,
+ "from": "F000:0460",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 491,
+ "toBlock": 386,
+ "from": "F000:0467",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 503,
+ "toBlock": 386,
+ "from": "F000:0488",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 507,
+ "toBlock": 386,
+ "from": "F000:0490",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 511,
+ "toBlock": 386,
+ "from": "F000:04BB",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 525,
+ "toBlock": 386,
+ "from": "F000:04D0",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 536,
+ "toBlock": 386,
+ "from": "F000:04DF",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 542,
+ "toBlock": 386,
+ "from": "F000:04F1",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 548,
+ "toBlock": 386,
+ "from": "F000:0503",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 557,
+ "toBlock": 386,
+ "from": "F000:0512",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 565,
+ "toBlock": 386,
+ "from": "F000:0521",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 571,
+ "toBlock": 386,
+ "from": "F000:0533",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 577,
+ "toBlock": 386,
+ "from": "F000:0548",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 587,
+ "toBlock": 386,
+ "from": "F000:0557",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 595,
+ "toBlock": 386,
+ "from": "F000:0566",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 601,
+ "toBlock": 386,
+ "from": "F000:0578",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 607,
+ "toBlock": 386,
+ "from": "F000:058A",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 616,
+ "toBlock": 386,
+ "from": "F000:0599",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 624,
+ "toBlock": 386,
+ "from": "F000:05A8",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 630,
+ "toBlock": 386,
+ "from": "F000:05BA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 636,
+ "toBlock": 386,
+ "from": "F000:05CC",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 645,
+ "toBlock": 386,
+ "from": "F000:05DB",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 653,
+ "toBlock": 386,
+ "from": "F000:05EA",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 659,
+ "toBlock": 386,
+ "from": "F000:05FC",
+ "target": "F000:15C7"
+ },
+ {
+ "kind": "crossPartitionFlow",
+ "fromPartition": 7,
+ "toPartition": 8,
+ "fromBlock": 665,
+ "toBlock": 386,
+ "from": "F000:060E",
+ "target": "F000:15C7"
}
]
}
\ No newline at end of file
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump1.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump1.txt
index 36123afb3b..4122123897 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump1.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump1.txt
@@ -4,27 +4,33 @@ F000:0006 mov SP,0x1000
F000:0009 mov SS,SP
F000:000B push BX
F000:000C jmp short 0x0010
+F000:000E jmp short 0xFFF2
F000:0010 stc
F000:0011 ja short 0x000E
F000:0013 clc
F000:0014 ja short 0x0018
+F000:0016 jmp short 0xFFF2
F000:0018 stc
F000:0019 jae short 0x0016
F000:001B clc
F000:001C jae short 0x0020
+F000:001E jmp short 0xFFF2
F000:0020 jb short 0x001E
F000:0022 stc
F000:0023 jb short 0x0027
+F000:0025 jmp short 0xFFF2
F000:0027 clc
F000:0028 jbe short 0x0025
F000:002A popf
F000:002B jbe short 0x002F
+F000:002D jmp short 0xFFF2
F000:002F push CX
F000:0030 popf
F000:0031 je short 0x002D
F000:0033 push BX
F000:0034 popf
F000:0035 je short 0x0039
+F000:0037 jmp short 0xFFF2
F000:0039 mov DX,0x08C0
F000:003C push DX
F000:003D popf
@@ -33,6 +39,7 @@ F000:0040 mov DX,0x0880
F000:0043 push DX
F000:0044 popf
F000:0045 jg short 0x0049
+F000:0047 jmp short 0xFFF2
F000:0049 mov DX,0x0080
F000:004C push DX
F000:004D popf
@@ -40,22 +47,26 @@ F000:004E jge short 0x0047
F000:0050 push CX
F000:0051 popf
F000:0052 jge short 0x0056
+F000:0054 jmp short 0xFFF2
F000:0056 jl short 0x0054
F000:0058 mov DX,0x0800
F000:005B push DX
F000:005C popf
F000:005D jl short 0x0061
+F000:005F jmp short 0xFFF2
F000:0061 push CX
F000:0062 popf
F000:0063 jle short 0x005F
F000:0065 push BX
F000:0066 popf
F000:0067 jle short 0x006B
+F000:0069 jmp short 0xFFF2
F000:006B jne short 0x0069
F000:006D mov DX,0x0CBF
F000:0070 push DX
F000:0071 popf
F000:0072 jne short 0x0077
+F000:0074 jmp near 0xFFF2
F000:0077 mov DX,0x0800
F000:007A push DX
F000:007B popf
@@ -64,6 +75,7 @@ F000:007E mov DX,0x06FF
F000:0081 push DX
F000:0082 popf
F000:0083 jno short 0x0088
+F000:0085 jmp near 0xFFF2
F000:0088 mov DX,4
F000:008B push DX
F000:008C popf
@@ -71,6 +83,7 @@ F000:008D jnp short 0x0085
F000:008F push CX
F000:0090 popf
F000:0091 jnp short 0x0096
+F000:0093 jmp near 0xFFF2
F000:0096 mov DX,0x0EFF
F000:0099 push DX
F000:009A popf
@@ -78,21 +91,26 @@ F000:009B jns short 0x0093
F000:009D push CX
F000:009E popf
F000:009F jns short 0x00A4
+F000:00A1 jmp near 0xFFF2
F000:00A4 jo short 0x00A1
F000:00A6 mov DX,0x0800
F000:00A9 push DX
F000:00AA popf
F000:00AB jo short 0x00B0
+F000:00AD jmp near 0xFFF2
F000:00B0 jp short 0x00AD
F000:00B2 mov DX,0x0804
F000:00B5 push DX
F000:00B6 popf
F000:00B7 jp short 0x00BC
+F000:00B9 jmp near 0xFFF2
F000:00BC js short 0x00B9
F000:00BE mov DX,0x0884
F000:00C1 push DX
F000:00C2 popf
F000:00C3 js short 0x00C8
+F000:00C5 jmp near 0xFFF2
F000:00C8 mov word ptr DS:[0],0x1234
F000:00CE hlt
F000:FFF0 jmp short 0
+F000:FFF2 hlt
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump2.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump2.txt
index 0b6ea53262..1bc9fbc934 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump2.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/jump2.txt
@@ -12,10 +12,12 @@ F000:1295 mov DX,0x0040
F000:1298 push DX
F000:1299 popf
F000:129A loope 0x129D
+F000:129C hlt
F000:129D call far E342:EBE0
F000:12A2 jcxz short 0x129D
F000:12A4 mov CX,0
F000:12A7 jcxz short 0x12AA
+F000:12A9 hlt
F000:12AA ret near 0x000A
E342:EBE0 call near word ptr DS:[0x3000]
E342:EBE4 mov DX,0
@@ -24,6 +26,7 @@ E342:EBE8 popf
E342:EBE9 mov CX,1
E342:EBEC loopne 0xEBE9
E342:EBEE loopne 0xEBF1
+E342:EBF0 hlt
E342:EBF1 ret far
E342:FDE0 mov BX,0x2FF0
E342:FDE3 mov SI,0x0010
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/rep.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/rep.txt
index 81a50519df..0c43749016 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/rep.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/rep.txt
@@ -66,6 +66,7 @@ F000:2023 mov AX,word ptr DS:[0]
F000:2026 mov BP,DI
F000:2028 mov word ptr SS:[BP+SI],AX
F000:202A jcxz short 0x202D
+F000:202C hlt
F000:202D jmp near word ptr DS:[0x3004]
F000:300D mov AX,0xF000
F000:3010 mov DS,AX
@@ -80,6 +81,7 @@ F000:3021 mov AX,word ptr DS:[2]
F000:3024 mov BP,DI
F000:3026 mov word ptr SS:[BP+SI],AX
F000:3028 jcxz short 0x302B
+F000:302A hlt
F000:302B jmp near word ptr DS:[0x3008]
F000:302F mov AX,0xF000
F000:3032 mov DS,AX
@@ -106,6 +108,7 @@ F000:4004 popf
F000:4005 mov CX,4
F000:4008 rep stos word ptr ES:[DI],AX
F000:400A jcxz short 0x400D
+F000:400C hlt
F000:400D mov BX,0x0040
F000:4010 push BX
F000:4011 popf
@@ -137,12 +140,15 @@ F000:4049 jmp near CX
F000:5000 mov CX,2
F000:5003 rep movs byte ptr ES:[DI],byte ptr DS:[SI]
F000:5005 jcxz short 0x5008
+F000:5007 hlt
F000:5008 mov CX,2
F000:500B rep lods AL,byte ptr DS:[SI]
F000:500D jcxz short 0x5010
+F000:500F hlt
F000:5010 mov CX,2
F000:5013 rep stos byte ptr ES:[DI],AL
F000:5015 jcxz short 0x5018
+F000:5017 hlt
F000:5018 mov BX,0
F000:501B push BX
F000:501C popf
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/selfmodifyterminator.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/selfmodifyterminator.txt
index 084581297b..39533a840f 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/selfmodifyterminator.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/selfmodifyterminator.txt
@@ -10,6 +10,9 @@ F000:0017 or AX,AX
F000:0019 selector
F000:0019 jmp short 0x0020
F000:0019 jne short 0x0010
+F000:001B mov AX,0xDEAD
+F000:001E push AX
+F000:001F hlt
F000:0020 push AX
F000:0021 hlt
F000:0022 cmp byte ptr DS:[0x0040],0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_branch.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_branch.txt
new file mode 100644
index 0000000000..99b50d2cc9
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_branch.txt
@@ -0,0 +1,11 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x001A
+F000:0013 mov byte ptr DS:[0x0401],0xEE
+F000:0018 jmp short 0x001A
+F000:001A mov byte ptr DS:[0x0402],0xAA
+F000:001F hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_call_entry.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_call_entry.txt
new file mode 100644
index 0000000000..baf6e74daf
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_call_entry.txt
@@ -0,0 +1,12 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x001E
+F000:0013 call near 0x001B
+F000:001B mov AL,0x42
+F000:001D ret near
+F000:001E mov byte ptr DS:[0x0402],0xAA
+F000:0023 hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_closure.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_closure.txt
new file mode 100644
index 0000000000..0659f233ea
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_closure.txt
@@ -0,0 +1,15 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x0024
+F000:0013 mov CX,3
+F000:0016 mov byte ptr DS:[0x0401],0
+F000:001B inc byte ptr DS:[0x0401]
+F000:001F dec CX
+F000:0020 jne short 0x001B
+F000:0022 jmp short 0x0024
+F000:0024 mov byte ptr DS:[0x0402],0xBB
+F000:0029 hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_convergence.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_convergence.txt
new file mode 100644
index 0000000000..e930d906f0
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_convergence.txt
@@ -0,0 +1,12 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xAA
+F000:0011 jmp short 0x001A
+F000:0013 mov byte ptr DS:[0x0401],0xBB
+F000:0018 jmp short 0x001A
+F000:001A mov byte ptr DS:[0x0402],0xCC
+F000:001F mov byte ptr DS:[0x0403],0xFF
+F000:0024 hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_discard.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_discard.txt
new file mode 100644
index 0000000000..99b50d2cc9
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_discard.txt
@@ -0,0 +1,11 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x001A
+F000:0013 mov byte ptr DS:[0x0401],0xEE
+F000:0018 jmp short 0x001A
+F000:001A mov byte ptr DS:[0x0402],0xAA
+F000:001F hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_invalid_opcode.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_invalid_opcode.txt
new file mode 100644
index 0000000000..bd47affed5
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_invalid_opcode.txt
@@ -0,0 +1,9 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x0017
+F000:0017 mov byte ptr DS:[0x0402],0xEE
+F000:001C hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_mixed_block.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_mixed_block.txt
new file mode 100644
index 0000000000..70bbdf122b
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_mixed_block.txt
@@ -0,0 +1,10 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 cmp AL,0
+F000:000A je short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xEE
+F000:0011 jmp short 0x0018
+F000:0013 mov byte ptr DS:[0x0401],0xDD
+F000:0018 mov byte ptr DS:[0x0402],0xAA
+F000:001D hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_smc_guard.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_smc_guard.txt
new file mode 100644
index 0000000000..8ea0355164
--- /dev/null
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_smc_guard.txt
@@ -0,0 +1,12 @@
+F000:0000 mov byte ptr DS:[0x0400],1
+F000:0005 mov AL,byte ptr DS:[0x0500]
+F000:0008 and AL,AL
+F000:000A jne short 0x0013
+F000:000C mov byte ptr DS:[0x0401],0xDD
+F000:0011 jmp short 0x0020
+F000:0013 mov byte ptr CS:[0x001A],0xEE
+F000:0019 mov byte ptr DS:[0x0401],0
+F000:001E jmp short 0x0020
+F000:0020 mov byte ptr DS:[0x0402],0xAA
+F000:0025 hlt
+F000:FFF0 jmp short 0
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/strings.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/strings.txt
index f35e5bbac1..f393a77210 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/strings.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/strings.txt
@@ -28,6 +28,7 @@ F000:1300 mov word ptr DS:[DI],AX
F000:1302 mov ES,DX
F000:1304 scas AX,word ptr ES:[DI]
F000:1305 je short 0x1350
+F000:1307 hlt
F000:1350 mov AL,0x80
F000:1352 std
F000:1353 stos byte ptr ES:[DI],AL
diff --git a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/test386.txt b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/test386.txt
index c0b02e4440..73e1134763 100644
--- a/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/test386.txt
+++ b/tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/test386.txt
@@ -23,23 +23,28 @@ F000:0083 mov AH,1
F000:0085 sahf
F000:0086 jae short 0x00AC
F000:0088 jb short 0x00AD
+F000:008A hlt
F000:008B mov AH,0x40
F000:008D sahf
F000:008E jne short 0x00AC
F000:0090 je short 0x00AF
+F000:0092 hlt
F000:0093 mov AH,4
F000:0095 sahf
F000:0096 jnp short 0x00AC
F000:0098 jp short 0x00B1
+F000:009A hlt
F000:009B mov AH,0x80
F000:009D sahf
F000:009E jns short 0x00AC
F000:00A0 js short 0x00B3
+F000:00A2 hlt
F000:00A3 mov AH,0x41
F000:00A5 sahf
F000:00A6 ja short 0x00AC
F000:00A8 jbe short 0x00B5
F000:00AA jmp short 0x00B7
+F000:00AC hlt
F000:00AD jb short 0x008B
F000:00AF je short 0x0093
F000:00B1 jp short 0x009B
@@ -50,18 +55,23 @@ F000:00B9 sahf
F000:00BA mov AX,0
F000:00BD sahf
F000:00BE jae short 0x00DB
+F000:00C0 hlt
F000:00C1 mov AH,0x95
F000:00C3 sahf
F000:00C4 jne short 0x00DD
+F000:00C6 hlt
F000:00C7 mov AH,0xD1
F000:00C9 sahf
F000:00CA jnp short 0x00DF
+F000:00CC hlt
F000:00CD mov AH,0x55
F000:00CF sahf
F000:00D0 jns short 0x00E1
+F000:00D2 hlt
F000:00D3 mov AH,0x94
F000:00D5 sahf
F000:00D6 ja short 0x00E3
+F000:00D8 hlt
F000:00D9 jmp short 0x00E5
F000:00DB jae short 0x00C1
F000:00DD jne short 0x00C7
@@ -74,14 +84,19 @@ F000:00E8 mov AL,0x40
F000:00EA shl AL,1
F000:00EC jno short 0x0122
F000:00EE jo short 0x0123
+F000:00F0 hlt
F000:00F1 jl short 0x0122
F000:00F3 jge short 0x0125
+F000:00F5 hlt
F000:00F6 jle short 0x0122
F000:00F8 jg short 0x0127
+F000:00FA hlt
F000:00FB mov AH,0x40
F000:00FD sahf
F000:00FE jl short 0x0129
+F000:0100 hlt
F000:0101 jle short 0x012B
+F000:0103 hlt
F000:0104 mov ECX,1
F000:010A jcxz short 0x0122
F000:010C mov ECX,0x00010000
@@ -90,6 +105,7 @@ F000:0114 jecxz short 0x0122
F000:0117 mov ECX,0
F000:011D jecxz short 0x012F
F000:0120 jmp short 0x0132
+F000:0122 hlt
F000:0123 jo short 0x00F1
F000:0125 jge short 0x00F6
F000:0127 jg short 0x00FB
@@ -101,23 +117,28 @@ F000:0132 mov AH,1
F000:0134 sahf
F000:0135 jae near 0x01F0
F000:0139 jb near 0x01F1
+F000:013D hlt
F000:013E mov AH,0x40
F000:0140 sahf
F000:0141 jne near 0x01F0
F000:0145 je near 0x01F5
+F000:0149 hlt
F000:014A mov AH,4
F000:014C sahf
F000:014D jnp near 0x01F0
F000:0151 jp near 0x01F9
+F000:0155 hlt
F000:0156 mov AH,0x80
F000:0158 sahf
F000:0159 jns near 0x01F0
F000:015D js near 0x01FD
+F000:0161 hlt
F000:0162 mov AH,0x41
F000:0164 sahf
F000:0165 ja near 0x01F0
F000:0169 jbe near 0x0201
F000:016D jmp near 0x0205
+F000:01F0 hlt
F000:01F1 jb near 0x013E
F000:01F5 je near 0x014A
F000:01F9 jp near 0x0156
@@ -128,18 +149,23 @@ F000:0207 sahf
F000:0208 mov AX,0
F000:020B sahf
F000:020C jae near 0x02B4
+F000:0210 hlt
F000:0211 mov AH,0x95
F000:0213 sahf
F000:0214 jne near 0x02B8
+F000:0218 hlt
F000:0219 mov AH,0xD1
F000:021B sahf
F000:021C jnp near 0x02BC
+F000:0220 hlt
F000:0221 mov AH,0x55
F000:0223 sahf
F000:0224 jns near 0x02C0
+F000:0228 hlt
F000:0229 mov AH,0x94
F000:022B sahf
F000:022C ja near 0x02C4
+F000:0230 hlt
F000:0231 jmp near 0x02C8
F000:02B4 jae near 0x0211
F000:02B8 jne near 0x0219
@@ -152,15 +178,21 @@ F000:02CB mov AL,0x40
F000:02CD shl AL,1
F000:02CF jno near 0x037A
F000:02D3 jo near 0x037B
+F000:02D7 hlt
F000:02D8 jl near 0x037A
F000:02DC jge near 0x037F
+F000:02E0 hlt
F000:02E1 jle near 0x037A
F000:02E5 jg near 0x0383
+F000:02E9 hlt
F000:02EA mov AH,0x40
F000:02EC sahf
F000:02ED jl near 0x0387
+F000:02F1 hlt
F000:02F2 jle near 0x038B
+F000:02F6 hlt
F000:02F7 jmp near 0x038F
+F000:037A hlt
F000:037B jo near 0x02D8
F000:037F jge near 0x02E1
F000:0383 jg near 0x02EA
@@ -1303,4 +1335,9 @@ F000:15BF mov DX,0x0999
F000:15C2 out DX,AL
F000:15C3 cli
F000:15C4 hlt
+F000:15C7 mov AX,CS
+F000:15C9 test AX,7
+F000:15CC jne short 0x15CC
+F000:15CE cli
+F000:15CF jmp short 0x15C7
F000:FFF0 jmp far F000:0045
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_branch.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_branch.bin
new file mode 100644
index 0000000000..2de1f0caaa
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_branch.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_call_entry.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_call_entry.bin
new file mode 100644
index 0000000000..72ee7215e8
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_call_entry.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_closure.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_closure.bin
new file mode 100644
index 0000000000..985a69ffaf
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_closure.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_convergence.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_convergence.bin
new file mode 100644
index 0000000000..17f88fd09f
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_convergence.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_discard.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_discard.bin
new file mode 100644
index 0000000000..2de1f0caaa
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_discard.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_invalid_opcode.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_invalid_opcode.bin
new file mode 100644
index 0000000000..56a14e318d
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_invalid_opcode.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_mixed_block.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_mixed_block.bin
new file mode 100644
index 0000000000..fa76029d17
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_mixed_block.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_seeded_timer_sweep.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_seeded_timer_sweep.bin
new file mode 100644
index 0000000000..df8b9f18eb
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_seeded_timer_sweep.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/speculative_smc_guard.bin b/tests/Spice86.Tests/Resources/cpuTests/speculative_smc_guard.bin
new file mode 100644
index 0000000000..3a98fda9ea
Binary files /dev/null and b/tests/Spice86.Tests/Resources/cpuTests/speculative_smc_guard.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/strings.bin b/tests/Spice86.Tests/Resources/cpuTests/strings.bin
index 3ea6e2b7bb..60239d666e 100644
Binary files a/tests/Spice86.Tests/Resources/cpuTests/strings.bin and b/tests/Spice86.Tests/Resources/cpuTests/strings.bin differ
diff --git a/tests/Spice86.Tests/Resources/cpuTests/test386.bin b/tests/Spice86.Tests/Resources/cpuTests/test386.bin
index c53825dd23..a92a2efa55 100644
Binary files a/tests/Spice86.Tests/Resources/cpuTests/test386.bin and b/tests/Spice86.Tests/Resources/cpuTests/test386.bin differ
diff --git a/tests/Spice86.Tests/SpeculativeCfgTest.cs b/tests/Spice86.Tests/SpeculativeCfgTest.cs
new file mode 100644
index 0000000000..48cab5c83b
--- /dev/null
+++ b/tests/Spice86.Tests/SpeculativeCfgTest.cs
@@ -0,0 +1,875 @@
+namespace Spice86.Tests;
+
+using FluentAssertions;
+
+using Spice86.Core.CLI;
+using Spice86.Core.Emulator.CPU.CfgCpu;
+using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
+using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
+using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying;
+using Spice86.Core.Emulator.ReverseEngineer.CfgCodeGeneration;
+using Spice86.Core.Emulator.ReverseEngineer.CfgCodeGeneration.Model;
+using Spice86.Core.Emulator.VM;
+using Spice86.Shared.Emulator.Memory;
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using Xunit;
+
+///
+/// Tests for the Speculative CFG Exploration feature.
+/// These validate that the explorer, promoter, generator, and runtime guard work end-to-end.
+///
+public sealed class SpeculativeCfgTest {
+ ///
+ /// The generated code must handle both branches from a single discovery run.
+ ///
+ /// Discovery run observes only selector=0 (fallthrough path). The generated code must emit
+ /// a guarded speculative branch for the unobserved arm (selector=1). When run with selector=1,
+ /// the generated override itself must execute the speculative path and produce the correct result
+ /// without falling back to the interpreter.
+ ///
+ /// This test performs a SINGLE discovery (selector=0), generates code from that trace, then
+ /// runs the compiled override with selector=1. If the emitter still produces FailAsUntested
+ /// instead of a guarded goto, this test will fail.
+ ///
+ [Fact]
+ public void SpeculativeBranchExecutesCorrectlyOnBothPaths() {
+ // Single discovery run with selector=0 (only observes fallthrough path)
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_branch", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ // Assert the generated source emits a speculative guard, NOT FailAsUntested,
+ // for the unobserved conditional jump target (F000:000A -> F000:0013).
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the emitter must produce a speculative guard for the unobserved JNZ arm, not FailAsUntested");
+ source.Should().NotContain("Unobserved conditional jump target at F000:000A",
+ "the speculative block at F000:0013 is reachable from the observed conditional and must not be treated as untested");
+
+ // Compile the override generated from selector=0 discovery
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Run with selector=0 (observed path) - should produce 0xDD
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+
+ RunWithCompiledOverride("speculative_branch", compiledOverride, expectedDiscovery, 1000);
+
+ // Run with selector=1 (speculative path) using the SAME generated code
+ // The override must handle this path via the guarded speculative branch.
+ byte[] expectedSpeculative = new byte[0x403];
+ expectedSpeculative[0x400] = 0x01;
+ expectedSpeculative[0x401] = 0xEE;
+ expectedSpeculative[0x402] = 0xAA;
+
+ RunWithCompiledOverride("speculative_branch", compiledOverride, expectedSpeculative, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ ///
+ /// Regression for the cross-variant convergence leak: running the self-modifying fixture with
+ /// speculation on must not introduce a SelectorNode at F000:1403. The three opcodes that occupy
+ /// that address over time (push AX / shl BX / inc CX) are reached on distinct predecessor edges,
+ /// so the observed-only graph has no selector there. Speculation must converge only on matching
+ /// instructions (same final-field signature), never wiring a predecessor to an unrelated variant
+ /// that would later force a selector during execution-time reconciliation.
+ ///
+ [Fact]
+ public void SpeculationOnSelfModifyInstructionsCreatesNoSelectorAtCrossVariantAddress() {
+ using Spice86Creator creator = new(binName: "selfmodifyinstructions", maxCycles: 100000,
+ enableSpeculativeCfgExploration: true);
+ using Spice86DependencyInjection di = creator.Create();
+ di.ProgramExecutor.Run();
+ Machine machine = di.Machine;
+
+ ISet allNodes = BrowseAllReachableNodes(machine);
+
+ uint linearOf1403 = 0xF0000 + 0x1403;
+ bool selectorAt1403 = allNodes.Any(node => node is SelectorNode && node.Address.Linear == linearOf1403);
+ selectorAt1403.Should().BeFalse(
+ "speculation must not wire cross-variant convergence edges that force a SelectorNode at F000:1403");
+
+ // The three distinct opcodes must still all be present at that address as separate variants.
+ int instructionsAt1403 = allNodes
+ .OfType()
+ .Count(node => node.Address.Linear == linearOf1403);
+ instructionsAt1403.Should().Be(3,
+ "push AX, shl BX and inc CX should each exist as their own variant at F000:1403");
+ }
+
+ private static ISet BrowseAllReachableNodes(Machine machine) {
+ IEnumerable entryPoints = machine.CfgCpu.ExecutionContextManager
+ .ExecutionContextEntryPoints.Values.SelectMany(nodes => nodes);
+ Queue queue = new(entryPoints);
+ HashSet visited = new();
+ while (queue.Count > 0) {
+ ICfgNode node = queue.Dequeue();
+ if (!visited.Add(node)) {
+ continue;
+ }
+ foreach (ICfgNode successor in node.Successors) {
+ queue.Enqueue(successor);
+ }
+ foreach (ICfgNode predecessor in node.Predecessors) {
+ queue.Enqueue(predecessor);
+ }
+ }
+ return visited;
+ }
+
+ ///
+ /// Source-level: the speculative branch fixture compiles and runs successfully via the
+ /// generated override on the observed path.
+ ///
+ [Fact]
+ public void SpeculativeBranchGeneratedOverrideCompilesAndRuns() {
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_branch", expectedDiscovery,
+ new GeneratedCodeRunOptions { MaxCycles = 1000 });
+ }
+
+ ///
+ /// Flag-off parity / speculation-on behavior for jump1.
+ /// With speculation enabled (default), jump1's unobserved fallthrough paths are resolved
+ /// via same-partition block entry lookup (speculative blocks are reachable from observed terminators).
+ ///
+ [Fact]
+ public void SpeculationOnJump1ResolvesUnobservedFallthroughs() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("jump1", options);
+ string source = generatedProgram.SourceText;
+
+ // jump1's always-taken conditional jumps leave their fallthrough edge unobserved. With
+ // speculation off those edges lower to FailAsUntested guarded by an "Unobserved conditional
+ // fallthrough" comment. With speculation on, every such fallthrough target is a block entry
+ // reachable from an observed terminator in the same partition, so it must be resolved rather
+ // than left untested.
+ source.Should().NotContain("Unobserved conditional fallthrough",
+ "speculation must resolve jump1's unobserved fallthrough edges to their same-partition block entries");
+ source.Should().NotContain("FailAsUntested",
+ "no jump1 arm should remain untested once speculation resolves the fallthrough edges");
+
+ // The resolved code must still compile and execute to the correct result via the override.
+ string memoryDumpPath = "Resources/cpuTests/res/MemoryDumps/jump1.bin";
+ byte[] expected = File.Exists(memoryDumpPath) ? File.ReadAllBytes(memoryDumpPath) : [];
+ runner.TestGeneratedCode("jump1", expected, options);
+ }
+
+ ///
+ /// Recursive closure (multi-block unobserved arm).
+ /// A single discovery run (selector=0) observes only the fallthrough. The generated code must
+ /// emit a guarded speculative closure for the loop path (selector=1). Running the same generated
+ /// code with selector=1 must execute the speculative loop and produce the correct result.
+ ///
+ [Fact]
+ public void SpeculativeClosureMultiBlockLoopExecutesCorrectly() {
+ // Single discovery run with selector=0 (only observes fallthrough)
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_closure", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ // Assert speculative guard is emitted for the closure entry
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the emitter must produce a speculative guard for the unobserved JNZ arm leading to the loop closure");
+ source.Should().NotContain("Unobserved conditional jump target",
+ "the speculative closure must be resolved, not treated as untested");
+
+ // Compile the override generated from selector=0 discovery
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Run with selector=0 (observed path) - should produce 0xDD
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xBB;
+
+ RunWithCompiledOverride("speculative_closure", compiledOverride, expectedDiscovery, 1000);
+
+ // Run with selector=1 (speculative loop path) using the SAME generated code
+ byte[] expectedSpeculative = new byte[0x403];
+ expectedSpeculative[0x400] = 0x01;
+ expectedSpeculative[0x401] = 0x03;
+ expectedSpeculative[0x402] = 0xBB;
+
+ RunWithCompiledOverride("speculative_closure", compiledOverride, expectedSpeculative, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ ///
+ /// Source-level: the speculative closure fixture compiles and runs successfully on the observed path.
+ ///
+ [Fact]
+ public void SpeculativeClosureGeneratedOverrideCompilesAndRuns() {
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xBB;
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode("speculative_closure", expectedDiscovery,
+ new GeneratedCodeRunOptions { MaxCycles = 1000 });
+ }
+
+ ///
+ /// Hard-stop on indirect transfer. The 'segpr' fixture has a direct call whose callee never
+ /// returns (no observed continuation). With speculation on, the call continuation is still NOT
+ /// speculated (it's out of scope by design). The generated source must still contain
+ /// "no continuation was observed during discovery" for that call.
+ ///
+ [Fact]
+ public void CallContinuationStaysOutOfScope() {
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("segpr", maxCycles: 10000);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("but no continuation was observed during discovery.");
+ }
+
+ ///
+ /// Mixed-block guard placed mid-block after observed prefix.
+ /// The speculative_branch fixture contains a conditional where the fallthrough is observed and
+ /// the JNZ target is speculative. The block containing the JNZ arm starts with the speculative
+ /// instruction. The guard must appear BEFORE the first speculative instruction's generated comment.
+ ///
+ [Fact]
+ public void MixedBlockGuardPlacedBeforeFirstSpeculativeInstruction() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_branch", options);
+ string source = generatedProgram.SourceText;
+
+ // The guard must appear in the generated source
+ source.Should().Contain("VerifySpeculativeEntryOrFail(");
+
+ // The guard must come BEFORE the first speculative block's instructions in the source
+ int guardIndex = source.IndexOf("VerifySpeculativeEntryOrFail(", StringComparison.Ordinal);
+ // F000:0013 is the alt_path entry (speculative JNZ target)
+ int speculativeInstructionIndex = source.IndexOf("// F000:0013", StringComparison.Ordinal);
+ speculativeInstructionIndex.Should().BeGreaterThanOrEqualTo(0,
+ "the first speculative instruction comment (F000:0013) must be present in the generated source");
+ guardIndex.Should().BeLessThan(speculativeInstructionIndex,
+ "the speculative guard must be emitted before the first speculative instruction comment");
+ }
+
+ ///
+ /// Flag-off produces FailAsUntested, no guard.
+ /// With EnableSpeculativeCfgExploration = false, the generated code should NOT contain
+ /// VerifySpeculativeEntryOrFail and should still contain FailAsUntested for unobserved arms.
+ ///
+ [Fact]
+ public void FlagOffProducesFailAsUntestedNoGuard() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = false
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_branch", options);
+ string source = generatedProgram.SourceText;
+
+ source.Should().NotContain("VerifySpeculativeEntryOrFail",
+ "with speculation disabled, no speculative guards should be emitted");
+ source.Should().Contain("FailAsUntested",
+ "with speculation disabled, unobserved arms must still produce FailAsUntested");
+ }
+
+ ///
+ /// Convergence onto observed block emits plain goto, no guard.
+ /// When a speculative arm targets a block that was already observed (in the same partition),
+ /// the generated code should emit a plain goto to that label, not a VerifySpeculativeEntryOrFail guard.
+ /// The observed target is already confirmed by execution - no verification needed.
+ ///
+ [Fact]
+ public void ConvergenceOntoObservedBlockEmitsPlainGotoNoGuard() {
+ // The speculative arm of speculative_branch converges onto the 'done' block, which was observed
+ // during discovery (reached via the fallthrough path). Speculative instructions are guarded
+ // per-instruction, but the JMP into 'done' must be a plain goto: the observed convergence target
+ // is already confirmed by execution and must never be re-verified.
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_branch", options);
+ string source = generatedProgram.SourceText;
+
+ // The speculative block entry at F000:0013 is guarded.
+ source.Should().Contain("VerifySpeculativeEntryOrFail(cs1, 0x0013",
+ "the speculative block entry at F000:0013 must be guarded");
+
+ // The observed convergence target 'done' (F000:001A) must NOT be guarded: the speculative arm
+ // reaches it via a plain goto, not a guarded entry. Per-instruction guards cover the speculative
+ // instructions themselves (e.g. the JMP at F000:0018), but never the observed target they jump to.
+ source.Should().NotContain("VerifySpeculativeEntryOrFail(cs1, 0x001A",
+ "the observed 'done' block at F000:001A is a convergence target and must be reached by a plain goto, never a speculative guard");
+ }
+
+ ///
+ /// Poison set persistence: after a dump-then-reload cycle the poison set is preserved.
+ ///
+ [Fact]
+ public void PoisonSetSurvivedDumpReloadCycle() {
+ // Run a program to build a graph, manually poison an address, dump, reload, and verify.
+ using Spice86Creator discoveryCreator = new(binName: "add", maxCycles: 1000);
+ using Spice86DependencyInjection discoveryDi = discoveryCreator.Create();
+ discoveryDi.ProgramExecutor.Run();
+
+ Machine discoveryMachine = discoveryDi.Machine;
+ CfgNodeIndex nodeIndex = discoveryMachine.CfgCpu.CfgNodeFeeder.NodeIndex;
+
+ // Manually poison an address
+ Spice86.Shared.Emulator.Memory.SegmentedAddress poisonedAddr = new(0x0170, 0x0050);
+ nodeIndex.PoisonSet.Add(poisonedAddr);
+ nodeIndex.PoisonSet.Should().Contain(poisonedAddr);
+
+ // Export and reload
+ Spice86.Core.Emulator.StateSerialization.CfgReload.CfgReloadDump dump =
+ new Spice86.Core.Emulator.StateSerialization.CfgReload.CfgReloadExporter()
+ .Export(discoveryMachine.CfgCpu.ExecutionContextManager, nodeIndex.PoisonSet);
+
+ dump.PoisonedAddresses.Should().NotBeNull();
+ dump.PoisonedAddresses.Should().Contain(poisonedAddr.ToString());
+
+ // Verify it round-trips through JSON
+ string json = System.Text.Json.JsonSerializer.Serialize(dump,
+ Spice86.Core.Emulator.StateSerialization.CfgReload.CfgReloadSerialization.Options);
+ Spice86.Core.Emulator.StateSerialization.CfgReload.CfgReloadDump reloaded =
+ System.Text.Json.JsonSerializer.Deserialize(json,
+ Spice86.Core.Emulator.StateSerialization.CfgReload.CfgReloadSerialization.Options)
+ ?? throw new InvalidOperationException("the dump must deserialize back into a non-null CfgReloadDump");
+
+ reloaded.PoisonedAddresses.Should().Contain(poisonedAddr.ToString());
+ }
+
+ ///
+ /// Direct call entry on speculative path. Discovery run (selector=0) never calls the
+ /// subroutine. The speculative path explores into the callee but does NOT speculate the
+ /// call continuation. When run with selector=1, the generated code enters the callee via
+ /// the speculative guard but after ret hits "no continuation was observed" (expected).
+ /// This validates that call entry IS explored but call continuation is NOT.
+ ///
+ [Fact]
+ public void SpeculativeCallEntryExploresCalleeButNotContinuation() {
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_call_entry", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ // The speculative call path must be guarded
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the speculative call path must be guarded");
+
+ // Call continuation is NOT speculated - it should produce FailAsUntested
+ source.Should().Contain("but no continuation was observed during discovery",
+ "call continuations are not speculated per design - they stay as untested");
+ }
+
+ ///
+ /// Convergence onto observed code - both paths reach the same merge point.
+ /// Discovery (selector=0) observes the mergepoint via path A. Speculative path B (selector=1)
+ /// converges onto the same observed mergepoint block. No duplicate, just a goto.
+ ///
+ [Fact]
+ public void SpeculativeConvergenceOntoObservedCodeExecutesCorrectly() {
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_convergence", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Discovery path (selector=0): path A writes 0xAA, mergepoint writes 0xCC, 0xFF
+ byte[] expectedDiscovery = new byte[0x404];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xAA;
+ expectedDiscovery[0x402] = 0xCC;
+ expectedDiscovery[0x403] = 0xFF;
+ RunWithCompiledOverride("speculative_convergence", compiledOverride, expectedDiscovery, 1000);
+
+ // Speculative path (selector=1): path B writes 0xBB, same mergepoint writes 0xCC, 0xFF
+ byte[] expectedSpeculative = new byte[0x404];
+ expectedSpeculative[0x400] = 0x01;
+ expectedSpeculative[0x401] = 0xBB;
+ expectedSpeculative[0x402] = 0xCC;
+ expectedSpeculative[0x403] = 0xFF;
+ RunWithCompiledOverride("speculative_convergence", compiledOverride, expectedSpeculative, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ ///
+ /// Invalid opcode hard-stop. The generated source still has FailAsUntested for the arm
+ /// that would decode into invalid opcodes (the explorer hard-stopped).
+ ///
+ [Fact]
+ public void SpeculativeInvalidOpcodeStaysFailAsUntested() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_invalid_opcode", options);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("FailAsUntested",
+ "an arm that decodes to invalid opcodes should remain untested (explorer hard-stopped)");
+
+ // F000:0013 is the JNZ target that decodes into the data region (0xFF 0xFF invalid opcodes).
+ // The explorer hard-stops there, so that arm must stay FailAsUntested and must NOT get a
+ // speculative guard - a guard would imply the explorer successfully speculated the path.
+ source.Should().NotContain("VerifySpeculativeEntryOrFail(cs1, 0x0013",
+ "the invalid-opcode data region at F000:0013 must not be guarded; the explorer hard-stopped on it");
+ }
+
+ ///
+ /// Mixed-block guard placement. The generated code for the speculative fallthrough arm
+ /// must include a guard and execute correctly on both paths.
+ ///
+ [Fact]
+ public void SpeculativeMixedBlockGuardPlacement() {
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_mixed_block", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the unobserved fallthrough arm must be guarded");
+
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Discovery path (selector=0): JE taken, writes 0xDD
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+ RunWithCompiledOverride("speculative_mixed_block", compiledOverride, expectedDiscovery, 1000);
+
+ // Speculative path (selector=1): JE not taken, fallthrough writes 0xEE
+ byte[] expectedSpeculative = new byte[0x403];
+ expectedSpeculative[0x400] = 0x01;
+ expectedSpeculative[0x401] = 0xEE;
+ expectedSpeculative[0x402] = 0xAA;
+ RunWithCompiledOverride("speculative_mixed_block", compiledOverride, expectedSpeculative, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ ///
+ /// SMC inside speculative run triggers guard failure.
+ /// The speculative_smc_guard fixture writes to its own code bytes on the speculative path.
+ /// The generated source must have VerifySpeculativeEntryOrFail. When the speculative path
+ /// is taken, the SMC invalidates the run signature and the guard fires.
+ ///
+ [Fact]
+ public void SpeculativeSmcGuardSourceContainsGuard() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_smc_guard", options);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the speculative SMC path must be guarded so that runtime SMC invalidates the run");
+ }
+
+ ///
+ /// Mid-block self-modifying code inside a speculative run must be caught.
+ ///
+ /// Discovery observes only the fallthrough path (selector=0). On the speculative path
+ /// (selector=1) the speculative block holds two instructions in the same straight-line run:
+ /// the first rewrites the ModRM byte of the second through a CS-segment-override write, so the
+ /// second instruction's bytes no longer match what the explorer decoded and baked into the
+ /// generated body. A single block-entry guard cannot catch this because at block entry the
+ /// bytes still match; the divergence only appears after the earlier instruction has executed.
+ /// Only a guard emitted before each speculative instruction detects it.
+ ///
+ /// Running the override compiled from the selector=0 trace with selector=1 must therefore throw
+ /// (the guard before the modified instruction fires) instead of silently executing the stale
+ /// decode-time body. No external memory modification is performed: the SMC is done by the
+ /// program itself, in-block.
+ ///
+ [Fact]
+ public void SpeculativeSmcGuardMidBlockSelfModificationGuardFires() {
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_smc_guard", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the speculative SMC path must be guarded");
+
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Discovery path (selector=0): no SMC on this path, runs to completion.
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+ RunWithCompiledOverride("speculative_smc_guard", compiledOverride, expectedDiscovery, 1000);
+
+ // Speculative path (selector=1): the first speculative instruction (F000:0013) rewrites the
+ // ModRM byte of the second speculative instruction (F000:0019) in the same block. The guard
+ // emitted before that second instruction detects the divergence and throws. The fired guard
+ // is the one at F000:0019, not the block-entry guard at F000:0013, which proves the
+ // detection happens mid-block rather than only at entry.
+ Action act = () => RunWithCompiledOverride("speculative_smc_guard", compiledOverride, [], 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+
+ act.Should().Throw()
+ .WithInnerException()
+ .WithMessage("*Speculative code at F000:0019 no longer matches memory*");
+ }
+
+ ///
+ /// Discard-on-divergence. Generated code with speculative path B is run after the memory
+ /// at B's target has been modified. The VerifySpeculativeEntryOrFail guard fires (memory changed)
+ /// and throws UnrecoverableException, preventing silent wrong execution.
+ ///
+ [Fact]
+ public void SpeculativeDiscardOnDivergenceGuardFires() {
+ GeneratedCodeRunOptions discoveryOptions = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_discard", discoveryOptions);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("VerifySpeculativeEntryOrFail",
+ "the speculative arm must have a guard");
+
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Discovery path (selector=0): works fine
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+ RunWithCompiledOverride("speculative_discard", compiledOverride, expectedDiscovery, 1000);
+
+ // Speculative path (selector=1) with memory modification: guard fires and throws.
+ // This proves the guard detects byte-level divergence and prevents silent wrong execution.
+ Action act = () => RunWithCompiledOverride("speculative_discard", compiledOverride, [], 1000,
+ machine => {
+ machine.Memory.UInt8[0, 0x0500] = 0x01;
+ // Modify the immediate byte at F000:0017 from 0xEE to 0xEF
+ machine.Memory.UInt8[0xF000, 0x0017] = 0xEF;
+ });
+
+ act.Should().Throw()
+ .WithInnerException()
+ .WithMessage("*Speculative code at F000:0013 no longer matches memory*");
+ }
+
+ ///
+ /// Known-safe handler seeding: after the emulator installs its provided interrupt handlers,
+ /// every emulator-installed hardware IRQ handler must be registered as a CFG generation root (an
+ /// execution-context entry point) so the handler flows into code generation even when no IRQ
+ /// fired during discovery. This is the mechanism that prevents the
+ /// "Could not find an override at address F000:xxxx" crash in generated code.
+ ///
+ /// Verified independently of the production seeding path: expectations are the fixed BIOS-default
+ /// vectors of the hardware IRQ handlers the emulator installs (timer/keyboard/RTC/mouse plus the
+ /// DefaultIrqHandler lines 3,4,5,7,10,11), not the PIC's runtime vector enumeration. Software
+ /// interrupt handlers (reached from the program's own INT instructions) must NOT be seeded.
+ ///
+ [Fact]
+ public void ExternalEventHandlersRegisteredAsGenerationRoots() {
+ // BIOS-default vectors of the emulator-installed hardware IRQ handlers.
+ // IRQ0->0x08 (timer), IRQ1->0x09 (keyboard), IRQ8->0x70 (RTC), IRQ12->0x74 (mouse),
+ // DefaultIrqHandler IRQ3/4/5/7->0x0B/0x0C/0x0D/0x0F, IRQ10/11->0x72/0x73.
+ byte[] hardwareInterruptVectors = [0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0F, 0x70, 0x72, 0x73, 0x74];
+ // Software interrupt handlers: serviced via the program's INT instructions, never seeded.
+ byte[] softwareInterruptVectors = [0x10, 0x16, 0x1C];
+
+ using Spice86Creator creator = new(binName: "add", maxCycles: 1000,
+ installInterruptVectors: true, enableSpeculativeCfgExploration: true);
+ using Spice86DependencyInjection di = creator.Create();
+
+ Machine machine = di.Machine;
+ ExecutionContextManager contextManager = machine.CfgCpu.ExecutionContextManager;
+
+ foreach (byte vectorNumber in hardwareInterruptVectors) {
+ SegmentedAddress handlerAddress = machine.InterruptVectorTable[vectorNumber];
+ handlerAddress.Should().NotBe(SegmentedAddress.ZERO,
+ $"the emulator must install a hardware IRQ handler at vector 0x{vectorNumber:X2}");
+ contextManager.ExecutionContextEntryPoints.Should().ContainKey(handlerAddress,
+ $"hardware IRQ handler at vector 0x{vectorNumber:X2} ({handlerAddress}) must be a generation root");
+ }
+
+ foreach (byte vectorNumber in softwareInterruptVectors) {
+ SegmentedAddress handlerAddress = machine.InterruptVectorTable[vectorNumber];
+ contextManager.ExecutionContextEntryPoints.Should().NotContainKey(handlerAddress,
+ $"software interrupt handler at vector 0x{vectorNumber:X2} ({handlerAddress}) must not be a generation root");
+ }
+ }
+
+ ///
+ /// End-to-end guard for the dangling-generation-root fix. With interrupt vectors installed and
+ /// speculation on, the emulator seeds the INT 8 (IRQ0 / PIT) handler as a speculative CFG
+ /// generation root. The fixture patches a different ISR at that address and lets the timer fire,
+ /// so the first INT 8 reconciles to a signature mismatch and SWEEPS the still-speculative seeded
+ /// root. The sweep must route through the RemoveInstruction fan-out so the root is dropped from
+ /// both the node index and the entry-point set: no detached, de-indexed ghost may survive as a
+ /// dead generation root. Reaching the end of Run (rather than the cycle-limit failsafe) proves the
+ /// handler fired and the reconcile/sweep ran.
+ ///
+ [Fact]
+ public void SeededTimerHandlerSweptDuringReconciliationLeavesNoDanglingRoot() {
+ using Spice86Creator creator = new(binName: "speculative_seeded_timer_sweep",
+ maxCycles: 0xFFFFFFF, enablePit: true, installInterruptVectors: true,
+ enableSpeculativeCfgExploration: true);
+ using Spice86DependencyInjection di = creator.Create();
+ Machine machine = di.Machine;
+
+ SegmentedAddress timerHandlerAddress = machine.InterruptVectorTable[0x8];
+
+ di.ProgramExecutor.Run();
+
+ CfgNodeIndex nodeIndex = machine.CfgCpu.CfgNodeFeeder.NodeIndex;
+ ExecutionContextManager contextManager = machine.CfgCpu.ExecutionContextManager;
+
+ nodeIndex.PoisonSet.Should().Contain(timerHandlerAddress,
+ "the first INT 8 must reconcile to a signature mismatch and poison the seeded handler address");
+
+ foreach (KeyValuePair> entry in contextManager.ExecutionContextEntryPoints) {
+ foreach (CfgInstruction root in entry.Value) {
+ nodeIndex.GetAtAddress(entry.Key).Should().Contain(root,
+ $"generation root at {entry.Key} must still be indexed - a swept root must leave no detached ghost");
+ }
+ }
+ }
+
+ ///
+ /// With speculation off, the hardware IRQ handlers must NOT be registered as generation roots
+ /// (no seeded node is produced), keeping behavior identical to before the feature.
+ ///
+ [Fact]
+ public void ExternalEventHandlersNotRegisteredWhenSpeculationDisabled() {
+ byte[] hardwareInterruptVectors = [0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0F, 0x70, 0x72, 0x73, 0x74];
+
+ using Spice86Creator creator = new(binName: "add", maxCycles: 1000,
+ installInterruptVectors: true, enableSpeculativeCfgExploration: false);
+ using Spice86DependencyInjection di = creator.Create();
+
+ Machine machine = di.Machine;
+ ExecutionContextManager contextManager = machine.CfgCpu.ExecutionContextManager;
+
+ foreach (byte vectorNumber in hardwareInterruptVectors) {
+ SegmentedAddress handlerAddress = machine.InterruptVectorTable[vectorNumber];
+ contextManager.ExecutionContextEntryPoints.Should().NotContainKey(handlerAddress,
+ "with speculation disabled no handler should be registered as a generation root");
+ }
+ }
+
+ ///
+ /// Generation validation: the mouse IRQ handler (vector 0x74, BiosMouseInt74Handler) never
+ /// fires during headless discovery because there's no mouse activity. With speculation + seeding,
+ /// it must still appear in the generated source as a DefineFunction at its F000 address and the
+ /// generated code must compile. This is the exact crash scenario the feature was designed to fix.
+ ///
+ [Fact]
+ public void MouseIrqHandlerEmittedInGeneratedCodeDespiteNeverFiring() {
+ string comFileName = Path.GetFullPath("Resources/cpuTests/intchain.com");
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ InstallInterruptVectors = true,
+ EnableSpeculativeCfgExploration = true
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource(comFileName, options);
+ string source = generatedProgram.SourceText;
+
+ // The mouse IRQ handler (vector 0x74) is at F000:005D in the standard layout.
+ // It must have a DefineFunction even though no mouse event fired during discovery.
+ // Look for a DefineFunction that references 0x005D (the mouse handler offset).
+ source.Should().Contain("0x005D",
+ "the mouse IRQ handler (BiosMouseInt74Handler, vector 0x74) must be emitted as a function "
+ + "even though no mouse event fires during headless discovery - this is the exact crash scenario");
+
+ // The generated source must contain the far call to the RETF stub. Now that far call imm
+ // registers its target as a static successor, the callee IS explored speculatively and the
+ // code generator emits a direct call (not SearchFunctionOverride). Verify the far call target
+ // (the RETF stub) is present as a defined function in the generated source.
+ source.Should().Contain("FarCall",
+ "the mouse handler's far call to the RETF stub must be lowered as a FarCall "
+ + "since far call imm now registers its callee as a static successor for speculative exploration");
+
+ // Must compile.
+ new GeneratedOverrideCompiler().CompileSupplier(source);
+ }
+
+ ///
+ /// End-to-end: with DOS initialized and speculation on, the generated code for
+ /// intchain.com (which exercises INT chains through F000 handlers) compiles AND runs
+ /// successfully as an override. This proves seeded handlers reach generation and execute.
+ ///
+ [Fact]
+ public void ExternalEventHandlerGeneratedOverrideCompilesAndRuns() {
+ string comFileName = Path.GetFullPath("Resources/cpuTests/intchain.com");
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ InstallInterruptVectors = true,
+ EnableSpeculativeCfgExploration = true
+ };
+ new GeneratedCodeMachineTestRunner().TestGeneratedCode(comFileName, [], options);
+ }
+
+ ///
+ /// The single speculation-off scenario: it proves why speculation is enabled everywhere else.
+ ///
+ /// Discovery observes only the fallthrough arm (selector=0). With speculation off the unobserved
+ /// JNZ arm is never explored, so the generator emits FailAsUntested for it instead of a guarded
+ /// speculative branch. The override therefore runs correctly on the observed path but, when run
+ /// with selector=1 (the path speculative discovery would have explored), reaches the untested arm
+ /// and throws. This is the missing-node crash that speculative discovery prevents.
+ ///
+ [Fact]
+ public void SpeculationOffLeavesUnobservedArmUntestedAndCrashesOnThatPath() {
+ GeneratedCodeRunOptions options = new() {
+ MaxCycles = 1000,
+ EnableSpeculativeCfgExploration = false
+ };
+ GeneratedCodeMachineTestRunner runner = new();
+ (_, GeneratedCSharpProgram generatedProgram) = runner.GenerateProgramAndSource("speculative_branch", options);
+ string source = generatedProgram.SourceText;
+
+ source.Should().Contain("FailAsUntested",
+ "with speculation off the unobserved JNZ arm must be left untested");
+ source.Should().NotContain("VerifySpeculativeEntryOrFail",
+ "with speculation off no speculative guard is emitted");
+
+ CompiledGeneratedOverride compiledOverride = new GeneratedOverrideCompiler().CompileSupplier(source);
+
+ // Observed path (selector=0): runs to the correct result.
+ byte[] expectedDiscovery = new byte[0x403];
+ expectedDiscovery[0x400] = 0x01;
+ expectedDiscovery[0x401] = 0xDD;
+ expectedDiscovery[0x402] = 0xAA;
+ RunWithCompiledOverride("speculative_branch", compiledOverride, expectedDiscovery, 1000);
+
+ // Unobserved path (selector=1): the untested arm is reached and the generated code throws,
+ // proving the speculative node omitted by discovery is actually needed at runtime.
+ Action act = () => RunWithCompiledOverride("speculative_branch", compiledOverride, [], 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+
+ act.Should().Throw()
+ .WithInnerException()
+ .WithMessage("*Untested code reached*");
+ }
+
+ ///
+ /// Interpreter-side selector=1 coverage. The golden MachineTest fixtures only run the observed
+ /// (selector=0) path; the selector=1 arm of each fixture is otherwise exercised only through a
+ /// generated override. These tests run the same bins in the pure interpreter (no override) with
+ /// selector=1 selected and assert the resulting memory dump, validating that the interpreter
+ /// itself executes the speculative arm to the correct result.
+ ///
+ [Fact]
+ public void InterpreterExecutesSpeculativeBranchSelectorOnePath() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xEE;
+ expected[0x402] = 0xAA;
+ RunInterpreter("speculative_branch", expected, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ [Fact]
+ public void InterpreterExecutesSpeculativeClosureSelectorOnePath() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0x03;
+ expected[0x402] = 0xBB;
+ RunInterpreter("speculative_closure", expected, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ [Fact]
+ public void InterpreterExecutesSpeculativeConvergenceSelectorOnePath() {
+ byte[] expected = new byte[0x404];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xBB;
+ expected[0x402] = 0xCC;
+ expected[0x403] = 0xFF;
+ RunInterpreter("speculative_convergence", expected, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ [Fact]
+ public void InterpreterExecutesSpeculativeMixedBlockSelectorOnePath() {
+ byte[] expected = new byte[0x403];
+ expected[0x400] = 0x01;
+ expected[0x401] = 0xEE;
+ expected[0x402] = 0xAA;
+ RunInterpreter("speculative_mixed_block", expected, 1000,
+ machine => { machine.Memory.UInt8[0, 0x0500] = 0x01; });
+ }
+
+ ///
+ /// Runs the binary in the pure interpreter (no generated override), applying an optional machine
+ /// configuration before execution, then asserts the memory dump matches expected.
+ ///
+ private static void RunInterpreter(string binName, byte[] expected, long maxCycles,
+ Action configureMachine) {
+ using Spice86Creator creator = new(binName: binName, maxCycles: maxCycles,
+ jitMode: JitMode.InterpretedOnly);
+ using Spice86DependencyInjection di = creator.Create();
+ configureMachine(di.Machine);
+ di.ProgramExecutor.Run();
+
+ byte[] actual = di.Machine.Memory.ReadRam((uint)expected.Length);
+ actual.Should().Equal(expected);
+ }
+
+ ///
+ /// Runs the binary with a pre-compiled override supplier and asserts memory matches expected.
+ /// This allows testing a generated override compiled from one discovery run against different inputs.
+ ///
+ private static void RunWithCompiledOverride(string binName, CompiledGeneratedOverride compiledOverride,
+ byte[] expected, long maxCycles, Action? configureMachine = null) {
+ using Spice86Creator creator = new(binName: binName, maxCycles: maxCycles,
+ jitMode: JitMode.InterpretedOnly, overrideSupplier: compiledOverride.Supplier);
+ using Spice86DependencyInjection di = creator.Create();
+ configureMachine?.Invoke(di.Machine);
+ di.FunctionCatalogue.FunctionInformations.Values
+ .Should().Contain(fi => fi.HasOverride);
+ di.ProgramExecutor.Run();
+
+ byte[] actual = di.Machine.Memory.ReadRam((uint)expected.Length);
+ actual.Should().Equal(expected);
+ }
+}
diff --git a/tests/Spice86.Tests/Spice86Creator.cs b/tests/Spice86.Tests/Spice86Creator.cs
index f49113a86a..2785f32ab7 100644
--- a/tests/Spice86.Tests/Spice86Creator.cs
+++ b/tests/Spice86.Tests/Spice86Creator.cs
@@ -28,7 +28,8 @@ public Spice86Creator(string binName, bool enablePit = false,
ushort sbBase = 0x220, byte sbIrq = 7, byte sbDma = 1, byte sbHdma = 5,
string? exeArgs = null, long? instructionTimeScale = null,
JitMode jitMode = JitMode.InterpretedOnly, bool failOnInvalidOpcode = false,
- string? recordedDataDirectory = null, bool reloadCfgGraph = false) {
+ string? recordedDataDirectory = null, bool reloadCfgGraph = false,
+ bool enableSpeculativeCfgExploration = true) {
string executablePath = Path.IsPathRooted(binName) ? binName : $"Resources/cpuTests/{binName}.bin";
if (overrideSupplierClassName != null && overrideSupplier != null) {
throw new ArgumentException("Provide either an override supplier instance or an override supplier class name, not both.");
@@ -82,6 +83,7 @@ public Spice86Creator(string binName, bool enablePit = false,
JitMode = jitMode,
FailOnInvalidOpcode = failOnInvalidOpcode,
ReloadCfgGraph = reloadCfgGraph,
+ EnableSpeculativeCfgExploration = enableSpeculativeCfgExploration,
};
_maxCycles = maxCycles;
diff --git a/tests/Spice86.Tests/UI/CfgCpu/CfgCpuViewModelTest.cs b/tests/Spice86.Tests/UI/CfgCpu/CfgCpuViewModelTest.cs
index c20053b8b5..849302a636 100644
--- a/tests/Spice86.Tests/UI/CfgCpu/CfgCpuViewModelTest.cs
+++ b/tests/Spice86.Tests/UI/CfgCpu/CfgCpuViewModelTest.cs
@@ -83,7 +83,7 @@ public Harness() {
_monitor = new CfgNodeExecutionCompilerMonitor(_loggerService);
_compiler = new CfgNodeExecutionCompiler(_monitor, _loggerService, JitMode.InterpretedOnly);
SequentialIdAllocator sharedIdAllocator = new();
- CfgNodeFeeder cfgNodeFeeder = new(Memory, State, breakpointsManager, replacerRegistry, _compiler, sharedIdAllocator);
+ CfgNodeFeeder cfgNodeFeeder = new(Memory, State, breakpointsManager, replacerRegistry, _compiler, sharedIdAllocator, enableSpeculativeExploration: false);
Linker = new NodeLinker(replacerRegistry, _compiler, sharedIdAllocator);
ContextManager = new ExecutionContextManager(
Memory, State, cfgNodeFeeder, replacerRegistry,