Skip to content

Commit

Permalink
fix: disable PIC if paused
Browse files Browse the repository at this point in the history
This makes the internal debugger step into more reliable.

Signed-off-by: Maximilien Noal <noal.maximilien@gmail.com>
  • Loading branch information
maximilien-noal committed Sep 7, 2024
1 parent 7c3668b commit dc575bc
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 15 deletions.
5 changes: 5 additions & 0 deletions src/Spice86.Core/Emulator/Devices/Timer/Counter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Counter {
/// </summary>
/// <param name="state">The CPU state</param>
/// <param name="loggerService">The logger service implementation</param>
/// <param name="index">The index of the counter in the counter array.</param>
/// <param name="activator">The activator for the counter</param>
public Counter(State state, ILoggerService loggerService, int index, CounterActivator activator) {
_loggerService = loggerService;
Expand Down Expand Up @@ -192,6 +193,10 @@ private byte Policy3(ushort value) {
return Lsb(value);
}

/// <summary>
/// Updates the frequency of activation based on the desired frequency
/// </summary>
/// <param name="desiredFrequency">The desired frequency of activation</param>
public void UpdateDesiredFreqency(long desiredFrequency) {
Activator.Frequency = desiredFrequency;
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
Expand Down
42 changes: 37 additions & 5 deletions src/Spice86.Core/Emulator/Devices/Timer/CounterActivator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
namespace Spice86.Core.Emulator.Devices.Timer;

using Spice86.Core.Emulator.VM;

/// <summary>
/// Base class for counter activators
/// </summary>
public abstract class CounterActivator {
private readonly IPauseHandler _pauseHandler;

/// <summary>
/// Initializes a new instance of the <see cref="CounterActivator"/> class.
/// </summary>
/// <param name="pauseHandler">The class responsible for pausing/resuming emulation.</param>
/// <param name="multiplier">The frequency multiplier.</param>
protected CounterActivator(IPauseHandler pauseHandler, double multiplier) {
_pauseHandler = pauseHandler;
Multiplier = multiplier;
}

/// <summary> True when activation can occur. </summary>
public abstract bool IsActivated { get; }

/// <summary>
/// Updates the frequency of activation based on the desired frequency
/// </summary>
/// <param name="desiredFrequency">The desired frequency of activation</param>
protected abstract void UpdateNonZeroFrequency(double desiredFrequency);

/// <summary>
/// Gets or sets the counter activation frequency multiplier
/// </summary>
public double Multiplier {
get => _multiplier;
set {
Expand All @@ -15,8 +39,15 @@ public double Multiplier {
}
}
private double _multiplier;
public bool IsFrozen => Multiplier == 0;

/// <summary>
/// Gets a value indicating whether the counter can't be activated.
/// </summary>
public bool IsFrozen => Multiplier == 0 || _pauseHandler.IsPaused;

/// <summary>
/// Gets or sets the frequency at which the counter is activated.
/// </summary>
public long Frequency {
get => _frequency;
set {
Expand All @@ -27,10 +58,11 @@ public long Frequency {

private long _frequency = 1;

protected CounterActivator(double multiplier) {
Multiplier = multiplier;
}

/// <summary>
/// Computes the actual frequency based on the desired frequency
/// </summary>
/// <param name="desiredFrequency">The desired frequency rate </param>
/// <returns>The actual frequency</returns>
protected double ComputeActualFrequency(long desiredFrequency) {
return Multiplier * desiredFrequency;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,32 @@ namespace Spice86.Core.Emulator.Devices.Timer;

using Spice86.Core.CLI;
using Spice86.Core.Emulator.CPU;
using Spice86.Core.Emulator.VM;

/// <summary>
/// A factory class that creates a new instance of one of the implementations of <see cref="CounterActivator"/> class based on the emulator configuration.
/// </summary>
public class CounterConfiguratorFactory {
private readonly State _state;
private readonly ILoggerService _loggerService;
private readonly IPauseHandler _pauseHandler;
private const long DefaultInstructionsPerSecond = 1000000L;
private readonly Configuration _configuration;

public CounterConfiguratorFactory(Configuration configuration, ILoggerService loggerService) {
/// <summary>
/// Initializes a new instance of the <see cref="CounterConfiguratorFactory"/> class.
/// </summary>
public CounterConfiguratorFactory(Configuration configuration, State state, IPauseHandler pauseHandler, ILoggerService loggerService) {
_state = state;
_loggerService = loggerService;
_pauseHandler = pauseHandler;
_configuration = configuration;
}

public CounterActivator InstanciateCounterActivator(State state) {
/// <summary>
/// Creates a new instance of one of the implementations of <see cref="CounterActivator"/> class based on the emulator configuration.
/// </summary>
public CounterActivator InstanciateCounterActivator() {
long? instructionsPerSecond = _configuration.InstructionsPerSecond;
if (instructionsPerSecond == null && _configuration.GdbPort != null) {
// With GDB, force to instructions per seconds as time based timers could perturbate steps
Expand All @@ -27,9 +41,9 @@ public CounterActivator InstanciateCounterActivator(State state) {
}

if (instructionsPerSecond != null) {
return new CyclesCounterActivator(state, instructionsPerSecond.Value, _configuration.TimeMultiplier);
return new CyclesCounterActivator(_state, _pauseHandler, instructionsPerSecond.Value, _configuration.TimeMultiplier);
}

return new TimeCounterActivator(_configuration.TimeMultiplier);
return new TimeCounterActivator(_pauseHandler, _configuration.TimeMultiplier);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Spice86.Core.Emulator.Devices.Timer;

using Spice86.Core.Emulator.CPU;
using Spice86.Core.Emulator.VM;

/// <summary>
/// Counter activator based on emulated cycles
Expand All @@ -11,11 +12,19 @@ public class CyclesCounterActivator : CounterActivator {
private long _cyclesBetweenActivations;
private long _lastActivationCycle;

public CyclesCounterActivator(State state, long instructionsPerSecond, double multiplier) : base(multiplier) {
/// <summary>
/// Initializes a new instance of the <see cref="CyclesCounterActivator"/> class.
/// </summary>
/// <param name="state">The CPU registers and flags.</param>
/// <param name="pauseHandler">The class responsible for pausing/resuming emulation.</param>
/// <param name="instructionsPerSecond">The number of instructions per second between activations.</param>
/// <param name="multiplier">The initial value for the frequency multiplier.</param>
public CyclesCounterActivator(State state, IPauseHandler pauseHandler, long instructionsPerSecond, double multiplier) : base (pauseHandler, multiplier) {
_state = state;
_instructionsPerSecond = instructionsPerSecond;
}

/// <inheritdoc />
public override bool IsActivated {
get {
if (IsFrozen) {
Expand All @@ -32,6 +41,7 @@ public override bool IsActivated {
}
}

/// <inheritdoc />
protected override void UpdateNonZeroFrequency(double desiredFrequency) {
_cyclesBetweenActivations = (long)(_instructionsPerSecond / desiredFrequency);
}
Expand Down
11 changes: 10 additions & 1 deletion src/Spice86.Core/Emulator/Devices/Timer/TimeCounterActivator.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
namespace Spice86.Core.Emulator.Devices.Timer;

using Spice86.Core.Emulator.VM;

/// <summary>
/// Counter activator based on real system time
/// </summary>
public class TimeCounterActivator : CounterActivator {
private long _lastActivationTime = System.Diagnostics.Stopwatch.GetTimestamp();
private long _timeBetweenTicks;
private long _ticks = 0;
public TimeCounterActivator(double multiplier) : base(multiplier) {

/// <summary>
/// Initializes a new instance of the <see cref="TimeCounterActivator"/> class.
/// </summary>
public TimeCounterActivator(IPauseHandler pauseHandler, double multiplier) : base(pauseHandler, multiplier) {
}

/// <inheritdoc />
public override bool IsActivated {
get {
_ticks++;
Expand All @@ -29,6 +37,7 @@ public override bool IsActivated {
}
}

/// <inheritdoc />
protected override void UpdateNonZeroFrequency(double desiredFrequency) {
_timeBetweenTicks = (long)(System.Diagnostics.Stopwatch.Frequency / desiredFrequency);
}
Expand Down
18 changes: 15 additions & 3 deletions src/Spice86.Core/Emulator/Devices/Timer/Timer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ public class Timer : DefaultIOPortHandler, ITimeMultiplier {
/// Initializes a new instance of the <see cref="Timer"/> class.
/// </summary>
public Timer(Configuration configuration, State state, IOPortDispatcher ioPortDispatcher,
ILoggerService loggerService, DualPic dualPic) : base(state, configuration.FailOnUnhandledPort, loggerService) {
CounterConfiguratorFactory counterConfiguratorFactory, ILoggerService loggerService, DualPic dualPic) : base(state, configuration.FailOnUnhandledPort, loggerService) {
_dualPic = dualPic;
CounterConfiguratorFactory counterConfiguratorFactory = new(configuration, loggerService);

for (int i = 0; i < _counters.Length; i++) {
_counters[i] = new Counter(state,
_loggerService,
i, counterConfiguratorFactory.InstanciateCounterActivator(state));
i, counterConfiguratorFactory.InstanciateCounterActivator());
}
InitPortHandlers(ioPortDispatcher);
}
Expand All @@ -52,13 +52,22 @@ public void SetTimeMultiplier(double multiplier) {
}
}

/// <summary>
/// Gets the counter at the specified index.
/// </summary>
/// <param name="counterIndex">The index of the counter to retrieve</param>
/// <returns>A reference to the counter</returns>
/// <exception cref="InvalidCounterIndexException">The index was out of range.</exception>
public Counter GetCounter(int counterIndex) {
if (counterIndex > _counters.Length || counterIndex < 0) {
throw new InvalidCounterIndexException(_state, counterIndex);
}
return _counters[counterIndex];
}

/// <summary>
/// Gets the number of ticks in the first counter.
/// </summary>
public long NumberOfTicks => _counters[0].Ticks;

/// <inheritdoc />
Expand Down Expand Up @@ -104,6 +113,9 @@ public override void WriteByte(int port, byte value) {
}


/// <summary>
/// If the counter is activated, triggers the interrupt request
/// </summary>
public void Tick() {
if (_counters[0].ProcessActivation()) {
_dualPic.ProcessInterruptRequest(0);
Expand Down
3 changes: 2 additions & 1 deletion src/Spice86/Spice86DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co
bootUpInTextMode: configuration.InitializeDOS is true);
VgaBios vgaBios = new VgaBios(memory, cpu, vgaFunctionality, biosDataArea, loggerService);

Timer timer = new Timer(configuration, state, ioPortDispatcher, loggerService, dualPic);
Timer timer = new Timer(configuration, state, ioPortDispatcher,
new CounterConfiguratorFactory(configuration, state, pauseHandler, loggerService), loggerService, dualPic);
TimerInt8Handler timerInt8Handler =
new TimerInt8Handler(memory, cpu, dualPic, timer, biosDataArea, loggerService);

Expand Down

0 comments on commit dc575bc

Please sign in to comment.