Implement global clock synchronization for new simulator

This commit is contained in:
Stone_Red
2026-03-27 02:22:32 +01:00
parent c9371c356c
commit 5b3cdc186f
2 changed files with 30 additions and 83 deletions
@@ -11,10 +11,13 @@ namespace StoneRed.LogicSimulator.Simulation;
internal class LogicGateSimulator
{
private readonly ConcurrentDictionary<ulong, LogicGate> logicGates = new ConcurrentDictionary<ulong, LogicGate>();
private readonly List<int> globalClockTargets = [];
private DateTime dateTime;
private bool logicGatesUpdated = false;
private ulong logicGateId = 0;
private ICircuitSimulator? circuitSimulator;
private int globalClockSourceGateId = -1;
private bool globalClockState;
public int TargetTicksPerSecond { get; set; } = 100;
@@ -110,14 +113,15 @@ internal class LogicGateSimulator
private void SimulationThread()
{
int tps = 0;
float sleepDelayIterations = 10000;
int sleepDelayIterations = 10000;
int sleepDelayMs = 10;
circuitSimulator = CreateSimulator();
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;
tps = 0;
@@ -125,7 +129,8 @@ internal class LogicGateSimulator
{
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;
}
@@ -143,11 +148,25 @@ internal class LogicGateSimulator
if (logicGatesUpdated)
{
circuitSimulator = CreateSimulator();
globalClockTargets.Clear();
globalClockSourceGateId = circuitSimulator.AddGate(GateKind.Source);
globalClockState = false;
circuitSimulator.SetSource(globalClockSourceGateId, false);
// Register all gates first
foreach (LogicGate gate in logicGates.Values)
{
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
@@ -169,11 +188,14 @@ internal class LogicGateSimulator
logicGatesUpdated = false;
}
globalClockState = !globalClockState;
circuitSimulator.SetSource(globalClockSourceGateId, globalClockState);
circuitSimulator.Step();
if (HighPerformanceClock)
{
Thread.SpinWait((int)sleepDelayIterations);
Thread.SpinWait(sleepDelayIterations);
}
else
{
@@ -1,93 +1,18 @@
using MonoGame.Extended.Input;
using StoneRed.LogicSimulator.Api;
using StoneRed.LogicSimulator.Api.Attributes;
using StoneRed.LogicSimulator.Api.Interfaces;
using System;
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
[LogicGateName("Clock")]
[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 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)
{
this.circuitSimulator = circuitSimulator;
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);
SimulatorGateId = circuitSimulator.AddGate(GateKind.Buffer);
}
}