Skip to content

Instantly share code, notes, and snippets.

@GuyInGrey
Last active June 4, 2023 03:39
Show Gist options
  • Save GuyInGrey/e9bd05cc0b1e338eecba49cb7e01d397 to your computer and use it in GitHub Desktop.
Save GuyInGrey/e9bd05cc0b1e338eecba49cb7e01d397 to your computer and use it in GitHub Desktop.
SynacorSharp
using SynacorSharp.Runtime;
namespace SynacorSharp.ConsoleApp
{
internal class Program
{
static void Main()
{
var engine = new SynacorEngine()
{
Data = SynacorEngineData.FromBinary("Challenge/challenge.bin")
};
while (!engine.Data.IsHalted)
{
engine.Step();
if (engine.Data.OutputQueue.TryDequeue(out var output))
{
Console.Write(output);
}
if (engine.Data.InputRequested)
{
var input = Console.ReadLine() + "\n";
foreach (var c in input)
{
engine.Data.InputQueue.Enqueue(c);
}
}
}
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Execution halted by script; Steps completed: {engine.Data.TotalStepsCompleted}; Pointer: {engine.Data.Pointer}");
Console.ForegroundColor = ConsoleColor.White;
}
}
}
using System.Runtime.CompilerServices;
namespace SynacorSharp.Runtime
{
public class SynacorEngine
{
public SynacorEngineData Data { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private ushort GetValue(ushort address)
{
if (address >= SynacorEngineData.Modulo + 8) { address = (ushort)(address % SynacorEngineData.Modulo); }
return address < SynacorEngineData.Modulo ? address : Data.Registers[address - SynacorEngineData.Modulo];
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private ushort LoadOpParameter(int parameterIndex, bool isNumeric)
{
var v = Data.Memory[Data.Pointer + 1 + parameterIndex];
if (isNumeric) { v = GetValue(v); }
return v;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void SetValue(ushort address, ushort value)
{
if (address >= SynacorEngineData.Modulo + 8) { address = (ushort)(address % SynacorEngineData.Modulo); }
if (address < SynacorEngineData.Modulo)
{
Data.Memory[address] = value;
}
else
{
Data.Registers[address - SynacorEngineData.Modulo] = value;
}
}
public void Step()
{
var opCode = (SynacorOpCode)Data.Memory[Data.Pointer];
ushort parameterCount = 0;
var staticPointer = false;
switch (opCode)
{
case SynacorOpCode.Halt:
Data.IsHalted = true;
staticPointer = true;
break;
case SynacorOpCode.Set:
parameterCount = 2;
SetValue(LoadOpParameter(0, false), LoadOpParameter(1, true));
break;
case SynacorOpCode.Push:
parameterCount = 1;
Data.Stack.Push(LoadOpParameter(0, true));
break;
case SynacorOpCode.Pop:
parameterCount = 1;
SetValue(LoadOpParameter(0, false), Data.Stack.Pop());
break;
case SynacorOpCode.IsEqualTo:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), LoadOpParameter(1, true) == LoadOpParameter(2, true) ? (ushort)1 : (ushort)0);
break;
case SynacorOpCode.IsGreaterThan:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), LoadOpParameter(1, true) > LoadOpParameter(2, true) ? (ushort)1 : (ushort)0);
break;
case SynacorOpCode.Jump:
parameterCount = 1;
staticPointer = true;
Data.Pointer = LoadOpParameter(0, true);
break;
case SynacorOpCode.JumpIfNonZero:
parameterCount = 2;
if (LoadOpParameter(0, true) != 0)
{
staticPointer = true;
Data.Pointer = LoadOpParameter(1, true);
}
break;
case SynacorOpCode.JumpIfZero:
parameterCount = 2;
if (LoadOpParameter(0, true) == 0)
{
staticPointer = true;
Data.Pointer = LoadOpParameter(1, true);
}
break;
case SynacorOpCode.Add:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), (ushort)((LoadOpParameter(1, true) + LoadOpParameter(2, true)) % SynacorEngineData.Modulo));
break;
case SynacorOpCode.Multiply:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), (ushort)((LoadOpParameter(1, true) * LoadOpParameter(2, true)) % SynacorEngineData.Modulo));
break;
case SynacorOpCode.Mod:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), (ushort)(LoadOpParameter(1, true) % LoadOpParameter(2, true)));
break;
case SynacorOpCode.BitwiseAnd:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), (ushort)(LoadOpParameter(1, true) & LoadOpParameter(2, true)));
break;
case SynacorOpCode.BitwiseOr:
parameterCount = 3;
SetValue(LoadOpParameter(0, false), (ushort)(LoadOpParameter(1, true) | LoadOpParameter(2, true)));
break;
case SynacorOpCode.BitwiseNot:
parameterCount = 2;
var not = ~LoadOpParameter(1, true);
if (LoadOpParameter(1, true) < SynacorEngineData.Modulo) { not -= SynacorEngineData.Modulo; }
SetValue(LoadOpParameter(0, false), (ushort)not);
break;
case SynacorOpCode.ReadMemory:
parameterCount = 2;
SetValue(LoadOpParameter(0, false), Data.Memory[LoadOpParameter(1, true)]);
break;
case SynacorOpCode.WriteMemory:
parameterCount = 2;
SetValue(LoadOpParameter(0, true), LoadOpParameter(1, true));
break;
case SynacorOpCode.Call:
parameterCount = 1;
staticPointer = true;
Data.Stack.Push((ushort)(Data.Pointer + 2));
Data.Pointer = LoadOpParameter(0, true);
break;
case SynacorOpCode.Return:
staticPointer = true;
Data.Pointer = Data.Stack.Pop();
break;
case SynacorOpCode.Output:
parameterCount = 1;
Data.OutputQueue.Enqueue((char)LoadOpParameter(0, true));
break;
case SynacorOpCode.Input:
char input;
while (!Data.InputQueue.TryDequeue(out input)) { Thread.Sleep(1); }
SetValue(LoadOpParameter(0, false), (ushort)input);
break;
case SynacorOpCode.NoOperator: break;
}
if (!staticPointer) { Data.Pointer += (ushort)(parameterCount + 1); }
var newOpCode = (SynacorOpCode)Data.Memory[Data.Pointer];
if (newOpCode == SynacorOpCode.Input && Data.InputQueue.IsEmpty)
{
Data.InputRequested = true;
}
else
{
Data.InputRequested = false;
}
Data.TotalStepsCompleted++;
}
}
}
using System.Collections.Concurrent;
namespace SynacorSharp.Runtime
{
[Serializable]
public class SynacorEngineData
{
public const ushort Modulo = 32768;
public ushort[] Memory { get; set; } = new ushort[32768 /* 15-bit range */];
public ushort[] Registers { get; set; } = new ushort[8];
public Stack<ushort> Stack { get; set; } = new();
public ushort Pointer { get; set; } = 0;
public bool IsHalted { get; set; } = false;
public int TotalStepsCompleted { get; set; } = 0;
public ConcurrentQueue<char> InputQueue { get; set; } = new();
public ConcurrentQueue<char> OutputQueue { get; set; } = new();
public bool InputRequested { get; set; } = false;
public static SynacorEngineData FromBinary(string filePath)
{
var data = new SynacorEngineData();
var bytes = File.ReadAllBytes(filePath);
for (var i = 0; i < bytes.Length; i += 2)
{
data.Memory[i / 2] = (ushort)(bytes[i] + (bytes[i + 1] * 256));
}
return data;
}
}
}
namespace SynacorSharp.Runtime
{
public enum SynacorOpCode : ushort
{
Halt = 0,
Set = 1,
Push = 2,
Pop = 3,
IsEqualTo = 4,
IsGreaterThan = 5,
Jump = 6,
JumpIfNonZero = 7,
JumpIfZero = 8,
Add = 9,
Multiply = 10,
Mod = 11,
BitwiseAnd = 12,
BitwiseOr = 13,
BitwiseNot = 14,
ReadMemory = 15,
WriteMemory = 16,
Call = 17,
Return = 18,
Output = 19,
Input = 20,
NoOperator = 21,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment