mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 00:56:23 +02:00
Implement new simulator
This commit is contained in:
@@ -1,25 +1,20 @@
|
|||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
using StoneRed.LogicSimulator.Api.Utilities;
|
using StoneRed.LogicSimulator.Api.Utilities;
|
||||||
|
using StoneRed.LogicSimulator.Simulation;
|
||||||
|
|
||||||
namespace StoneRed.LogicSimulator.Api;
|
namespace StoneRed.LogicSimulator.Api;
|
||||||
#pragma warning disable S112 // General exceptions should never be thrown
|
|
||||||
|
|
||||||
internal abstract class LogicGate
|
internal abstract class LogicGate
|
||||||
{
|
{
|
||||||
private readonly List<LogicGateConnection> logicGateConnections = new();
|
private readonly List<LogicGateConnection> logicGateConnections = [];
|
||||||
private bool logicGateConnectionsChnaged = false;
|
|
||||||
private int currentInput;
|
|
||||||
private int newInput;
|
|
||||||
private int output;
|
|
||||||
private int cachedOutput;
|
|
||||||
private LogicGateConnection[] logicGateConnectionsArray = Array.Empty<LogicGateConnection>();
|
|
||||||
public abstract int OutputCount { get; set; }
|
public abstract int OutputCount { get; set; }
|
||||||
public abstract int InputCount { get; set; }
|
public abstract int InputCount { get; set; }
|
||||||
internal IReadOnlyList<LogicGateConnection> LogicGateConnections => logicGateConnections.AsReadOnly();
|
internal IReadOnlyList<LogicGateConnection> LogicGateConnections => logicGateConnections.AsReadOnly();
|
||||||
internal GraphicsDevice? GraphicsDevice { get; set; }
|
internal GraphicsDevice? GraphicsDevice { get; set; }
|
||||||
internal LogicGateWorldData WorldData { get; init; } = new LogicGateWorldData();
|
internal LogicGateWorldData WorldData { get; init; } = new LogicGateWorldData();
|
||||||
internal ulong Id { get; set; }
|
internal ulong Id { get; set; }
|
||||||
|
internal int SimulatorGateId { get; set; } = -1;
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
@@ -32,44 +27,14 @@ internal abstract class LogicGate
|
|||||||
return Id.GetHashCode();
|
return Id.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void NextTick()
|
|
||||||
{
|
|
||||||
currentInput = newInput;
|
|
||||||
newInput = 0;
|
|
||||||
output = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetInput(int value, int index)
|
|
||||||
{
|
|
||||||
if (index >= InputCount)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inputs should act as or gates
|
|
||||||
|
|
||||||
if (value == 1)
|
|
||||||
{
|
|
||||||
newInput.SetBit(1, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Connect(LogicGate logicGate, int inputIndex, int outputIndex)
|
internal void Connect(LogicGate logicGate, int inputIndex, int outputIndex)
|
||||||
{
|
{
|
||||||
if (inputIndex >= logicGate.InputCount)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(inputIndex, logicGate.InputCount);
|
||||||
{
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(outputIndex, OutputCount);
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputIndex >= OutputCount)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (logicGateConnections)
|
lock (logicGateConnections)
|
||||||
{
|
{
|
||||||
logicGateConnections.Add(new LogicGateConnection(logicGate, inputIndex, outputIndex));
|
logicGateConnections.Add(new LogicGateConnection(logicGate, inputIndex, outputIndex));
|
||||||
logicGateConnectionsChnaged = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,89 +58,24 @@ internal abstract class LogicGate
|
|||||||
(!inputIndex.HasValue || c.InputIndex == inputIndex) &&
|
(!inputIndex.HasValue || c.InputIndex == inputIndex) &&
|
||||||
(!outputIndex.HasValue || c.OutputIndex == outputIndex));
|
(!outputIndex.HasValue || c.OutputIndex == outputIndex));
|
||||||
logicGateConnections.RemoveAt(index);
|
logicGateConnections.RemoveAt(index);
|
||||||
logicGateConnectionsChnaged = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update()
|
|
||||||
{
|
|
||||||
Execute();
|
|
||||||
cachedOutput = output;
|
|
||||||
PublishOutput();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int GetCachedOutputBit(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= OutputCount)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedOutput.GetBit(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal virtual void Initialize()
|
protected internal virtual void Initialize()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
protected abstract void Execute();
|
protected internal abstract void Register(ICircuitSimulator circuitSimulator);
|
||||||
|
|
||||||
protected int GetInputBit(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= InputCount)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentInput.GetBit(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetOutputBit(int value, int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= OutputCount)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
output.SetBit(value, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Texture2D CreateTexture(int width, int height)
|
protected Texture2D CreateTexture(int width, int height)
|
||||||
{
|
{
|
||||||
return new Texture2D(GraphicsDevice, width, height);
|
return new Texture2D(GraphicsDevice, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PublishOutput()
|
|
||||||
{
|
|
||||||
if (logicGateConnectionsChnaged)
|
|
||||||
{
|
|
||||||
lock (logicGateConnections)
|
|
||||||
{
|
|
||||||
logicGateConnectionsArray = logicGateConnections.ToArray();
|
|
||||||
logicGateConnectionsChnaged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < logicGateConnectionsArray.Length; i++)
|
|
||||||
{
|
|
||||||
LogicGateConnection connection = logicGateConnectionsArray[i];
|
|
||||||
connection.LogicGate.SetInput(output.GetBit(connection.OutputIndex), connection.InputIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning restore S112 // General exceptions should never be thrown
|
internal class LogicGateConnection(LogicGate logicGate, int inputIndex, int outputIndex)
|
||||||
|
|
||||||
internal class LogicGateConnection
|
|
||||||
{
|
{
|
||||||
public LogicGate LogicGate { get; }
|
public LogicGate LogicGate { get; } = logicGate;
|
||||||
|
|
||||||
public int InputIndex { get; }
|
public int InputIndex { get; } = inputIndex;
|
||||||
public int OutputIndex { get; }
|
public int OutputIndex { get; } = outputIndex;
|
||||||
|
|
||||||
public LogicGateConnection(LogicGate logicGate, int inputIndex, int outputIndex)
|
|
||||||
{
|
|
||||||
LogicGate = logicGate;
|
|
||||||
InputIndex = inputIndex;
|
|
||||||
OutputIndex = outputIndex;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -13,5 +13,8 @@
|
|||||||
<PackageReference Include="MonoGame.Extended.Input" Version="3.8.0" />
|
<PackageReference Include="MonoGame.Extended.Input" Version="3.8.0" />
|
||||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\StoneRed.LogicSimulator.Simulation\StoneRed.LogicSimulator.Simulation.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ internal class LogicGateSimulator
|
|||||||
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;
|
||||||
|
|
||||||
public int TargetTicksPerSecond { get; set; } = 100;
|
public int TargetTicksPerSecond { get; set; } = 100;
|
||||||
|
|
||||||
public int ActualTicksPerSecond { get; private set; }
|
public int ActualTicksPerSecond { get; private set; }
|
||||||
@@ -34,13 +36,18 @@ internal class LogicGateSimulator
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
if (IsRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsRunning = true;
|
||||||
|
|
||||||
Thread thread = new Thread(SimulationThread)
|
Thread thread = new Thread(SimulationThread)
|
||||||
{
|
{
|
||||||
IsBackground = true
|
IsBackground = true
|
||||||
};
|
};
|
||||||
|
|
||||||
IsRunning = true;
|
|
||||||
|
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +62,11 @@ internal class LogicGateSimulator
|
|||||||
logicGatesUpdated = logicGates.TryAdd(gate.Id, gate);
|
logicGatesUpdated = logicGates.TryAdd(gate.Id, gate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LogicGatesUpdated()
|
||||||
|
{
|
||||||
|
logicGatesUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
public LogicGate GetLogicGate(ulong id)
|
public LogicGate GetLogicGate(ulong id)
|
||||||
{
|
{
|
||||||
return logicGates[id];
|
return logicGates[id];
|
||||||
@@ -75,12 +87,44 @@ internal class LogicGateSimulator
|
|||||||
logicGatesUpdated = logicGates.TryRemove(logicGate.Id, out _);
|
logicGatesUpdated = logicGates.TryRemove(logicGate.Id, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool GetGateOutput(LogicGate gate)
|
||||||
|
{
|
||||||
|
if (circuitSimulator == null || gate.SimulatorGateId < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return circuitSimulator.GetOutput(gate.SimulatorGateId);
|
||||||
|
}
|
||||||
|
|
||||||
private void SimulationThread()
|
private void SimulationThread()
|
||||||
{
|
{
|
||||||
int tps = 0;
|
int tps = 0;
|
||||||
float iterations = 10000;
|
float sleepDelayIterations = 10000;
|
||||||
|
int sleepDelayMs = 10;
|
||||||
|
|
||||||
LogicGate[] logicGatesArray = logicGates.Values.ToArray();
|
circuitSimulator = new CycleCircuitSimulator();
|
||||||
|
|
||||||
|
Timer timeCheckTimer = new Timer(_ =>
|
||||||
|
{
|
||||||
|
ActualTicksPerSecond = tps;
|
||||||
|
dateTime = DateTime.Now;
|
||||||
|
tps = 0;
|
||||||
|
|
||||||
|
if (HighPerformanceClock)
|
||||||
|
{
|
||||||
|
float percentage = Math.Abs((TargetTicksPerSecond - (float)ActualTicksPerSecond) / Math.Abs((float)ActualTicksPerSecond) * 100);
|
||||||
|
|
||||||
|
sleepDelayIterations *= (float)ActualTicksPerSecond / TargetTicksPerSecond;
|
||||||
|
|
||||||
|
ClockCalibrating = percentage > 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update cached sleep delay when target TPS changes
|
||||||
|
sleepDelayMs = Math.Max((int)(1000f / TargetTicksPerSecond), 1);
|
||||||
|
}
|
||||||
|
}, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
while (IsRunning)
|
while (IsRunning)
|
||||||
{
|
{
|
||||||
@@ -88,55 +132,45 @@ internal class LogicGateSimulator
|
|||||||
|
|
||||||
if (logicGatesUpdated)
|
if (logicGatesUpdated)
|
||||||
{
|
{
|
||||||
logicGatesArray = logicGates.Values.ToArray();
|
circuitSimulator = new CycleCircuitSimulator();
|
||||||
|
|
||||||
|
// Register all gates first
|
||||||
|
foreach (LogicGate gate in logicGates.Values)
|
||||||
|
{
|
||||||
|
gate.Register(circuitSimulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then connect them based on LogicGate connections
|
||||||
|
foreach (LogicGate gate in logicGates.Values)
|
||||||
|
{
|
||||||
|
foreach (LogicGateConnection connection in gate.LogicGateConnections)
|
||||||
|
{
|
||||||
|
// Connect: this gate's output → connected gate's input
|
||||||
|
circuitSimulator.ConnectGates(
|
||||||
|
gate.SimulatorGateId,
|
||||||
|
connection.LogicGate.SimulatorGateId,
|
||||||
|
connection.InputIndex
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
circuitSimulator.Reset();
|
||||||
|
|
||||||
logicGatesUpdated = false;
|
logicGatesUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DateTime.Now > dateTime.AddSeconds(1))
|
circuitSimulator.Step();
|
||||||
{
|
|
||||||
ActualTicksPerSecond = tps;
|
|
||||||
dateTime = DateTime.Now;
|
|
||||||
tps = 0;
|
|
||||||
|
|
||||||
if (HighPerformanceClock)
|
|
||||||
{
|
|
||||||
float percentage = Math.Abs((TargetTicksPerSecond - (float)ActualTicksPerSecond) / Math.Abs((float)ActualTicksPerSecond) * 100);
|
|
||||||
|
|
||||||
if (ActualTicksPerSecond / 2 > TargetTicksPerSecond)
|
|
||||||
{
|
|
||||||
iterations *= 2;
|
|
||||||
}
|
|
||||||
else if (ActualTicksPerSecond * 2 < TargetTicksPerSecond)
|
|
||||||
{
|
|
||||||
iterations /= 2;
|
|
||||||
}
|
|
||||||
else if (percentage > 5)
|
|
||||||
{
|
|
||||||
iterations *= (float)ActualTicksPerSecond / TargetTicksPerSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClockCalibrating = percentage > 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < logicGatesArray.Length; i++)
|
|
||||||
{
|
|
||||||
logicGatesArray[i].NextTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < logicGatesArray.Length; i++)
|
|
||||||
{
|
|
||||||
logicGatesArray[i].Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HighPerformanceClock)
|
if (HighPerformanceClock)
|
||||||
{
|
{
|
||||||
Thread.SpinWait((int)iterations);
|
Thread.SpinWait((int)sleepDelayIterations);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Thread.Sleep(Math.Max((int)(1f / TargetTicksPerSecond * 1000), 1));
|
Thread.Sleep(sleepDelayMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = timeCheckTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ internal class AndGate : LogicGate
|
|||||||
|
|
||||||
public override int OutputCount { get; set; } = 1;
|
public override int OutputCount { get; set; } = 1;
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
SetOutputBit(GetInputBit(0) == 1 && GetInputBit(1) == 1 ? 1 : 0, 0);
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.And2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,30 +11,30 @@ namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
|||||||
[LogicGateDescription("A button is a momentary switch that can be turned on and off.")]
|
[LogicGateDescription("A button is a momentary switch that can be turned on and off.")]
|
||||||
internal class Button : LogicGate, IInteractable, IColorable
|
internal class Button : LogicGate, IInteractable, IColorable
|
||||||
{
|
{
|
||||||
|
private ICircuitSimulator? circuitSimulator;
|
||||||
|
private int gateId;
|
||||||
|
private bool isPressed;
|
||||||
|
|
||||||
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 bool IsPressed { get; set; }
|
|
||||||
public Color Color { get; set; } = Color.Purple;
|
public Color Color { get; set; } = Color.Purple;
|
||||||
public string Info { get; set; } = "OFF";
|
public string Info { get; set; } = "OFF";
|
||||||
|
|
||||||
public void OnInteraction(MouseStateExtended mouseState, MouseStateExtended previousMouseState, KeyboardStateExtended keyboardStateExtended)
|
public void OnInteraction(MouseStateExtended mouseState, MouseStateExtended previousMouseState, KeyboardStateExtended keyboardStateExtended)
|
||||||
{
|
{
|
||||||
IsPressed = mouseState.IsButtonDown(MouseButton.Left);
|
isPressed = mouseState.IsButtonDown(MouseButton.Left);
|
||||||
Color = IsPressed ? Color.Green : Color.Purple;
|
Color = isPressed ? Color.Green : Color.Purple;
|
||||||
Info = IsPressed ? "ON" : "OFF";
|
Info = isPressed ? "ON" : "OFF";
|
||||||
|
|
||||||
|
circuitSimulator?.SetSource(gateId, isPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
if (IsPressed)
|
this.circuitSimulator = circuitSimulator;
|
||||||
{
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Source);
|
||||||
SetOutputBit(1, 0);
|
gateId = SimulatorGateId;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetOutputBit(0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,12 @@ namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
|||||||
[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, IInteractable
|
||||||
{
|
{
|
||||||
|
private ICircuitSimulator? circuitSimulator;
|
||||||
|
private int gateId;
|
||||||
private int count = 0;
|
private int count = 0;
|
||||||
private int tickRate = 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;
|
||||||
@@ -49,28 +53,41 @@ internal class Clock : LogicGate, IInteractable
|
|||||||
tickRate = Math.Clamp(tickRate, 0, int.MaxValue);
|
tickRate = Math.Clamp(tickRate, 0, int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
if (tickRate <= 0)
|
this.circuitSimulator = circuitSimulator;
|
||||||
{
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Source);
|
||||||
SetOutputBit(0, 0);
|
gateId = SimulatorGateId;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count >= tickRate * 2)
|
// Watch own output to count ticks and toggle
|
||||||
|
circuitSimulator.WatchGate(SimulatorGateId, (oldMask, newMask) =>
|
||||||
{
|
{
|
||||||
count = 0;
|
if (tickRate <= 0)
|
||||||
}
|
{
|
||||||
|
count = 0;
|
||||||
|
currentState = false;
|
||||||
|
circuitSimulator.SetSource(gateId, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
count++;
|
// Count the tick
|
||||||
|
count++;
|
||||||
|
|
||||||
if (count > tickRate)
|
if (count >= tickRate * 2)
|
||||||
{
|
{
|
||||||
SetOutputBit(1, 0);
|
count = 0;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Toggle state based on count
|
||||||
SetOutputBit(0, 0);
|
bool newState = count > tickRate;
|
||||||
}
|
if (newState != currentState)
|
||||||
|
{
|
||||||
|
currentState = newState;
|
||||||
|
circuitSimulator.SetSource(gateId, newState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize to off state
|
||||||
|
circuitSimulator.SetSource(gateId, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,8 +15,15 @@ internal class Lamp : LogicGate, IColorable
|
|||||||
|
|
||||||
public Color Color { get; set; } = Color.CadetBlue;
|
public Color Color { get; set; } = Color.CadetBlue;
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
Color = GetInputBit(0) == 0 ? Color.CadetBlue : Color.Yellow;
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Sink);
|
||||||
|
|
||||||
|
// Watch input to update lamp color
|
||||||
|
circuitSimulator.WatchGate(SimulatorGateId, (oldMask, newMask) =>
|
||||||
|
{
|
||||||
|
bool isOn = (newMask & 1) != 0;
|
||||||
|
Color = isOn ? Color.Yellow : Color.CadetBlue;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ internal class NotGate : LogicGate
|
|||||||
|
|
||||||
public override int OutputCount { get; set; } = 1;
|
public override int OutputCount { get; set; } = 1;
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
SetOutputBit(GetInputBit(0) == 1 ? 0 : 1, 0);
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Not);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ internal class OrGate : LogicGate
|
|||||||
|
|
||||||
public override int OutputCount { get; set; } = 1;
|
public override int OutputCount { get; set; } = 1;
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
SetOutputBit(GetInputBit(0), 0);
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Or2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,11 +11,8 @@ internal class Pin : LogicGate
|
|||||||
|
|
||||||
public override int InputCount { get; set; } = 1;
|
public override int InputCount { get; set; } = 1;
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < InputCount; i++)
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Buffer);
|
||||||
{
|
|
||||||
SetOutputBit(GetInputBit(i), i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,8 @@ namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
|||||||
[LogicGateDescription("A switch is a toggleable switch that can be turned on and off.")]
|
[LogicGateDescription("A switch is a toggleable switch that can be turned on and off.")]
|
||||||
internal class Switch : LogicGate, IInteractable, IColorable
|
internal class Switch : LogicGate, IInteractable, IColorable
|
||||||
{
|
{
|
||||||
|
private ICircuitSimulator? circuitSimulator;
|
||||||
|
private int gateId;
|
||||||
private bool isPressed;
|
private bool isPressed;
|
||||||
public override int OutputCount { get; set; } = 1;
|
public override int OutputCount { get; set; } = 1;
|
||||||
|
|
||||||
@@ -28,17 +30,15 @@ internal class Switch : LogicGate, IInteractable, IColorable
|
|||||||
|
|
||||||
Color = isPressed ? Color.Green : Color.Purple;
|
Color = isPressed ? Color.Green : Color.Purple;
|
||||||
Info = isPressed ? "ON" : "OFF";
|
Info = isPressed ? "ON" : "OFF";
|
||||||
|
|
||||||
|
circuitSimulator?.SetSource(gateId, isPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
if (isPressed)
|
this.circuitSimulator = circuitSimulator;
|
||||||
{
|
|
||||||
SetOutputBit(1, 0);
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Source);
|
||||||
}
|
gateId = SimulatorGateId;
|
||||||
else
|
|
||||||
{
|
|
||||||
SetOutputBit(0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,28 +33,38 @@ internal class TestLogicGate : LogicGate, Api.Interfaces.IDrawable
|
|||||||
texture = CreateTexture(15, 15);
|
texture = CreateTexture(15, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Execute()
|
protected internal override void Register(ICircuitSimulator circuitSimulator)
|
||||||
{
|
{
|
||||||
// A
|
SimulatorGateId = circuitSimulator.AddGate(GateKind.Sink);
|
||||||
SetRangeWithSpacing(20, 24, 1, GetInputBit(0) == 1 ? Color.Red : Color.Gray);
|
|
||||||
|
|
||||||
// B
|
// Watch all 7 inputs to update display
|
||||||
SetRangeWithSpacing(40, 100, 15, GetInputBit(1) == 1 ? Color.Red : Color.Gray);
|
circuitSimulator.WatchGate(SimulatorGateId, (oldMask, newMask) =>
|
||||||
|
{
|
||||||
|
// Extract each bit from the input mask
|
||||||
|
bool inputA = (newMask & (1 << 0)) != 0;
|
||||||
|
bool inputB = (newMask & (1 << 1)) != 0;
|
||||||
|
bool inputC = (newMask & (1 << 2)) != 0;
|
||||||
|
bool inputD = (newMask & (1 << 3)) != 0;
|
||||||
|
bool inputE = (newMask & (1 << 4)) != 0;
|
||||||
|
bool inputF = (newMask & (1 << 5)) != 0;
|
||||||
|
bool inputG = (newMask & (1 << 6)) != 0;
|
||||||
|
|
||||||
// C
|
// Update 7-segment display
|
||||||
SetRangeWithSpacing(130, 190, 15, GetInputBit(2) == 1 ? Color.Red : Color.Gray);
|
// A
|
||||||
|
SetRangeWithSpacing(20, 24, 1, inputA ? Color.Red : Color.Gray);
|
||||||
// D
|
// B
|
||||||
SetRangeWithSpacing(200, 204, 1, GetInputBit(3) == 1 ? Color.Red : Color.Gray);
|
SetRangeWithSpacing(40, 100, 15, inputB ? Color.Red : Color.Gray);
|
||||||
|
// C
|
||||||
// E
|
SetRangeWithSpacing(130, 190, 15, inputC ? Color.Red : Color.Gray);
|
||||||
SetRangeWithSpacing(124, 184, 15, GetInputBit(4) == 1 ? Color.Red : Color.Gray);
|
// D
|
||||||
|
SetRangeWithSpacing(200, 204, 1, inputD ? Color.Red : Color.Gray);
|
||||||
// F
|
// E
|
||||||
SetRangeWithSpacing(34, 94, 15, GetInputBit(5) == 1 ? Color.Red : Color.Gray);
|
SetRangeWithSpacing(124, 184, 15, inputE ? Color.Red : Color.Gray);
|
||||||
|
// F
|
||||||
// G
|
SetRangeWithSpacing(34, 94, 15, inputF ? Color.Red : Color.Gray);
|
||||||
SetRangeWithSpacing(110, 114, 1, GetInputBit(6) == 1 ? Color.Red : Color.Gray);
|
// G
|
||||||
|
SetRangeWithSpacing(110, 114, 1, inputG ? Color.Red : Color.Gray);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetRangeWithSpacing(int start, int end, int spacing, Color value)
|
private void SetRangeWithSpacing(int start, int end, int spacing, Color value)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RollForward>Major</RollForward>
|
<RollForward>Major</RollForward>
|
||||||
<PublishReadyToRun>false</PublishReadyToRun>
|
<PublishReadyToRun>false</PublishReadyToRun>
|
||||||
<TieredCompilation>false</TieredCompilation>
|
<TieredCompilation>false</TieredCompilation>
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\StoneRed.LogicSimulator.Api\StoneRed.LogicSimulator.Api.csproj" />
|
<ProjectReference Include="..\StoneRed.LogicSimulator.Api\StoneRed.LogicSimulator.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\StoneRed.LogicSimulator.Simulation\StoneRed.LogicSimulator.Simulation.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="UserInterface\Windows\SettingsWindow.cs">
|
<Compile Update="UserInterface\Windows\SettingsWindow.cs">
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ internal class WorldScreen : SrlsScreen<Grid>
|
|||||||
// Draw logic gate connections
|
// Draw logic gate connections
|
||||||
foreach (LogicGateConnection connection in logicGate.LogicGateConnections)
|
foreach (LogicGateConnection connection in logicGate.LogicGateConnections)
|
||||||
{
|
{
|
||||||
Color lineColor = logicGate.GetCachedOutputBit(connection.OutputIndex) == 1 ? Color.Red : Color.LightBlue;
|
Color lineColor = simulator.GetGateOutput(logicGate) ? Color.Red : Color.LightBlue;
|
||||||
srls.SpriteBatch.DrawLine((logicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), (connection.LogicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), lineColor, 5 * srls.Scale, hideCablesBehindGatesCheckBox.IsChecked ? 0.5f : 0.1f);
|
srls.SpriteBatch.DrawLine((logicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), (connection.LogicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), lineColor, 5 * srls.Scale, hideCablesBehindGatesCheckBox.IsChecked ? 0.5f : 0.1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,6 +423,7 @@ internal class WorldScreen : SrlsScreen<Grid>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simulator.LogicGatesUpdated();
|
||||||
connectionContext = null;
|
connectionContext = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user