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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user