Skip to content

Commit

Permalink
Address of CfgNodes is a segmented address and not a physical address…
Browse files Browse the repository at this point in the history
… to take into account the differences in execution results when an instruction at the same physical address is executed with different segmented addresses
  • Loading branch information
kevinferrare committed Aug 8, 2023
1 parent a3e0a25 commit 47802dc
Show file tree
Hide file tree
Showing 24 changed files with 371 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;

using Spice86.Shared.Emulator.Memory;

public abstract class CfgNode : ICfgNode {
public CfgNode(uint physicalAddress) {
PhysicalAddress = physicalAddress;
public CfgNode(SegmentedAddress address) {
Address = address;
}

public ISet<ICfgNode> Predecessors { get; } = new HashSet<ICfgNode>();
public ISet<ICfgNode> Successors { get; } = new HashSet<ICfgNode>();
public uint PhysicalAddress { get; }
public SegmentedAddress Address { get; }

public abstract bool IsAssembly { get; }
public abstract void UpdateSuccessorCache();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;

using Spice86.Shared.Emulator.Memory;

public interface ICfgNode {
ISet<ICfgNode> Predecessors { get; }
ISet<ICfgNode> Successors { get; }

public uint PhysicalAddress { get; }
public SegmentedAddress Address { get; }
public bool IsAssembly { get; }

public void UpdateSuccessorCache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private ICfgNode CreateDiscriminatedNode(CfgInstruction instruction1, CfgInstruc
return reducedInstructions.First();
}

DiscriminatedNode res = new DiscriminatedNode(instruction1.PhysicalAddress);
DiscriminatedNode res = new DiscriminatedNode(instruction1.Address);
foreach (CfgInstruction reducedInstruction in reducedInstructions) {
// Make predecessors of instructions point to res instead of instruction1/2
_nodeLinker.ReplacePredecessors(reducedInstruction, res);
Expand Down
28 changes: 15 additions & 13 deletions src/Spice86.Core/Emulator/CPU/CfgCpu/Feeder/CurrentInstructions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.VM;
using Spice86.Core.Emulator.VM.Breakpoint;
using Spice86.Shared.Emulator.Memory;

/// <summary>
/// Cache of current instructions in memory.
Expand All @@ -17,28 +18,28 @@ public class CurrentInstructions : IInstructionReplacer<CfgInstruction> {
/// Instruction currently known to be in memory at a given address.
/// Memory write breakpoints invalidate this cache when CPU writes there.
/// </summary>
private readonly IDictionary<uint, CfgInstruction> _currentInstructionAtAddress =
new Dictionary<uint, CfgInstruction>();
private readonly Dictionary<SegmentedAddress, CfgInstruction> _currentInstructionAtAddress =
new Dictionary<SegmentedAddress, CfgInstruction>();


/// <summary>
/// Breakpoints that have been installed to monitor instruction at a given address. So that we can reset them when we want.
/// </summary>
private readonly IDictionary<uint, IList<AddressBreakPoint>> _breakpointsForInstruction =
new Dictionary<uint, IList<AddressBreakPoint>>();
private readonly Dictionary<SegmentedAddress, List<AddressBreakPoint>> _breakpointsForInstruction =
new Dictionary<SegmentedAddress, List<AddressBreakPoint>>();

public CurrentInstructions(IMemory memory, MachineBreakpoints machineBreakpoints) {
_memory = memory;
_machineBreakpoints = machineBreakpoints;
}

public CfgInstruction? GetAtAddress(uint physicalAddress) {
_currentInstructionAtAddress.TryGetValue(physicalAddress, out CfgInstruction? res);
public CfgInstruction? GetAtAddress(SegmentedAddress address) {
_currentInstructionAtAddress.TryGetValue(address, out CfgInstruction? res);
return res;
}

public void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;
if (_currentInstructionAtAddress.ContainsKey(instructionAddress)) {
ClearCurrentInstruction(old);
SetAsCurrent(instruction);
Expand All @@ -54,11 +55,12 @@ public void SetAsCurrent(CfgInstruction instruction) {
}

private void CreateBreakpointsForInstruction(CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;
List<AddressBreakPoint> breakpoints = new();
_breakpointsForInstruction.Add(instructionAddress, breakpoints);
for (uint byteAddress = instructionAddress;
byteAddress < instructionAddress + instruction.Length;
uint instructionPhysicalAddress = instructionAddress.ToPhysical();
for (uint byteAddress = instructionPhysicalAddress;
byteAddress < instructionPhysicalAddress + instruction.Length;
byteAddress++) {
// When reached the breakpoint will clear the cache and the other breakpoints for the instruction
AddressBreakPoint breakPoint = new AddressBreakPoint(BreakPointType.WRITE, byteAddress,
Expand All @@ -81,18 +83,18 @@ private void OnBreakPointReached(BreakPoint breakPoint, CfgInstruction instructi
}

private void AddInstructionInCurrentCache(CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;
_currentInstructionAtAddress.Add(instructionAddress, instruction);
}

private void ClearCurrentInstruction(CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;
IList<AddressBreakPoint> breakpoints = _breakpointsForInstruction[instructionAddress];
_breakpointsForInstruction.Remove(instructionAddress);
foreach (AddressBreakPoint breakPoint in breakpoints) {
_machineBreakpoints.ToggleBreakPoint(breakPoint, false);
}

_currentInstructionAtAddress.Remove(instruction.PhysicalAddress);
_currentInstructionAtAddress.Remove(instruction.Address);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public InstructionsFeeder(MachineBreakpoints machineBreakpoints, IMemory memory,
}

public CfgInstruction GetInstructionFromMemory(ushort segment, ushort offset) {
uint physicalAddress = MemoryUtils.ToPhysicalAddress(segment, offset);
SegmentedAddress physicalAddress = new SegmentedAddress(segment, offset);
// Try to get instruction from cache that represents current memory state.
CfgInstruction? current = _currentInstructions.GetAtAddress(physicalAddress);
if (current != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public MemoryInstructionMatcher(IMemory memory) {
}

private bool IsMatchingWithCurrentMemory(CfgInstruction instruction) {
Span<byte> bytesInMemory = _memory.GetSpan((int)instruction.PhysicalAddress, instruction.Length);
Span<byte> bytesInMemory = _memory.GetSpan((int)instruction.Address.ToPhysical(), instruction.Length);
return instruction.Discriminator.Equals(bytesInMemory);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,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;

/// <summary>
/// Cache of previous instructions that existed in a memory address at a time.
Expand All @@ -12,38 +13,37 @@ public class PreviousInstructions : IInstructionReplacer<CfgInstruction> {
/// <summary>
/// Instructions that were parsed at a given address. List for address is ordered by instruction decreasing length
/// </summary>
private readonly IDictionary<uint, ISet<CfgInstruction>> _previousInstructionsAtAddress =
new Dictionary<uint, ISet<CfgInstruction>>();
private readonly Dictionary<SegmentedAddress, HashSet<CfgInstruction>> _previousInstructionsAtAddress = new();

public PreviousInstructions(IMemory memory) {
_memoryInstructionMatcher = new MemoryInstructionMatcher(memory);
}

public CfgInstruction? GetAtAddress(uint physicalAddress) {
if (_previousInstructionsAtAddress.TryGetValue(physicalAddress,
out ISet<CfgInstruction>? previousInstructionsAtAddress)) {
public CfgInstruction? GetAtAddress(SegmentedAddress address) {
if (_previousInstructionsAtAddress.TryGetValue(address,
out HashSet<CfgInstruction>? previousInstructionsAtAddress)) {
return _memoryInstructionMatcher.MatchExistingInstructionWithMemory(previousInstructionsAtAddress);
}

return null;
}

public void ReplaceInstruction(CfgInstruction old, CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;

if (_previousInstructionsAtAddress.TryGetValue(instructionAddress,
out ISet<CfgInstruction>? previousInstructionsAtAddress)) {
out HashSet<CfgInstruction>? previousInstructionsAtAddress)) {
if (previousInstructionsAtAddress.Remove(old)) {
AddInstructionInPrevious(instruction);
}
}
}

public void AddInstructionInPrevious(CfgInstruction instruction) {
uint instructionAddress = instruction.PhysicalAddress;
SegmentedAddress instructionAddress = instruction.Address;

if (!_previousInstructionsAtAddress.TryGetValue(instructionAddress,
out ISet<CfgInstruction>? previousInstructionsAtAddress)) {
out HashSet<CfgInstruction>? previousInstructionsAtAddress)) {
previousInstructionsAtAddress = new HashSet<CfgInstruction>();
_previousInstructionsAtAddress.Add(instructionAddress, previousInstructionsAtAddress);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void Accept(MovRegImm32 instruction) {
}

public void Accept(DiscriminatedNode discriminatedNode) {
int address = (int)discriminatedNode.PhysicalAddress;
int address = (int)discriminatedNode.Address.ToPhysical();
foreach (InstructionDiscriminator instructionDiscriminator in discriminatedNode.SuccessorsPerDiscriminator.Keys) {
int length = instructionDiscriminator.DiscriminatorValue.Count;
Span<byte> bytes = _memory.GetSpan(address, length);
Expand All @@ -83,7 +83,7 @@ private void MoveIpToEndOfInstruction(CfgInstruction instruction) {
}

private ICfgNode? GetSuccessorAtCsIp(CfgInstruction instruction) {
instruction.SuccessorsPerAddress.TryGetValue(_state.IpPhysicalAddress, out ICfgNode? res);
instruction.SuccessorsPerAddress.TryGetValue(_state.IpSegmentedAddress, out ICfgNode? res);
return res;
}

Expand Down
5 changes: 3 additions & 2 deletions src/Spice86.Core/Emulator/CPU/CfgCpu/Linker/NodeLinker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.Linker;
using Spice86.Core.Emulator.CPU.CfgCpu.Feeder;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.SelfModifying;
using Spice86.Shared.Emulator.Memory;

public class NodeLinker : IInstructionReplacer<ICfgNode> {
/// <summary>
Expand All @@ -20,8 +21,8 @@ public void Link(ICfgNode current, ICfgNode next) {
}

private void LinkCfgInstruction(CfgInstruction current, ICfgNode next) {
IDictionary<uint, ICfgNode> successors = current.SuccessorsPerAddress;
if (!successors.TryGetValue(next.PhysicalAddress, out ICfgNode? shouldBeNext)) {
Dictionary<SegmentedAddress, ICfgNode> successors = current.SuccessorsPerAddress;
if (!successors.TryGetValue(next.Address, out ICfgNode? shouldBeNext)) {
AttachCurrentToNext(current, next);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction;

using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Prefix;
using Spice86.Shared.Emulator.Memory;

using System.Linq;

/// <summary>
/// Base of all the instructions: Prefixes (optional) and an opcode that can be either one or 2 bytes.
/// </summary>
public abstract class CfgInstruction : CfgNode {
protected CfgInstruction(uint physicalAddress, InstructionField<byte> opcodeField) : this(physicalAddress,
protected CfgInstruction(SegmentedAddress address, InstructionField<byte> opcodeField) : this(address,
opcodeField, new List<InstructionPrefix>()) {
}

protected CfgInstruction(uint physicalAddress, InstructionField<byte> opcodeField,
protected CfgInstruction(SegmentedAddress address, InstructionField<byte> opcodeField,
IList<InstructionPrefix> prefixes)
: base(physicalAddress) {
: base(address) {
InstructionPrefixes = prefixes;
PrefixFields = prefixes.Select(prefix => prefix.PrefixField).ToList();
foreach (InstructionPrefix prefix in prefixes) {
Expand All @@ -42,10 +43,10 @@ public void PostInit() {
/// <summary>
/// Cache of Successors property per address. Maintenance is complex with self modifying code and is done by the InstructionLinker
/// </summary>
public Dictionary<uint, ICfgNode> SuccessorsPerAddress { get; private set; } = new Dictionary<uint, ICfgNode>();
public Dictionary<SegmentedAddress, ICfgNode> SuccessorsPerAddress { get; private set; } = new Dictionary<SegmentedAddress, ICfgNode>();

public override void UpdateSuccessorCache() {
SuccessorsPerAddress = Successors.ToDictionary(node => node.PhysicalAddress);
SuccessorsPerAddress = Successors.ToDictionary(node => node.Address);
}

public override bool IsAssembly { get => true; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Core.Emulator.CPU.CfgCpu.ControlFlowGraph;
using Spice86.Shared.Emulator.Memory;

public class HltInstruction : CfgInstruction {
public HltInstruction(uint physicalAddress, InstructionField<byte> opcodeField) :
base(physicalAddress, opcodeField) {
public HltInstruction(SegmentedAddress address, InstructionField<byte> opcodeField) :
base(address, opcodeField) {
}

public override void Visit(ICfgNodeVisitor visitor) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Shared.Emulator.Memory;

using System.Numerics;

public abstract class JmpNearImm<T> : CfgInstruction where T : ISignedNumber<T> {
public JmpNearImm(uint physicalAddress, InstructionField<byte> opcodeField, InstructionField<T> offsetField) :
base(physicalAddress, opcodeField) {
public JmpNearImm(SegmentedAddress address, InstructionField<byte> opcodeField, InstructionField<T> offsetField) :
base(address, opcodeField) {
OffsetField = offsetField;
FieldsInOrder.Add(OffsetField);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Shared.Emulator.Memory;

public class JmpNearImm16 : JmpNearImm<short> {
public override void Visit(ICfgNodeVisitor visitor) {
visitor.Accept(this);
}

public JmpNearImm16(uint physicalAddress, InstructionField<byte> opcodeField, InstructionField<short> offsetField) :
base(physicalAddress, opcodeField, offsetField) {
public JmpNearImm16(SegmentedAddress address, InstructionField<byte> opcodeField, InstructionField<short> offsetField) :
base(address, opcodeField, offsetField) {
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Shared.Emulator.Memory;

public class JmpNearImm8 : JmpNearImm<sbyte> {
public override void Visit(ICfgNodeVisitor visitor) {
visitor.Accept(this);
}

public JmpNearImm8(uint physicalAddress, InstructionField<byte> opcodeField, InstructionField<sbyte> offsetField) :
base(physicalAddress, opcodeField, offsetField) {
public JmpNearImm8(SegmentedAddress address, InstructionField<byte> opcodeField, InstructionField<sbyte> offsetField) :
base(address, opcodeField, offsetField) {
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Prefix;
using Spice86.Shared.Emulator.Memory;

using System.Numerics;

public abstract class MovRegImm<T> : CfgInstruction where T : IUnsignedNumber<T> {
public MovRegImm(uint physicalAddress,
public MovRegImm(SegmentedAddress address,
InstructionField<byte> opcodeField,
IList<InstructionPrefix> prefixes,
InstructionField<T> valueField,
int regIndex) :
base(physicalAddress, opcodeField, prefixes) {
base(address, opcodeField, prefixes) {
ValueField = valueField;
FieldsInOrder.Add(ValueField);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Prefix;
using Spice86.Shared.Emulator.Memory;

public class MovRegImm16 : MovRegImm<ushort> {
public MovRegImm16(uint physicalAddress,
public MovRegImm16(SegmentedAddress address,
InstructionField<byte> opcodeField,
IList<InstructionPrefix> prefixes,
InstructionField<ushort> valueField,
int regIndex) : base(physicalAddress, opcodeField, prefixes, valueField, regIndex) {
int regIndex) : base(address, opcodeField, prefixes, valueField, regIndex) {
}

public override void Visit(ICfgNodeVisitor visitor) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
namespace Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Instructions;

using Spice86.Core.Emulator.CPU.CfgCpu.ParsedInstruction.Prefix;
using Spice86.Shared.Emulator.Memory;

public class MovRegImm32 : MovRegImm<uint> {
public MovRegImm32(uint physicalAddress,
public MovRegImm32(SegmentedAddress address,
InstructionField<byte> opcodeField,
IList<InstructionPrefix> prefixes,
InstructionField<uint> valueField,
int regIndex) : base(physicalAddress, opcodeField, prefixes, valueField, regIndex) {
int regIndex) : base(address, opcodeField, prefixes, valueField, regIndex) {
}

public override void Visit(ICfgNodeVisitor visitor) {
Expand Down
Loading

0 comments on commit 47802dc

Please sign in to comment.