mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 09:06:29 +02:00
Implement global clock synchronization for new simulator
This commit is contained in:
@@ -11,10 +11,13 @@ namespace StoneRed.LogicSimulator.Simulation;
|
|||||||
internal class LogicGateSimulator
|
internal class LogicGateSimulator
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<ulong, LogicGate> logicGates = new ConcurrentDictionary<ulong, LogicGate>();
|
private readonly ConcurrentDictionary<ulong, LogicGate> logicGates = new ConcurrentDictionary<ulong, LogicGate>();
|
||||||
|
private readonly List<int> globalClockTargets = [];
|
||||||
private DateTime dateTime;
|
private DateTime dateTime;
|
||||||
private bool logicGatesUpdated = false;
|
private bool logicGatesUpdated = false;
|
||||||
private ulong logicGateId = 0;
|
private ulong logicGateId = 0;
|
||||||
private ICircuitSimulator? circuitSimulator;
|
private ICircuitSimulator? circuitSimulator;
|
||||||
|
private int globalClockSourceGateId = -1;
|
||||||
|
private bool globalClockState;
|
||||||
|
|
||||||
public int TargetTicksPerSecond { get; set; } = 100;
|
public int TargetTicksPerSecond { get; set; } = 100;
|
||||||
|
|
||||||
@@ -110,14 +113,15 @@ internal class LogicGateSimulator
|
|||||||
private void SimulationThread()
|
private void SimulationThread()
|
||||||
{
|
{
|
||||||
int tps = 0;
|
int tps = 0;
|
||||||
float sleepDelayIterations = 10000;
|
int sleepDelayIterations = 10000;
|
||||||
int sleepDelayMs = 10;
|
int sleepDelayMs = 10;
|
||||||
|
|
||||||
circuitSimulator = CreateSimulator();
|
circuitSimulator = CreateSimulator();
|
||||||
|
|
||||||
Timer timeCheckTimer = new Timer(_ =>
|
Timer timeCheckTimer = new Timer(_ =>
|
||||||
{
|
{
|
||||||
ActualTicksPerSecond = tps;
|
// Compensate for any time drift by calculating actual TPS and adjusting sleep delay accordingly
|
||||||
|
ActualTicksPerSecond = (int)Math.Round(tps / (DateTime.Now - dateTime).TotalSeconds);
|
||||||
dateTime = DateTime.Now;
|
dateTime = DateTime.Now;
|
||||||
tps = 0;
|
tps = 0;
|
||||||
|
|
||||||
@@ -125,7 +129,8 @@ internal class LogicGateSimulator
|
|||||||
{
|
{
|
||||||
float percentage = Math.Abs((TargetTicksPerSecond - (float)ActualTicksPerSecond) / Math.Abs((float)ActualTicksPerSecond) * 100);
|
float percentage = Math.Abs((TargetTicksPerSecond - (float)ActualTicksPerSecond) / Math.Abs((float)ActualTicksPerSecond) * 100);
|
||||||
|
|
||||||
sleepDelayIterations *= (float)ActualTicksPerSecond / TargetTicksPerSecond;
|
sleepDelayIterations = Math.Max(sleepDelayIterations, 1);
|
||||||
|
sleepDelayIterations *= (int)((float)ActualTicksPerSecond / TargetTicksPerSecond);
|
||||||
|
|
||||||
ClockCalibrating = percentage > 5;
|
ClockCalibrating = percentage > 5;
|
||||||
}
|
}
|
||||||
@@ -143,11 +148,25 @@ internal class LogicGateSimulator
|
|||||||
if (logicGatesUpdated)
|
if (logicGatesUpdated)
|
||||||
{
|
{
|
||||||
circuitSimulator = CreateSimulator();
|
circuitSimulator = CreateSimulator();
|
||||||
|
globalClockTargets.Clear();
|
||||||
|
globalClockSourceGateId = circuitSimulator.AddGate(GateKind.Source);
|
||||||
|
globalClockState = false;
|
||||||
|
circuitSimulator.SetSource(globalClockSourceGateId, false);
|
||||||
|
|
||||||
// Register all gates first
|
// Register all gates first
|
||||||
foreach (LogicGate gate in logicGates.Values)
|
foreach (LogicGate gate in logicGates.Values)
|
||||||
{
|
{
|
||||||
gate.Register(circuitSimulator);
|
gate.Register(circuitSimulator);
|
||||||
|
if (gate is LogicGates.Clock)
|
||||||
|
{
|
||||||
|
globalClockTargets.Add(gate.SimulatorGateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect the global clock source to all clock gates
|
||||||
|
for (int i = 0; i < globalClockTargets.Count; i++)
|
||||||
|
{
|
||||||
|
circuitSimulator.ConnectGates(globalClockSourceGateId, globalClockTargets[i], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then connect them based on LogicGate connections
|
// Then connect them based on LogicGate connections
|
||||||
@@ -169,11 +188,14 @@ internal class LogicGateSimulator
|
|||||||
logicGatesUpdated = false;
|
logicGatesUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globalClockState = !globalClockState;
|
||||||
|
circuitSimulator.SetSource(globalClockSourceGateId, globalClockState);
|
||||||
|
|
||||||
circuitSimulator.Step();
|
circuitSimulator.Step();
|
||||||
|
|
||||||
if (HighPerformanceClock)
|
if (HighPerformanceClock)
|
||||||
{
|
{
|
||||||
Thread.SpinWait((int)sleepDelayIterations);
|
Thread.SpinWait(sleepDelayIterations);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -193,4 +215,4 @@ internal class LogicGateSimulator
|
|||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +1,18 @@
|
|||||||
using MonoGame.Extended.Input;
|
|
||||||
using StoneRed.LogicSimulator.Api;
|
using StoneRed.LogicSimulator.Api;
|
||||||
using StoneRed.LogicSimulator.Api.Attributes;
|
using StoneRed.LogicSimulator.Api.Attributes;
|
||||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||||
|
|
||||||
[LogicGateName("Clock")]
|
[LogicGateName("Clock")]
|
||||||
[LogicGateDescription("A clock is a circuit that oscillates between a high and a low state.")]
|
[LogicGateDescription("A clock is a circuit that oscillates between a high and a low state.")]
|
||||||
internal class Clock : LogicGate, IInteractable
|
internal class Clock : LogicGate
|
||||||
{
|
{
|
||||||
private ICircuitSimulator? circuitSimulator;
|
|
||||||
private int gateId;
|
|
||||||
private int count = 0;
|
|
||||||
private int tickRate = 0;
|
|
||||||
private bool currentState = false;
|
|
||||||
|
|
||||||
public override int OutputCount { get; set; } = 1;
|
public override int OutputCount { get; set; } = 1;
|
||||||
|
|
||||||
public override int InputCount { get; set; } = 0;
|
public override int InputCount { get; set; } = 0;
|
||||||
|
|
||||||
public string Info
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (tickRate <= 0)
|
|
||||||
{
|
|
||||||
return "Disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
return tickRate + "\n" + ((count > tickRate) ? count - tickRate : count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnInteraction(MouseStateExtended mouseState, MouseStateExtended previousMouseState, KeyboardStateExtended keyboardStateExtended)
|
|
||||||
{
|
|
||||||
if (mouseState.DeltaScrollWheelValue == 0 || !keyboardStateExtended.IsShiftDown())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mouseState.DeltaScrollWheelValue < 0 && tickRate < int.MaxValue - 10)
|
|
||||||
{
|
|
||||||
tickRate += keyboardStateExtended.IsControlDown() ? 10 : 1;
|
|
||||||
}
|
|
||||||
else if (tickRate >= 1)
|
|
||||||
{
|
|
||||||
tickRate -= keyboardStateExtended.IsControlDown() ? 10 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
tickRate = Math.Clamp(tickRate, 0, int.MaxValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
this.circuitSimulator = circuitSimulator;
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Buffer);
|
||||||
SimulatorGateId = circuitSimulator.AddGate(GateKind.Source);
|
|
||||||
gateId = SimulatorGateId;
|
|
||||||
|
|
||||||
// Watch own output to count ticks and toggle
|
|
||||||
circuitSimulator.WatchGate(SimulatorGateId, (oldMask, newMask) =>
|
|
||||||
{
|
|
||||||
if (tickRate <= 0)
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
currentState = false;
|
|
||||||
circuitSimulator.SetSource(gateId, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the tick
|
|
||||||
count++;
|
|
||||||
|
|
||||||
if (count >= tickRate * 2)
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle state based on count
|
|
||||||
bool newState = count > tickRate;
|
|
||||||
if (newState != currentState)
|
|
||||||
{
|
|
||||||
currentState = newState;
|
|
||||||
circuitSimulator.SetSource(gateId, newState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize to off state
|
|
||||||
circuitSimulator.SetSource(gateId, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user