From 712b7e3a15af8f8eeef5c24c5dc7c69bad15a706 Mon Sep 17 00:00:00 2001 From: Kevin Ferrare Date: Mon, 22 Jun 2026 22:30:02 +0200 Subject: [PATCH] feat: Speculative discovery of nodes in memory --- AGENTS.md | 7 +- src/Spice86.Core/CLI/Configuration.cs | 13 + .../Emulator/CPU/CfgCpu/CfgCpu.cs | 5 +- .../CPU/CfgCpu/ControlFlowGraph/CfgBlock.cs | 70 +- .../CPU/CfgCpu/ControlFlowGraph/CfgNode.cs | 15 + .../CPU/CfgCpu/ControlFlowGraph/ICfgNode.cs | 18 + .../CPU/CfgCpu/ExecutionContextManager.cs | 54 + .../CPU/CfgCpu/Feeder/CfgNodeFeeder.cs | 30 +- .../CPU/CfgCpu/Feeder/CfgNodeIndex.cs | 132 + .../CPU/CfgCpu/Feeder/CurrentInstructions.cs | 11 + .../CPU/CfgCpu/Feeder/IInstructionReplacer.cs | 9 +- .../CPU/CfgCpu/Feeder/InstructionReplacer.cs | 1 + .../Feeder/InstructionReplacerRegistry.cs | 6 + .../CPU/CfgCpu/Feeder/InstructionsFeeder.cs | 106 +- .../CPU/CfgCpu/Feeder/PreviousInstructions.cs | 9 + .../CPU/CfgCpu/Feeder/SpeculativeExplorer.cs | 192 + .../CPU/CfgCpu/Feeder/SpeculativePromoter.cs | 37 + .../Feeder/SpeculativeReachabilityPruner.cs | 97 + .../CfgCpu/Feeder/SpeculativeReconciler.cs | 77 + .../Emulator/CPU/CfgCpu/Linker/NodeLinker.cs | 150 +- .../ParsedInstruction/CfgInstruction.cs | 60 + .../SelfModifying/SelectorNode.cs | 1 + .../CPU/CfgCpu/ParsedInstruction/Signature.cs | 8 +- .../CPU/CfgCpu/Parser/InstructionParser.cs | 27 +- .../Parser/SpecificParsers/CallParser.cs | 3 + .../Parser/SpecificParsers/JccParser.cs | 6 +- .../Parser/SpecificParsers/JcxzParser.cs | 2 + .../Parser/SpecificParsers/JmpParser.cs | 2 + .../Parser/SpecificParsers/LoopParser.cs | 2 + .../SimpleInstructionParser.cs | 6 + .../Emulator/Devices/ExternalInput/DualPic.cs | 17 + .../RoutineInstall/InterruptInstaller.cs | 21 +- .../ReverseEngineer/CSharpOverrideHelper.cs | 50 + .../CfgCodeGeneration/CSharpAstEmitter.cs | 40 +- .../CfgCodeGeneration/CfgGeneratorContext.cs | 8 +- .../CfgCodeGeneration/CpuFaultWrapper.cs | 2 +- .../CfgCodeGeneration/MethodEmitter.cs | 29 + .../ControlFlowGraph/CfgBlockGraphExporter.cs | 6 + .../CfgReload/CfgGraphReloader.cs | 39 +- .../CfgReload/CfgReloadDump.cs | 7 + .../CfgReload/CfgReloadExporter.cs | 25 +- .../CfgReload/CfgReloadNodeInfo.cs | 7 + .../EmulationStateDataWriter.cs | 7 +- src/Spice86.Shared/Utils/DictionaryUtils.cs | 19 + src/Spice86/Spice86DependencyInjection.cs | 37 +- .../CfgCpu/CfgGraphReloadTest.cs | 31 + .../Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs | 2 +- .../Spice86.Tests/CfgCpu/CfgNodeIndexTest.cs | 94 + .../CfgCpu/InstructionsFeederTest.cs | 2 +- .../NodeLinkerSpeculativeConflictTest.cs | 53 + .../CfgCpu/SpeculativeBlockIntegrityTest.cs | 150 + .../SpeculativeExplorerEdgeWiringTest.cs | 640 ++ .../SpeculativeLinkerAndPromotionTest.cs | 270 + .../SpeculativeReachabilityPrunerTest.cs | 215 + .../CfgCpu/SpeculativeReconcilerTest.cs | 133 + .../CfgCpu/SpeculativeSweepEntryPointTest.cs | 62 + .../CfgCpu/SpeculativeTestBase.cs | 128 + .../CfgCpu/SuccessorConsistency.cs | 118 + .../CfgCpu/SuccessorConsistencyTest.cs | 137 + .../SingleStepTestMinimalMachine.cs | 2 +- .../ExecutionContextManagerFactory.cs | 2 +- .../Spice86.Tests/GeneratedCodeMachineTest.cs | 96 +- .../GeneratedCodeMachineTestRunner.cs | 6 +- .../Spice86.Tests/GeneratedCodeRunOptions.cs | 1 + tests/Spice86.Tests/MachineTest.cs | 81 + .../cpuTests/asmsrc/speculative_branch.asm | 38 + .../asmsrc/speculative_call_entry.asm | 48 + .../cpuTests/asmsrc/speculative_closure.asm | 43 + .../asmsrc/speculative_convergence.asm | 44 + .../cpuTests/asmsrc/speculative_discard.asm | 47 + .../asmsrc/speculative_invalid_opcode.asm | 41 + .../asmsrc/speculative_mixed_block.asm | 45 + .../asmsrc/speculative_seeded_timer_sweep.asm | 80 + .../cpuTests/asmsrc/speculative_smc_guard.asm | 57 + .../Resources/cpuTests/asmsrc/strings.asm | 5 + .../asmsrc/test386.asm/src/test386.asm | 8 +- .../cpuTests/res/DumpedCfgBlocks/bcdcnv.json | 90 +- .../cpuTests/res/DumpedCfgBlocks/bitwise.json | 6 +- .../cpuTests/res/DumpedCfgBlocks/control.json | 30 +- .../res/DumpedCfgBlocks/datatrnf.json | 6 +- .../cpuTests/res/DumpedCfgBlocks/div.json | 238 +- .../res/DumpedCfgBlocks/divfaultloop.json | 42 +- .../res/DumpedCfgBlocks/externalint.json | 24 +- .../res/DumpedCfgBlocks/interrupt.json | 76 +- .../cpuTests/res/DumpedCfgBlocks/jmpmov.json | 24 +- .../cpuTests/res/DumpedCfgBlocks/jump1.json | 1135 ++- .../cpuTests/res/DumpedCfgBlocks/jump2.json | 244 +- .../linearsamesegmenteddifferent.json | 24 +- .../res/DumpedCfgBlocks/lockprefix.json | 42 +- .../DumpedCfgBlocks/ownerlesscallcycle.json | 32 +- .../partition_cross_function_loop.json | 50 +- .../partition_indirect_call_jump.json | 20 +- .../partition_jump_into_function_middle.json | 34 +- .../partition_mixed_activation_cycle.json | 34 +- ...artition_multi_entry_dominated_shared.json | 82 +- ...tition_multi_entry_irreducible_shared.json | 36 +- .../partition_shared_tail.json | 24 +- .../cpuTests/res/DumpedCfgBlocks/rep.json | 378 +- .../cpuTests/res/DumpedCfgBlocks/segpr.json | 20 +- .../res/DumpedCfgBlocks/selfmodifycall.json | 40 +- .../selfmodifyinstructions.json | 62 +- .../res/DumpedCfgBlocks/selfmodifyje.json | 14 +- .../DumpedCfgBlocks/selfmodifyterminator.json | 62 +- .../res/DumpedCfgBlocks/selfmodifyvalue.json | 8 +- .../DumpedCfgBlocks/speculative_branch.json | 88 + .../speculative_call_entry.json | 100 + .../DumpedCfgBlocks/speculative_closure.json | 122 + .../speculative_convergence.json | 89 + .../DumpedCfgBlocks/speculative_discard.json | 88 + .../speculative_invalid_opcode.json | 70 + .../speculative_mixed_block.json | 87 + .../speculative_smc_guard.json | 89 + .../cpuTests/res/DumpedCfgBlocks/sticli.json | 12 +- .../cpuTests/res/DumpedCfgBlocks/strings.json | 92 +- .../cpuTests/res/DumpedCfgBlocks/test386.json | 7261 ++++++++++------- .../res/DumpedCfgPartitions/bcdcnv.json | 28 +- .../res/DumpedCfgPartitions/bitwise.json | 2 +- .../res/DumpedCfgPartitions/control.json | 10 +- .../res/DumpedCfgPartitions/datatrnf.json | 2 +- .../cpuTests/res/DumpedCfgPartitions/div.json | 176 +- .../res/DumpedCfgPartitions/divfaultloop.json | 22 +- .../res/DumpedCfgPartitions/externalint.json | 10 +- .../res/DumpedCfgPartitions/interrupt.json | 66 +- .../res/DumpedCfgPartitions/jmpmov.json | 8 +- .../res/DumpedCfgPartitions/jump1.json | 120 +- .../res/DumpedCfgPartitions/jump2.json | 99 +- .../linearsamesegmenteddifferent.json | 18 +- .../res/DumpedCfgPartitions/lockprefix.json | 48 +- .../ownerlesscallcycle.json | 24 +- .../partition_cross_function_loop.json | 38 +- .../partition_indirect_call_jump.json | 16 +- .../partition_jump_into_function_middle.json | 34 +- .../partition_mixed_activation_cycle.json | 28 +- ...artition_multi_entry_dominated_shared.json | 74 +- ...tition_multi_entry_irreducible_shared.json | 38 +- .../partition_shared_tail.json | 22 +- .../cpuTests/res/DumpedCfgPartitions/rep.json | 94 +- .../res/DumpedCfgPartitions/segpr.json | 24 +- .../DumpedCfgPartitions/selfmodifycall.json | 20 +- .../selfmodifyinstructions.json | 11 +- .../res/DumpedCfgPartitions/selfmodifyje.json | 4 +- .../selfmodifyterminator.json | 23 +- .../DumpedCfgPartitions/selfmodifyvalue.json | 2 +- .../speculative_branch.json | 24 + .../speculative_call_entry.json | 49 + .../speculative_closure.json | 26 + .../speculative_convergence.json | 24 + .../speculative_discard.json | 24 + .../speculative_invalid_opcode.json | 23 + .../speculative_mixed_block.json | 24 + .../speculative_smc_guard.json | 24 + .../res/DumpedCfgPartitions/sticli.json | 4 +- .../res/DumpedCfgPartitions/strings.json | 55 +- .../res/DumpedCfgPartitions/test386.json | 2955 ++++++- .../cpuTests/res/DumpedListing/jump1.txt | 18 + .../cpuTests/res/DumpedListing/jump2.txt | 3 + .../cpuTests/res/DumpedListing/rep.txt | 6 + .../DumpedListing/selfmodifyterminator.txt | 3 + .../res/DumpedListing/speculative_branch.txt | 11 + .../DumpedListing/speculative_call_entry.txt | 12 + .../res/DumpedListing/speculative_closure.txt | 15 + .../DumpedListing/speculative_convergence.txt | 12 + .../res/DumpedListing/speculative_discard.txt | 11 + .../speculative_invalid_opcode.txt | 9 + .../DumpedListing/speculative_mixed_block.txt | 10 + .../DumpedListing/speculative_smc_guard.txt | 12 + .../cpuTests/res/DumpedListing/strings.txt | 1 + .../cpuTests/res/DumpedListing/test386.txt | 37 + .../Resources/cpuTests/speculative_branch.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_call_entry.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_closure.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_convergence.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_discard.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_invalid_opcode.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_mixed_block.bin | Bin 0 -> 65536 bytes .../speculative_seeded_timer_sweep.bin | Bin 0 -> 65536 bytes .../cpuTests/speculative_smc_guard.bin | Bin 0 -> 65536 bytes .../Resources/cpuTests/strings.bin | Bin 65536 -> 65536 bytes .../Resources/cpuTests/test386.bin | Bin 65536 -> 65536 bytes tests/Spice86.Tests/SpeculativeCfgTest.cs | 875 ++ tests/Spice86.Tests/Spice86Creator.cs | 4 +- .../UI/CfgCpu/CfgCpuViewModelTest.cs | 2 +- 182 files changed, 15085 insertions(+), 5373 deletions(-) create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeIndex.cs create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeExplorer.cs create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativePromoter.cs create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReachabilityPruner.cs create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/SpeculativeReconciler.cs create mode 100644 tests/Spice86.Tests/CfgCpu/CfgNodeIndexTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/NodeLinkerSpeculativeConflictTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeBlockIntegrityTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeExplorerEdgeWiringTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeLinkerAndPromotionTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeReachabilityPrunerTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeReconcilerTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeSweepEntryPointTest.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SpeculativeTestBase.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SuccessorConsistency.cs create mode 100644 tests/Spice86.Tests/CfgCpu/SuccessorConsistencyTest.cs create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_branch.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_call_entry.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_closure.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_convergence.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_discard.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_invalid_opcode.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_mixed_block.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_seeded_timer_sweep.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/asmsrc/speculative_smc_guard.asm create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_branch.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_call_entry.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_closure.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_convergence.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_discard.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_invalid_opcode.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_mixed_block.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgBlocks/speculative_smc_guard.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_branch.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_call_entry.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_closure.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_convergence.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_discard.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_invalid_opcode.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_mixed_block.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedCfgPartitions/speculative_smc_guard.json create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_branch.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_call_entry.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_closure.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_convergence.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_discard.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_invalid_opcode.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_mixed_block.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/res/DumpedListing/speculative_smc_guard.txt create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_branch.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_call_entry.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_closure.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_convergence.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_discard.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_invalid_opcode.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_mixed_block.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_seeded_timer_sweep.bin create mode 100644 tests/Spice86.Tests/Resources/cpuTests/speculative_smc_guard.bin create mode 100644 tests/Spice86.Tests/SpeculativeCfgTest.cs 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 0000000000000000000000000000000000000000..2de1f0caaaa4a2b493d82622a43fcc7e2c75ae1d GIT binary patch literal 65536 zcmeIup$&jA6h+ZrQi@|+Hee7mQ=qy96Evy9LxN)ka-O;RomwKNEynd&rdm=y+ne5^ zmLcy~7y$wV2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 n2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF{8FIJpIi3>_stCH literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..72ee7215e842efaa686b1c7b61b9f171f98ad51a GIT binary patch literal 65536 zcmeIuu?>ST6hOi6U`&C;P}wOPWDt~+iV={IyajzExAdgW3h3Sj?`nyh=IGzsYp5mV zqmAD_J}I|ueeSh9@^Xd<5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U xAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVA=D0&V(Rx_;Rv4tf9p literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..985a69ffafc67889ff314d6cbc8bf4fc4178fc3f GIT binary patch literal 65536 zcmeIuyA6X-5Cp&j9ymjyEDE9n7eX6|#1UOk!AnU<_&B5mG#9LH!=0sxRE$x7f5T^K zinQi#s^b5}aYsMw`EF&Jw{Qdq5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn+oA+&;#f7pyA} Ak^lez literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..17f88fd09f2e79594e693f88ee54374b69d6f5df GIT binary patch literal 65536 zcmeIup%H*E6hy%QAvn6F0}4UY0tWRJoy6k942~9H%CoT z`g}zha=VieAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U pAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!CvC1nT^G)E?2I4tD?m literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2de1f0caaaa4a2b493d82622a43fcc7e2c75ae1d GIT binary patch literal 65536 zcmeIup$&jA6h+ZrQi@|+Hee7mQ=qy96Evy9LxN)ka-O;RomwKNEynd&rdm=y+ne5^ zmLcy~7y$wV2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 n2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF{8FIJpIi3>_stCH literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..56a14e318d8803a7e67875a4746184b8bb762265 GIT binary patch literal 65536 zcmeIuF%5t)6hOh2e9DGf93TWKOm%^RCY{&|D;CH-2k&Z0k&iU*=eE?6<7zS9YMG+n zNdyQGAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs j0RjXF5FkK+009C72oNAZfB*pk1PBly@K=Ghe$K`N`4n0WskY30Rf&=_k5J_dLr4xLx)fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72#lKgT%UGf zQ5-t6Dy+&Ztn*Me{lUJhw%zc$ZsIsi<2+4b-X^5sv#84Ub$Nf>VwSSv-4>4|2@oJa zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 t2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+z&{CGde!gT&lkdQBcA{O literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3a98fda9eae3a8db74a19550e1b6eef6dbbd9575 GIT binary patch literal 65536 zcmeIuu?>Sj5Jb^E>%5Mj02e`bbU-1FT?Nr~K?NrfksCRr1@tES;k=en%O{@8I=@<$ zy7ggd`HuY`(WB*~evdE$1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 x2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oSiLKp!uMeka&f4g&xH literal 0 HcmV?d00001 diff --git a/tests/Spice86.Tests/Resources/cpuTests/strings.bin b/tests/Spice86.Tests/Resources/cpuTests/strings.bin index 3ea6e2b7bbb8b6e577cd798212378de7fdabb95f..60239d666e08d70cc04208458c19cf020d9a8175 100644 GIT binary patch delta 19 bcmZo@U}GNz>GikGmqg`WO@9Ah5CCfZ3bX(K delta 22 ecmZo@U}GNz`OE9Cn=gvWy_)>tzW@Mw2@9tH 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,