Files
StoneRed.LogicSimulator/StoneRed.LogicSimulator/Simulation/LogicGates/Interfaces/LogicGate.cs
T

166 lines
4.1 KiB
C#

using Microsoft.Xna.Framework.Graphics;
using StoneRed.LogicSimulator.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
namespace StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
#pragma warning disable S112 // General exceptions should never be thrown
internal abstract class LogicGate
{
private readonly List<LogicGateConnection> logicGateConnections = new();
private int currentInput;
private int newInput;
private int output;
private int cachedOutput;
internal GraphicsDevice? GraphicsDevice { get; set; }
internal LogicGateWorldData WorldData { get; init; } = new LogicGateWorldData();
internal ulong Id { get; set; }
public IReadOnlyList<LogicGateConnection> LogicGateConnections => logicGateConnections.AsReadOnly();
public abstract int OutputCount { get; set; }
public abstract int InputCount { get; set; }
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)
{
if (inputIndex >= logicGate.InputCount)
{
throw new IndexOutOfRangeException();
}
if (outputIndex >= OutputCount)
{
throw new IndexOutOfRangeException();
}
lock (logicGateConnections)
{
logicGateConnections.Add(new LogicGateConnection(logicGate, inputIndex, outputIndex));
}
}
internal bool IsConnectedTo(LogicGate logicGate)
{
lock (logicGateConnections)
{
return logicGateConnections.Any(c => c.LogicGate.Id == logicGate.Id);
}
}
internal void Disconnect(LogicGate logicGate)
{
lock (logicGateConnections)
{
int index = logicGateConnections.FindIndex(c => c.LogicGate.Id == logicGate.Id);
logicGateConnections.RemoveAt(index);
}
}
internal void Update()
{
Execute();
cachedOutput = output;
PublishOutput();
}
internal int GetCachedOutputBit(int index)
{
if (index < 0 || index >= OutputCount)
{
throw new IndexOutOfRangeException();
}
return cachedOutput.GetBit(index);
}
public override bool Equals(object? obj)
{
return obj is LogicGate gate &&
Id == gate.Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
protected abstract void Execute();
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)
{
return new Texture2D(GraphicsDevice, width, height);
}
private void PublishOutput()
{
lock (logicGateConnections)
{
foreach (LogicGateConnection connection in logicGateConnections)
{
connection.LogicGate.SetInput(output.GetBit(connection.OutputIndex), connection.InputIndex);
}
}
}
}
#pragma warning restore S112 // General exceptions should never be thrown
internal class LogicGateConnection
{
public LogicGate LogicGate { get; }
public int InputIndex { get; }
public int OutputIndex { get; }
public LogicGateConnection(LogicGate logicGate, int inputIndex, int outputIndex)
{
LogicGate = logicGate;
InputIndex = inputIndex;
OutputIndex = outputIndex;
}
};