diff --git a/Parsons.Steamdeck/Parsons.Steamdeck.csproj b/Parsons.Steamdeck/Parsons.Steamdeck.csproj
new file mode 100644
index 0000000..cfa13ad
--- /dev/null
+++ b/Parsons.Steamdeck/Parsons.Steamdeck.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+ true
+ true
+ Size
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Parsons.Steamdeck/Program.cs b/Parsons.Steamdeck/Program.cs
new file mode 100644
index 0000000..20500f8
--- /dev/null
+++ b/Parsons.Steamdeck/Program.cs
@@ -0,0 +1,125 @@
+using System;
+using System.IO;
+using System.Numerics;
+using Core;
+using Raylib_cs;
+
+namespace Parsons.SteamDeck
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ // 1. Initialize Cross-Platform OpenGL Window
+ // THE FIX: Use PascalCase C# enums
+ Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
+ Raylib.InitWindow(1280, 800, "Parsons Master System"); // Native Steam Deck Resolution
+ Raylib.InitAudioDevice();
+
+ // 2. Initialize Hardware-Agnostic Audio Stream (44.1kHz, 32-bit float, Mono)
+ AudioStream audioStream = Raylib.LoadAudioStream(44100, 32, 1);
+ Raylib.PlayAudioStream(audioStream);
+
+ // 3. Boot the Core Emulator
+ SmsMachine machine = new SmsMachine();
+ RaylibAudioPlayer audioPlayer = new RaylibAudioPlayer();
+ machine.AudioProcessor.AudioDevice = audioPlayer;
+
+ // Load a ROM (Passed via command line argument, e.g. ./Parsons.SteamDeck sonic.sms)
+ if (args.Length > 0 && File.Exists(args[0]))
+ {
+ machine.LoadCartridge(File.ReadAllBytes(args[0]));
+ bool isGG = args[0].EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
+ machine.VideoProcessor.IsGameGear = isGG;
+ }
+
+ // Lock the engine loop to 60 FPS natively
+ Raylib.SetTargetFPS(60);
+
+ // 4. Prepare the Video Texture Pipeline
+ // THE FIX: Use Color.Black
+ Image img = Raylib.GenImageColor(256, 192, Color.Black);
+ Texture2D vdpTexture = Raylib.LoadTextureFromImage(img);
+ byte[] pixelBuffer = new byte[256 * 192 * 4]; // Fast RGBA translation array
+
+ // --- THE MAIN GAME LOOP ---
+ while (!Raylib.WindowShouldClose())
+ {
+ // A. OS-Agnostic Input Polling (Works on Keyboard AND Steam Deck Controller)
+ // THE FIX: PascalCase KeyboardKey and GamepadButton enums
+ byte pad = 0xFF;
+ if (Raylib.IsKeyDown(KeyboardKey.Up) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp)) pad &= 0xFE;
+ if (Raylib.IsKeyDown(KeyboardKey.Down) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown)) pad &= 0xFD;
+ if (Raylib.IsKeyDown(KeyboardKey.Left) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceLeft)) pad &= 0xFB;
+ if (Raylib.IsKeyDown(KeyboardKey.Right) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceRight)) pad &= 0xF7;
+
+ // Steam Deck "A" Button is RightFaceDown. "B" Button is RightFaceRight.
+ if (Raylib.IsKeyDown(KeyboardKey.Z) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown)) pad &= 0xEF; // B1 / A Button
+ if (Raylib.IsKeyDown(KeyboardKey.X) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceRight)) pad &= 0xDF; // B2 / B Button
+
+ machine.IoBus.Joypad1Keyboard = pad;
+
+ // B. Execute exactly one frame of T-States
+ machine.RunFrame();
+
+ // C. Flush Audio Buffer to the Sound Card
+ if (Raylib.IsAudioStreamProcessed(audioStream))
+ {
+ float[] samples = audioPlayer.GetQueuedSamples();
+ if (samples.Length > 0)
+ {
+ unsafe
+ {
+ fixed (float* p = samples)
+ {
+ Raylib.UpdateAudioStream(audioStream, p, samples.Length);
+ }
+ }
+ }
+ }
+
+ // D. Fast Pixel Translation (ARGB int -> RGBA bytes)
+ int[] frameBuffer = machine.VideoProcessor.FrameBuffer;
+ for (int i = 0; i < frameBuffer.Length; i++)
+ {
+ int argb = frameBuffer[i];
+ pixelBuffer[i * 4 + 0] = (byte)((argb >> 16) & 0xFF); // R
+ pixelBuffer[i * 4 + 1] = (byte)((argb >> 8) & 0xFF); // G
+ pixelBuffer[i * 4 + 2] = (byte)(argb & 0xFF); // B
+ pixelBuffer[i * 4 + 3] = 255; // A
+ }
+
+ // Push bytes directly to the GPU Texture
+ unsafe
+ {
+ fixed (byte* p = pixelBuffer)
+ {
+ Raylib.UpdateTexture(vdpTexture, p);
+ }
+ }
+
+ // E. Render to Screen (Perfect Aspect Ratio Scaling)
+ Raylib.BeginDrawing();
+ Raylib.ClearBackground(Color.Black);
+
+ float scale = Math.Min((float)Raylib.GetScreenWidth() / 256, (float)Raylib.GetScreenHeight() / 192);
+ Rectangle sourceRec = new Rectangle(0, 0, 256, 192);
+ Rectangle destRec = new Rectangle(
+ (Raylib.GetScreenWidth() - (256 * scale)) * 0.5f,
+ (Raylib.GetScreenHeight() - (192 * scale)) * 0.5f,
+ 256 * scale, 192 * scale);
+
+ // Draw the VDP Frame
+ Raylib.DrawTexturePro(vdpTexture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
+
+ Raylib.EndDrawing();
+ }
+
+ // Clean up hardware resources on exit
+ Raylib.UnloadTexture(vdpTexture);
+ Raylib.UnloadAudioStream(audioStream);
+ Raylib.CloseAudioDevice();
+ Raylib.CloseWindow();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Parsons.Steamdeck/RaylibAudioPlayer.cs b/Parsons.Steamdeck/RaylibAudioPlayer.cs
new file mode 100644
index 0000000..9eea779
--- /dev/null
+++ b/Parsons.Steamdeck/RaylibAudioPlayer.cs
@@ -0,0 +1,28 @@
+using Core.Interfaces;
+using System.Collections.Concurrent;
+
+namespace Parsons.SteamDeck
+{
+ public class RaylibAudioPlayer : IAudioDevice
+ {
+ private ConcurrentQueue _sampleQueue = new ConcurrentQueue();
+
+ public void AddSample(float sample)
+ {
+ // Queue the sample from the emulator core
+ _sampleQueue.Enqueue(sample);
+ }
+
+ public float[] GetQueuedSamples()
+ {
+ // Pull all pending samples out of the queue to send to the sound card
+ int count = _sampleQueue.Count;
+ float[] buffer = new float[count];
+ for (int i = 0; i < count; i++)
+ {
+ _sampleQueue.TryDequeue(out buffer[i]);
+ }
+ return buffer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ParsonsMasterSystem2026.sln b/ParsonsMasterSystem2026.sln
index 8613677..c38a4ff 100644
--- a/ParsonsMasterSystem2026.sln
+++ b/ParsonsMasterSystem2026.sln
@@ -1,12 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.14.37216.2 d17.14
+VisualStudioVersion = 17.14.37216.2
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Desktop\Desktop.csproj", "{E245F3A5-E541-43BB-B64E-B4B2BB3D8C51}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{B0A741E4-CE3E-4DF0-96B2-E314973F9201}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parsons.Steamdeck", "Parsons.Steamdeck\Parsons.Steamdeck.csproj", "{B619DF68-7EBF-4C6A-AC24-AD5250404943}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0A741E4-CE3E-4DF0-96B2-E314973F9201}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B619DF68-7EBF-4C6A-AC24-AD5250404943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B619DF68-7EBF-4C6A-AC24-AD5250404943}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B619DF68-7EBF-4C6A-AC24-AD5250404943}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B619DF68-7EBF-4C6A-AC24-AD5250404943}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE