diff --git a/StoneRed.LogicSimulator/Simulation/LogicGateSimulator.cs b/StoneRed.LogicSimulator/Simulation/LogicGateSimulator.cs index 5d5bc3a..a5534d9 100644 --- a/StoneRed.LogicSimulator/Simulation/LogicGateSimulator.cs +++ b/StoneRed.LogicSimulator/Simulation/LogicGateSimulator.cs @@ -11,10 +11,13 @@ namespace StoneRed.LogicSimulator.Simulation; internal class LogicGateSimulator { private readonly ConcurrentDictionary logicGates = new ConcurrentDictionary(); + private readonly List 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 { @@ -193,4 +215,4 @@ internal class LogicGateSimulator _ => throw new NotImplementedException() }; } -} \ No newline at end of file +} diff --git a/StoneRed.LogicSimulator/Simulation/LogicGates/Clock.cs b/StoneRed.LogicSimulator/Simulation/LogicGates/Clock.cs index 663de5f..179efff 100644 --- a/StoneRed.LogicSimulator/Simulation/LogicGates/Clock.cs +++ b/StoneRed.LogicSimulator/Simulation/LogicGates/Clock.cs @@ -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); } -} \ No newline at end of file +}