From 1ed817879230c0e13a9696bd8e91e7c6ade38ae4 Mon Sep 17 00:00:00 2001 From: Kevin Ferrare Date: Wed, 18 Sep 2024 21:34:22 +0200 Subject: [PATCH] Fix CfgCpu messing up graph on external interruptions. Make interrupt vector table a memory based array. --- .../Emulator/CPU/CfgCpu/CfgCpu.cs | 16 ++++-- .../CPU/CfgCpu/ExecutionContextManager.cs | 54 ++++++++++++------- .../CPU/CfgCpu/Feeder/CfgNodeFeeder.cs | 19 ++++--- .../CPU/CfgCpu/Feeder/CurrentInstructions.cs | 13 +++-- .../CPU/CfgCpu/Feeder/DiscriminatorReducer.cs | 10 ++-- .../CPU/CfgCpu/Feeder/IInstructionReplacer.cs | 6 +-- .../CPU/CfgCpu/Feeder/InstructionReplacer.cs | 10 ++++ .../Feeder/InstructionReplacerRegistry.cs | 17 ++++++ .../CPU/CfgCpu/Feeder/InstructionsFeeder.cs | 14 ++--- .../CPU/CfgCpu/Feeder/PreviousInstructions.cs | 7 +-- .../CPU/CfgCpu/Linker/ExecutionContext.cs | 8 +-- .../Emulator/CPU/CfgCpu/Linker/NodeLinker.cs | 26 +++++---- .../Emulator/CPU/InterruptVectorTable.cs | 28 ++-------- src/Spice86.Core/Emulator/ProgramExecutor.cs | 4 +- .../DataStructure/Array/MemoryBasedArray.cs | 2 +- src/Spice86.Core/Emulator/VM/EmulationLoop.cs | 4 ++ src/Spice86/Spice86DependencyInjection.cs | 2 +- .../Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs | 14 ++--- .../CfgCpu/InstructionsFeederTest.cs | 11 ++-- 19 files changed, 156 insertions(+), 109 deletions(-) create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs create mode 100644 src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs index 91c1eb745c..0528c1b2f8 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/CfgCpu.cs @@ -18,17 +18,18 @@ public class CfgCpu : IInstructionExecutor { private readonly InstructionExecutionHelper _instructionExecutionHelper; private readonly State _state; private readonly DualPic _dualPic; - private readonly ExecutionContextManager _executionContextManager; - private readonly CfgNodeFeeder _cfgNodeFeeder; + private readonly ExecutionContextManager _executionContextManager; + private readonly InstructionReplacerRegistry _replacerRegistry = new(); public CfgCpu(IMemory memory, State state, IOPortDispatcher ioPortDispatcher, CallbackHandler callbackHandler, DualPic dualPic, EmulatorBreakpointsManager emulatorBreakpointsManager, ILoggerService loggerService) { _instructionExecutionHelper = new(state, memory, ioPortDispatcher, callbackHandler, loggerService); _state = state; _dualPic = dualPic; - _executionContextManager = new(emulatorBreakpointsManager); - _cfgNodeFeeder = new(memory, state, emulatorBreakpointsManager); + + _cfgNodeFeeder = new(memory, state, emulatorBreakpointsManager, _replacerRegistry); + _executionContextManager = new(emulatorBreakpointsManager, _cfgNodeFeeder, _replacerRegistry); } public ExecutionContextManager ExecutionContextManager => _executionContextManager; @@ -56,6 +57,13 @@ public void ExecuteNext() { CurrentExecutionContext.NodeToExecuteNextAccordingToGraph = nextToExecute; HandleExternalInterrupt(); } + + /// + /// Signal to the cfg cpu that we are at the entry point of the program + /// + public void SignalEntry() { + _executionContextManager.SignalNewExecutionContext(_state.IpSegmentedAddress, null); + } private void HandleExternalInterrupt() { if (!_state.InterruptFlag) { diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs index 23586fdf19..1c25de0518 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/ExecutionContextManager.cs @@ -1,43 +1,61 @@ namespace 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.Linker; +using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction; using Spice86.Core.Emulator.VM; using Spice86.Core.Emulator.VM.Breakpoint; using Spice86.Shared.Emulator.Memory; -public class ExecutionContextManager { +public class ExecutionContextManager : InstructionReplacer { private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager; - private readonly Dictionary _executionContextEntryPoints = new(); - - public ExecutionContextManager(EmulatorBreakpointsManager emulatorBreakpointsManager) { + private readonly Dictionary> _executionContextEntryPoints = new(); + private readonly CfgNodeFeeder _cfgNodeFeeder; + private int _currentDepth; + + public ExecutionContextManager(EmulatorBreakpointsManager emulatorBreakpointsManager, CfgNodeFeeder cfgNodeFeeder, + InstructionReplacerRegistry replacerRegistry) : base(replacerRegistry) { _emulatorBreakpointsManager = emulatorBreakpointsManager; + _cfgNodeFeeder = cfgNodeFeeder; // Initial context at init - CurrentExecutionContext = new(); - InitialExecutionContext = CurrentExecutionContext; + CurrentExecutionContext = new(_currentDepth); } - - public ExecutionContext InitialExecutionContext { get; } public ExecutionContext CurrentExecutionContext { get; private set; } public void SignalNewExecutionContext(SegmentedAddress entryAddress, SegmentedAddress? expectedReturnAddress) { - uint physicalEntryAddress = entryAddress.ToPhysical(); - if (!_executionContextEntryPoints.TryGetValue(physicalEntryAddress, out ExecutionContext? executionContext)) { - executionContext = new ExecutionContext(); - _executionContextEntryPoints.Add(entryAddress.ToPhysical(), executionContext); - } - // Reset the execution context so that nodes it last executed are not linked to the new ones that will come - executionContext.LastExecuted = null; - executionContext.NodeToExecuteNextAccordingToGraph = null; + // Save current execution context ExecutionContext previousExecutionContext = CurrentExecutionContext; - CurrentExecutionContext = executionContext; + // Create a new one at a higher depth + _currentDepth++; + CurrentExecutionContext = new(_currentDepth); if (expectedReturnAddress != null) { // breakpoint that deletes itself on reach. Should be triggered when the return address is reached and before it starts execution. _emulatorBreakpointsManager.ToggleBreakPoint(new AddressBreakPoint(BreakPointType.EXECUTION, expectedReturnAddress.Value.ToPhysical(), (_) => { - // Restore previous execution context + // Restore previous execution context and depth CurrentExecutionContext = previousExecutionContext; + _currentDepth--; }, true), true); } + + RegisterCurrentInstructionAsEntryPoint(entryAddress); + } + + private void RegisterCurrentInstructionAsEntryPoint(SegmentedAddress entryAddress) { + // Register a new entry point + ICfgNode toExecute = _cfgNodeFeeder.GetLinkedCfgNodeToExecute(CurrentExecutionContext); + if (!_executionContextEntryPoints.TryGetValue(entryAddress, out ISet? nodes)) { + nodes = new HashSet(); + _executionContextEntryPoints.Add(entryAddress, nodes); + } + nodes.Add(toExecute); } + public override void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { + if (_executionContextEntryPoints.TryGetValue(instruction.Address, out ISet? entriesAtAddress) + && entriesAtAddress.Remove(old)) { + entriesAtAddress.Add(instruction); + } + } } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs index ffe12518c4..e000c0acf6 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CfgNodeFeeder.cs @@ -1,14 +1,13 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph; +using Spice86.Core.Emulator.CPU.CfgCpu.Exceptions; using Spice86.Core.Emulator.CPU.CfgCpu.Linker; using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction; using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying; using Spice86.Core.Emulator.Memory; using Spice86.Core.Emulator.VM; -using System.Linq; - /// /// Handles coherency between the memory and the graph of instructions executed by the CPU. /// Next node to execute is normally the next node from the graph but several checks are done to make sure it is really it: @@ -20,14 +19,15 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; public class CfgNodeFeeder { private readonly State _state; private readonly InstructionsFeeder _instructionsFeeder; - private readonly NodeLinker _nodeLinker = new(); + private readonly NodeLinker _nodeLinker; private readonly DiscriminatorReducer _discriminatorReducer; - public CfgNodeFeeder(IMemory memory, State state, EmulatorBreakpointsManager emulatorBreakpointsManager) { + public CfgNodeFeeder(IMemory memory, State state, EmulatorBreakpointsManager emulatorBreakpointsManager, + InstructionReplacerRegistry replacerRegistry) { _state = state; - _instructionsFeeder = new(emulatorBreakpointsManager, memory, state); - _discriminatorReducer = new(new List>() - { _nodeLinker, _instructionsFeeder }); + _instructionsFeeder = new(emulatorBreakpointsManager, memory, state, replacerRegistry); + _nodeLinker = new(replacerRegistry); + _discriminatorReducer = new(replacerRegistry); } private CfgInstruction CurrentNodeFromInstructionFeeder => @@ -61,6 +61,11 @@ private ICfgNode DetermineToExecute(ICfgNode? currentFromGraph) { return currentFromGraph; } + if (fromMemory.Address != currentFromGraph.Address) { + // should never happen + throw new UnhandledCfgDiscrepancyException("Nodes from memory and from graph don't have the same address. This should never happen."); + } + // Graph and memory are not aligned ... Need to inject Node with discriminator // If previous was Discriminated and current was not in its successors we would not be there because // currentFromGraph would have been null and the linker would then link it to DiscriminatedNode diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs index dc967e1eff..63fc5a9357 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs @@ -10,7 +10,7 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; /// Cache of current instructions in memory. /// Cache coherency is managed by breakpoints, as soon as an instruction is written in memory it is evicted. /// -public class CurrentInstructions : IInstructionReplacer { +public class CurrentInstructions : InstructionReplacer { private readonly IMemory _memory; private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager; @@ -18,17 +18,16 @@ public class CurrentInstructions : IInstructionReplacer { /// Instruction currently known to be in memory at a given address. /// Memory write breakpoints invalidate this cache when CPU writes there. /// - private readonly Dictionary _currentInstructionAtAddress = - new Dictionary(); + private readonly Dictionary _currentInstructionAtAddress = new(); /// /// Breakpoints that have been installed to monitor instruction at a given address. So that we can reset them when we want. /// - private readonly Dictionary> _breakpointsForInstruction = - new Dictionary>(); + private readonly Dictionary> _breakpointsForInstruction = new(); - public CurrentInstructions(IMemory memory, EmulatorBreakpointsManager emulatorBreakpointsManager) { + public CurrentInstructions(IMemory memory, EmulatorBreakpointsManager emulatorBreakpointsManager, + InstructionReplacerRegistry replacerRegistry) : base(replacerRegistry) { _memory = memory; _emulatorBreakpointsManager = emulatorBreakpointsManager; } @@ -38,7 +37,7 @@ public CurrentInstructions(IMemory memory, EmulatorBreakpointsManager emulatorBr return res; } - public void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { + public override void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { SegmentedAddress instructionAddress = instruction.Address; if (_currentInstructionAtAddress.ContainsKey(instructionAddress)) { ClearCurrentInstruction(old); diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/DiscriminatorReducer.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/DiscriminatorReducer.cs index 309c5a69f9..aef374bd57 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/DiscriminatorReducer.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/DiscriminatorReducer.cs @@ -5,10 +5,10 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; using System.Linq; public class DiscriminatorReducer { - private readonly IList> _instructionReplacers; + private readonly InstructionReplacerRegistry _replacerRegistry; - public DiscriminatorReducer(IList> instructionReplacers) { - _instructionReplacers = instructionReplacers; + public DiscriminatorReducer(InstructionReplacerRegistry replacerRegistry) { + _replacerRegistry = replacerRegistry; } private static Dictionary> GroupByType(List instructions) { @@ -98,9 +98,7 @@ private void ReplaceWithReference(CfgInstruction reference, IList where T : ICfgNode { - void ReplaceInstruction(T old, T instruction); +public interface IInstructionReplacer { + void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction); } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs new file mode 100644 index 0000000000..5cbf206bf1 --- /dev/null +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacer.cs @@ -0,0 +1,10 @@ +namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; + +using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction; + +public abstract class InstructionReplacer : IInstructionReplacer { + protected InstructionReplacer(InstructionReplacerRegistry replacerRegistry) { + replacerRegistry.Register(this); + } + public abstract void ReplaceInstruction(CfgInstruction old, 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 new file mode 100644 index 0000000000..c782360818 --- /dev/null +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionReplacerRegistry.cs @@ -0,0 +1,17 @@ +namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; + +using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction; + +public class InstructionReplacerRegistry : IInstructionReplacer { + private List _replacers = new(); + + public void Register(InstructionReplacer replacer) { + _replacers.Add(replacer); + } + + public void ReplaceInstruction(CfgInstruction old, CfgInstruction newInstruction) { + foreach (InstructionReplacer instructionReplacer in _replacers) { + instructionReplacer.ReplaceInstruction(old, newInstruction); + } + } +} \ 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 98f43ac761..76d2175c6d 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/InstructionsFeeder.cs @@ -14,15 +14,16 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; /// If an instruction is modified and then put back in its original version, it is the original instance of the parsed instruction that will be returned for the address /// Instructions can be replaced in the cache (see method ReplaceInstruction) /// -public class InstructionsFeeder : IInstructionReplacer { +public class InstructionsFeeder { private readonly InstructionParser _instructionParser; private readonly CurrentInstructions _currentInstructions; private readonly PreviousInstructions _previousInstructions; - public InstructionsFeeder(EmulatorBreakpointsManager emulatorBreakpointsManager, IMemory memory, State cpuState) { - _currentInstructions = new(memory, emulatorBreakpointsManager); + public InstructionsFeeder(EmulatorBreakpointsManager emulatorBreakpointsManager, IMemory memory, State cpuState, + InstructionReplacerRegistry replacerRegistry) { + _currentInstructions = new(memory, emulatorBreakpointsManager, replacerRegistry); _instructionParser = new(memory, cpuState); - _previousInstructions = new(memory); + _previousInstructions = new(memory, replacerRegistry); } public CfgInstruction GetInstructionFromMemory(SegmentedAddress address) { @@ -48,9 +49,4 @@ public CfgInstruction GetInstructionFromMemory(SegmentedAddress address) { _previousInstructions.AddInstructionInPrevious(parsed); return parsed; } - - public void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { - _currentInstructions.ReplaceInstruction(old, instruction); - _previousInstructions.ReplaceInstruction(old, instruction); - } } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs index 5c9b5f7f89..250236e517 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/PreviousInstructions.cs @@ -7,7 +7,7 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder; /// /// Cache of previous instructions that existed in a memory address at a time. /// -public class PreviousInstructions : IInstructionReplacer { +public class PreviousInstructions : InstructionReplacer { private readonly MemoryInstructionMatcher _memoryInstructionMatcher; /// @@ -15,7 +15,8 @@ public class PreviousInstructions : IInstructionReplacer { /// private readonly Dictionary> _previousInstructionsAtAddress = new(); - public PreviousInstructions(IMemory memory) { + public PreviousInstructions(IMemory memory, InstructionReplacerRegistry replacerRegistry) : base( + replacerRegistry) { _memoryInstructionMatcher = new MemoryInstructionMatcher(memory); } @@ -28,7 +29,7 @@ public PreviousInstructions(IMemory memory) { return null; } - public void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { + public override void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { SegmentedAddress instructionAddress = instruction.Address; if (_previousInstructionsAtAddress.TryGetValue(instructionAddress, diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/ExecutionContext.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/ExecutionContext.cs index 367d566e6b..8680a373de 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/ExecutionContext.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/ExecutionContext.cs @@ -3,13 +3,13 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Linker; using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph; public class ExecutionContext { - private static int _nextIndex = 0; - public ExecutionContext() { - Index = _nextIndex++; + public ExecutionContext(int depth) { + Depth = depth; } - public int Index { get; } + public int Depth { get; } + /// /// Last node actually executed by the CPU /// diff --git a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs index 6ebf5535f2..c344629091 100644 --- a/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs +++ b/src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs @@ -7,9 +7,12 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Linker; using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying; using Spice86.Shared.Emulator.Memory; -public class NodeLinker : IInstructionReplacer { +public class NodeLinker : InstructionReplacer { private readonly NodeToString _nodeToString = new(); + public NodeLinker(InstructionReplacerRegistry replacerRegistry) : base(replacerRegistry) { + } + /// /// Ensure current and next are linked together. /// @@ -27,7 +30,7 @@ private void LinkCfgInstruction(CfgInstruction current, ICfgNode next) { Dictionary successors = current.SuccessorsPerAddress; if (!successors.TryGetValue(next.Address, out ICfgNode? shouldBeNext)) { // New link found - AttachCurrentToNext(current, next); + AttachToNext(current, next); return; } @@ -54,7 +57,7 @@ private void LinkDiscriminatedNode(DiscriminatedNode current, ICfgNode next) { Dictionary successors = current.SuccessorsPerDiscriminator; if (!successors.TryGetValue(nextCfgInstruction.Discriminator, out CfgInstruction? shouldBeNextOrNull)) { // New link discovered, create it - AttachCurrentToNext(current, next); + AttachToNext(current, next); return; } if (!ReferenceEquals(shouldBeNextOrNull, next)) { @@ -65,20 +68,21 @@ private void LinkDiscriminatedNode(DiscriminatedNode current, ICfgNode next) { } } - public void AttachCurrentToNext(ICfgNode current, ICfgNode next) { - LinkCurrentToNext(current, next); + public void AttachToNext(ICfgNode current, ICfgNode next) { + LinkToNext(current, next); current.UpdateSuccessorCache(); } - private void LinkCurrentToNext(ICfgNode current, ICfgNode next) { + private void LinkToNext(ICfgNode current, ICfgNode next) { current.Successors.Add(next); next.Predecessors.Add(current); } - public void ReplaceInstruction(ICfgNode old, ICfgNode instruction) { + public override void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) { + // Unlinks old from the graph and links instruction instead, effectively replacing it InsertIntermediatePredecessor(old, instruction); foreach (ICfgNode successor in old.Successors) { - LinkCurrentToNext(instruction, successor); + LinkToNext(instruction, successor); successor.Predecessors.Remove(old); old.Successors.Remove(successor); } @@ -89,12 +93,14 @@ public void ReplaceInstruction(ICfgNode old, ICfgNode instruction) { public void InsertIntermediatePredecessor(ICfgNode current, ICfgNode newPredecessor) { foreach (ICfgNode predecessor in current.Predecessors) { - LinkCurrentToNext(predecessor, newPredecessor); + // Replace current with new in the predecessors successors (: + LinkToNext(predecessor, newPredecessor); predecessor.Successors.Remove(current); predecessor.UpdateSuccessorCache(); } + // Make new the only predecessor of current current.Predecessors.Clear(); - LinkCurrentToNext(newPredecessor, current); + LinkToNext(newPredecessor, current); newPredecessor.UpdateSuccessorCache(); } } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/CPU/InterruptVectorTable.cs b/src/Spice86.Core/Emulator/CPU/InterruptVectorTable.cs index ace154f87f..b7de76b986 100644 --- a/src/Spice86.Core/Emulator/CPU/InterruptVectorTable.cs +++ b/src/Spice86.Core/Emulator/CPU/InterruptVectorTable.cs @@ -1,36 +1,16 @@ namespace Spice86.Core.Emulator.CPU; -using Spice86.Core.Emulator.Memory.Indexable; -using Spice86.Shared.Emulator.Memory; +using Spice86.Core.Emulator.Memory.ReaderWriter; +using Spice86.Core.Emulator.ReverseEngineer.DataStructure.Array; /// /// Wraps reads and writes to the Interrupt Vector Table (IVT) /// -public class InterruptVectorTable { - private readonly IIndexable _memory; - +public class InterruptVectorTable : SegmentedAddressArray { /// /// Initializes a new instance. /// /// The memory bus. - public InterruptVectorTable(IIndexable memory) { - _memory = memory; - } - - /// - /// Reads or writes to the interrupt vector table - /// - /// Vector to access in the interrupt table - public SegmentedAddress this[byte vectorNumber] { - get { - // Table starts at memory address 0. - uint offsetAddress = (uint)(4 * vectorNumber); - return _memory.SegmentedAddress[offsetAddress]; - } - set { - // install the vector in the vector table. Table starts at memory address 0. - uint offsetAddress = (uint)(4 * vectorNumber); - _memory.SegmentedAddress[offsetAddress] = new(value.Segment, value.Offset); - } + public InterruptVectorTable(IByteReaderWriter memory) : base(memory, 0, 0x100) { } } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/ProgramExecutor.cs b/src/Spice86.Core/Emulator/ProgramExecutor.cs index 80646140dc..425ce5016b 100644 --- a/src/Spice86.Core/Emulator/ProgramExecutor.cs +++ b/src/Spice86.Core/Emulator/ProgramExecutor.cs @@ -6,6 +6,7 @@ using Spice86.Core.CLI; using Spice86.Core.Emulator.CPU; +using Spice86.Core.Emulator.CPU.CfgCpu; using Spice86.Core.Emulator.Devices.Timer; using Spice86.Core.Emulator.Function; using Spice86.Core.Emulator.Gdb; @@ -43,6 +44,7 @@ public sealed class ProgramExecutor : IDisposable { /// The class that is responsible for serializing the state of the emulator to a directory. /// The memory bus. /// The emulated x86 CPU. + /// The emulated x86 CPU, CFG version. /// The CPU registers and flags. /// The programmable interval timer. /// The DOS kernel. @@ -54,7 +56,7 @@ public sealed class ProgramExecutor : IDisposable { /// The logging service to use. public ProgramExecutor(Configuration configuration, EmulatorBreakpointsManager emulatorBreakpointsManager, EmulatorStateSerializer emulatorStateSerializer, - IMemory memory, Cpu cpu, State state, Timer timer, Dos dos, + IMemory memory, Cpu cpu, CfgCpu cfgCpu, State state, Timer timer, Dos dos, CallbackHandler callbackHandler, FunctionHandler functionHandler, ExecutionFlowRecorder executionFlowRecorder, IPauseHandler pauseHandler, IScreenPresenter? screenPresenter, ILoggerService loggerService) { _configuration = configuration; diff --git a/src/Spice86.Core/Emulator/ReverseEngineer/DataStructure/Array/MemoryBasedArray.cs b/src/Spice86.Core/Emulator/ReverseEngineer/DataStructure/Array/MemoryBasedArray.cs index ccea2903c3..88a8c3f4a0 100644 --- a/src/Spice86.Core/Emulator/ReverseEngineer/DataStructure/Array/MemoryBasedArray.cs +++ b/src/Spice86.Core/Emulator/ReverseEngineer/DataStructure/Array/MemoryBasedArray.cs @@ -8,7 +8,7 @@ /// An abstract generic class that represents a memory-based array data structure with a base address. /// /// The type of elements in the array. -public abstract class MemoryBasedArray : MemoryBasedDataStructure, IEnumerable, IList { +public abstract class MemoryBasedArray : MemoryBasedDataStructure, IList { private readonly int _length; /// diff --git a/src/Spice86.Core/Emulator/VM/EmulationLoop.cs b/src/Spice86.Core/Emulator/VM/EmulationLoop.cs index 143277a32a..af113522b1 100644 --- a/src/Spice86.Core/Emulator/VM/EmulationLoop.cs +++ b/src/Spice86.Core/Emulator/VM/EmulationLoop.cs @@ -5,6 +5,7 @@ namespace Spice86.Core.Emulator.VM; +using Spice86.Core.Emulator.CPU.CfgCpu; using Spice86.Core.Emulator.Gdb; using Spice86.Shared.Interfaces; @@ -87,6 +88,9 @@ private void StartRunLoop(FunctionHandler functionHandler) { private void RunLoop() { _stopwatch.Start(); + if (_cpu is CfgCpu cfgCpu) { + cfgCpu.SignalEntry(); + } while (_cpuState.IsRunning) { _emulatorBreakpointsManager.CheckBreakPoint(); _pauseHandler.WaitIfPaused(); diff --git a/src/Spice86/Spice86DependencyInjection.cs b/src/Spice86/Spice86DependencyInjection.cs index a367d82094..9113be7b3b 100644 --- a/src/Spice86/Spice86DependencyInjection.cs +++ b/src/Spice86/Spice86DependencyInjection.cs @@ -197,7 +197,7 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co reader.ReadGhidraSymbolsFromFileOrCreate(), functionHandler, functionHandlerInExternalInterrupt); ProgramExecutor programExecutor = new(configuration, emulatorBreakpointsManager, - emulatorStateSerializer, memory, cpu, state, + emulatorStateSerializer, memory, cpu, cfgCpu, state, timer, dos, callbackHandler, functionHandler, executionFlowRecorder, pauseHandler, mainWindowViewModel, loggerService); diff --git a/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs b/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs index 37055a65fa..17f74c4397 100644 --- a/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs +++ b/tests/Spice86.Tests/CfgCpu/CfgNodeFeederTest.cs @@ -39,7 +39,7 @@ private CfgNodeFeeder CreateCfgNodeFeeder() { _memory = new(memoryBreakpoints, new Ram(64), new A20Gate()); _state = new State(); EmulatorBreakpointsManager emulatorBreakpointsManager = new EmulatorBreakpointsManager(memoryBreakpoints, new PauseHandler(loggerService), _state); - return new(_memory, _state, emulatorBreakpointsManager); + return new(_memory, _state, emulatorBreakpointsManager, new()); } private void WriteMovReg16(SegmentedAddress address, byte opcode, ushort value) { @@ -78,7 +78,7 @@ private void WriteTwoMovAx() { public void ReadInstructionViaParser() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteMovAx(ZeroAddress, DefaultValue); // Act @@ -93,7 +93,7 @@ public void ReadInstructionViaParser() { public void LinkTwoInstructions() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteMovAx(ZeroAddress, DefaultValue); WriteMovBx(EndOfMov0Address, DefaultValue); ICfgNode movAx = SimulateExecution(cfgNodeFeeder, executionContext); @@ -112,7 +112,7 @@ public void LinkTwoInstructions() { public void MovAxChangedToMovBx() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteTwoMovAx(); ICfgNode movAx0 = SimulateExecution(cfgNodeFeeder, executionContext); // Parse second Mov AX and insert it in graph @@ -144,7 +144,7 @@ public void MovAxChangedToMovBx() { public void MovAxChangedValue() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteTwoMovAx(); SimulateExecution(cfgNodeFeeder, executionContext); // Just parse next and insert it in graph @@ -164,7 +164,7 @@ public void MovAxChangedValue() { public void MovAxChangedToMovBxThenMovCx() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteTwoMovAx(); SimulateExecution(cfgNodeFeeder, executionContext); ICfgNode movAx1 = cfgNodeFeeder.GetLinkedCfgNodeToExecute(executionContext); @@ -190,7 +190,7 @@ public void MovAxChangedToMovBxThenMovCx() { public void MovAxChangedToMovBxThenMovAxWithDifferentValue() { // Arrange CfgNodeFeeder cfgNodeFeeder = CreateCfgNodeFeeder(); - ExecutionContext executionContext = new ExecutionContext(); + ExecutionContext executionContext = new ExecutionContext(0); WriteTwoMovAx(); SimulateExecution(cfgNodeFeeder, executionContext); ICfgNode movAx1 = cfgNodeFeeder.GetLinkedCfgNodeToExecute(executionContext); diff --git a/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs b/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs index 03145c6493..54eb6bdfa2 100644 --- a/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs +++ b/tests/Spice86.Tests/CfgCpu/InstructionsFeederTest.cs @@ -25,13 +25,16 @@ public class InstructionsFeederTest { private static readonly SegmentedAddress SixteenAddressViaSegment = new(1, 0); private static readonly MemoryBreakpoints MemoryBreakpoints = new(); private readonly Memory _memory = new(MemoryBreakpoints, new Ram(64), new A20Gate()); + private InstructionReplacerRegistry _instructionReplacer = new(); private InstructionsFeeder CreateInstructionsFeeder() { _memory.Memset8(0, 0, 64); + _instructionReplacer = new(); ILoggerService loggerService = Substitute.For(); State state = new(); EmulatorBreakpointsManager emulatorBreakpointsManager = new EmulatorBreakpointsManager(MemoryBreakpoints, new PauseHandler(loggerService), state); - return new InstructionsFeeder(emulatorBreakpointsManager, _memory, state); + + return new InstructionsFeeder(emulatorBreakpointsManager, _memory, state, _instructionReplacer); } private void WriteJumpNear(SegmentedAddress address) { @@ -151,7 +154,7 @@ public void ReplaceInstruction() { // Act CfgInstruction newInstruction = CreateReplacementInstruction(); - instructionsFeeder.ReplaceInstruction(old, newInstruction); + _instructionReplacer.ReplaceInstruction(old, newInstruction); CfgInstruction instruction = instructionsFeeder.GetInstructionFromMemory(ZeroAddress); // Assert @@ -167,7 +170,7 @@ public void ReplaceInstructionAndEnsureSelfModifyingCodeIsDetected() { // Act CfgInstruction newInstruction = CreateReplacementInstruction(); - instructionsFeeder.ReplaceInstruction(old, newInstruction); + _instructionReplacer.ReplaceInstruction(old, newInstruction); WriteMovAx(ZeroAddress); CfgInstruction instruction = instructionsFeeder.GetInstructionFromMemory(ZeroAddress); @@ -184,7 +187,7 @@ public void ReplaceInstructionAndEnsureItStaysAfterSelfModifyingCode() { // Act CfgInstruction newInstruction = CreateReplacementInstruction(); - instructionsFeeder.ReplaceInstruction(old, newInstruction); + _instructionReplacer.ReplaceInstruction(old, newInstruction); WriteMovAx(ZeroAddress); instructionsFeeder.GetInstructionFromMemory(ZeroAddress); WriteJumpNear(ZeroAddress);