From 86452af2d48e2d1883dbfd67316e371f2a593bfe Mon Sep 17 00:00:00 2001
From: Melodi <62381002+Melodi17@users.noreply.github.com>
Date: Mon, 4 Aug 2025 00:58:48 +1000
Subject: Initial commit
---
.DS_Store | Bin 0 -> 8196 bytes
.config/dotnet-tools.json | 36 +++++
.fleet/run.json | 11 ++
.gitattributes | 2 +
.gitignore | 5 +
.idea/.idea.Chip8Emu2/.idea/.gitignore | 13 ++
.idea/.idea.Chip8Emu2/.idea/.name | 1 +
.idea/.idea.Chip8Emu2/.idea/encodings.xml | 4 +
.idea/.idea.Chip8Emu2/.idea/indexLayout.xml | 8 +
.idea/.idea.Chip8Emu2/.idea/vcs.xml | 6 +
.idea/.idea.Q1Emu/.idea/.gitignore | 13 ++
.idea/.idea.Q1Emu/.idea/encodings.xml | 4 +
.idea/.idea.Q1Emu/.idea/indexLayout.xml | 8 +
.idea/.idea.Q1Emu/.idea/misc.xml | 10 ++
.idea/.idea.Q1Emu/.idea/vcs.xml | 6 +
Assembler/Assembler.cs | 106 +++++++++++++
Chip/Bus.cs | 55 +++++++
Chip/BusDevice.cs | 50 +++++++
Chip/DisplayDevice.cs | 76 ++++++++++
Chip/Q1Cpu.cs | 152 +++++++++++++++++++
Chip/Q1CpuAddressingModes.cs | 150 +++++++++++++++++++
Chip/Q1CpuInstructions.cs | 225 ++++++++++++++++++++++++++++
Chip/RamDevice.cs | 49 ++++++
Chip8Emu2.sln.DotSettings.user | 2 +
Content/Content.mgcb | 15 ++
DisplayEngine.cs | 72 +++++++++
GlobalUsings.cs | 12 ++
Icon.bmp | Bin 0 -> 262282 bytes
Icon.ico | Bin 0 -> 147541 bytes
Make.cs | 48 ++++++
Program.cs | 76 ++++++++++
Q1Emu.csproj | 38 +++++
Q1Emu.sln | 16 ++
Q1Emu.sln.DotSettings.user | 2 +
ROMs/.DS_Store | Bin 0 -> 6148 bytes
ROMs/test.q1 | Bin 0 -> 10 bytes
app.manifest | 43 ++++++
37 files changed, 1314 insertions(+)
create mode 100644 .DS_Store
create mode 100644 .config/dotnet-tools.json
create mode 100644 .fleet/run.json
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 .idea/.idea.Chip8Emu2/.idea/.gitignore
create mode 100644 .idea/.idea.Chip8Emu2/.idea/.name
create mode 100644 .idea/.idea.Chip8Emu2/.idea/encodings.xml
create mode 100644 .idea/.idea.Chip8Emu2/.idea/indexLayout.xml
create mode 100644 .idea/.idea.Chip8Emu2/.idea/vcs.xml
create mode 100644 .idea/.idea.Q1Emu/.idea/.gitignore
create mode 100644 .idea/.idea.Q1Emu/.idea/encodings.xml
create mode 100644 .idea/.idea.Q1Emu/.idea/indexLayout.xml
create mode 100644 .idea/.idea.Q1Emu/.idea/misc.xml
create mode 100644 .idea/.idea.Q1Emu/.idea/vcs.xml
create mode 100644 Assembler/Assembler.cs
create mode 100644 Chip/Bus.cs
create mode 100644 Chip/BusDevice.cs
create mode 100644 Chip/DisplayDevice.cs
create mode 100644 Chip/Q1Cpu.cs
create mode 100644 Chip/Q1CpuAddressingModes.cs
create mode 100644 Chip/Q1CpuInstructions.cs
create mode 100644 Chip/RamDevice.cs
create mode 100644 Chip8Emu2.sln.DotSettings.user
create mode 100644 Content/Content.mgcb
create mode 100644 DisplayEngine.cs
create mode 100644 GlobalUsings.cs
create mode 100644 Icon.bmp
create mode 100644 Icon.ico
create mode 100644 Make.cs
create mode 100644 Program.cs
create mode 100644 Q1Emu.csproj
create mode 100644 Q1Emu.sln
create mode 100644 Q1Emu.sln.DotSettings.user
create mode 100644 ROMs/.DS_Store
create mode 100644 ROMs/test.q1
create mode 100644 app.manifest
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..7ae4f61
Binary files /dev/null and b/.DS_Store differ
diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 0000000..84ff03d
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,36 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-mgcb": {
+ "version": "3.8.2.1105",
+ "commands": [
+ "mgcb"
+ ]
+ },
+ "dotnet-mgcb-editor": {
+ "version": "3.8.2.1105",
+ "commands": [
+ "mgcb-editor"
+ ]
+ },
+ "dotnet-mgcb-editor-linux": {
+ "version": "3.8.2.1105",
+ "commands": [
+ "mgcb-editor-linux"
+ ]
+ },
+ "dotnet-mgcb-editor-windows": {
+ "version": "3.8.2.1105",
+ "commands": [
+ "mgcb-editor-windows"
+ ]
+ },
+ "dotnet-mgcb-editor-mac": {
+ "version": "3.8.2.1105",
+ "commands": [
+ "mgcb-editor-mac"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.fleet/run.json b/.fleet/run.json
new file mode 100644
index 0000000..5ae6c96
--- /dev/null
+++ b/.fleet/run.json
@@ -0,0 +1,11 @@
+{
+ "configurations": [
+ {
+ "type": "dotnet",
+ "name": "Dotnet configuration",
+ "projectPath": "$PROJECT_DIR$",
+ "solutionPath": "$PROJECT_DIR$",
+ "args": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..add57be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
\ No newline at end of file
diff --git a/.idea/.idea.Chip8Emu2/.idea/.gitignore b/.idea/.idea.Chip8Emu2/.idea/.gitignore
new file mode 100644
index 0000000..f0180a3
--- /dev/null
+++ b/.idea/.idea.Chip8Emu2/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/contentModel.xml
+/projectSettingsUpdater.xml
+/.idea.Chip8Emu2.iml
+/modules.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.Chip8Emu2/.idea/.name b/.idea/.idea.Chip8Emu2/.idea/.name
new file mode 100644
index 0000000..95e8088
--- /dev/null
+++ b/.idea/.idea.Chip8Emu2/.idea/.name
@@ -0,0 +1 @@
+Chip8Emu2
\ No newline at end of file
diff --git a/.idea/.idea.Chip8Emu2/.idea/encodings.xml b/.idea/.idea.Chip8Emu2/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.Chip8Emu2/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Chip8Emu2/.idea/indexLayout.xml b/.idea/.idea.Chip8Emu2/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.Chip8Emu2/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Chip8Emu2/.idea/vcs.xml b/.idea/.idea.Chip8Emu2/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.Chip8Emu2/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Q1Emu/.idea/.gitignore b/.idea/.idea.Q1Emu/.idea/.gitignore
new file mode 100644
index 0000000..99ea8f4
--- /dev/null
+++ b/.idea/.idea.Q1Emu/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/.idea.Q1Emu.iml
+/projectSettingsUpdater.xml
+/modules.xml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.Q1Emu/.idea/encodings.xml b/.idea/.idea.Q1Emu/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.Q1Emu/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Q1Emu/.idea/indexLayout.xml b/.idea/.idea.Q1Emu/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.Q1Emu/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Q1Emu/.idea/misc.xml b/.idea/.idea.Q1Emu/.idea/misc.xml
new file mode 100644
index 0000000..5207dea
--- /dev/null
+++ b/.idea/.idea.Q1Emu/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Q1Emu/.idea/vcs.xml b/.idea/.idea.Q1Emu/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.Q1Emu/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assembler/Assembler.cs b/Assembler/Assembler.cs
new file mode 100644
index 0000000..14a33fd
--- /dev/null
+++ b/Assembler/Assembler.cs
@@ -0,0 +1,106 @@
+namespace Q1Emu.Assembler;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+public class Assembler
+{
+ public readonly BinaryWriter Writer;
+ public readonly StringReader Reader;
+
+ public Assembler(StringReader reader, BinaryWriter writer)
+ {
+ this.Reader = reader;
+ this.Writer = writer;
+ }
+
+ public void Assemble()
+ {
+ while (this.Reader.ReadLine() is { } line)
+ {
+ string trimmedLine = line.Trim();
+ if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith(';'))
+ continue;
+
+ this.ParseInstruction(trimmedLine);
+ }
+ }
+
+ private void ParseInstruction(string instruction)
+ {
+ string[] parts = instruction.Split(' ', 2);
+ string opcode = parts[0].ToUpperInvariant();
+
+ parts = parts.Length > 1 ? parts[1].Split(',') : [];
+ if (parts.Length > 2)
+ throw new ArgumentException($"Invalid instruction format: {instruction}");
+
+ u8 m1 = 0, m2 = 0;
+ if (parts.Length > 0)
+ {
+ m1 = this.ParseOperand(parts[0]);
+ if (parts.Length > 1)
+ m2 = this.ParseOperand(parts[1]);
+ }
+
+ switch (opcode)
+ {
+ case "NOP":
+ this.Writer.Write((u8) 0x00);
+ break;
+
+ default:
+ throw new NotSupportedException($"Unsupported opcode: {opcode}");
+ }
+ }
+
+ private u8 ParseOperand(string operand)
+ {
+ operand = operand.Trim();
+
+ bool IsRegister(string text)
+ {
+ if (text.Length > 1 && text[0] == 'V')
+ return true;
+
+ string[] hardcodedRegisters =
+ [
+ "AX",
+ "AH",
+ "AL",
+ "DX",
+ "DH",
+ "DL",
+ "LX",
+ "PC",
+ "SP",
+ ];
+
+ return hardcodedRegisters.Contains(text);
+ }
+
+ if (operand.StartsWith("@"))
+ {
+ return 0x03; // Indirect
+ }
+
+ if (operand.StartsWith("[") && operand.EndsWith("]"))
+ {
+ string inner = operand[1..^1].Trim();
+ if (inner.Contains("+"))
+ return 0x07; // Register indirect with offset
+
+ if (IsRegister(inner))
+ return 0x06; // Register indirect
+
+ return 0x02; // Direct
+ }
+
+ if (IsRegister(operand))
+ return 0x05; // Register
+
+ return 0x01; // Immediate
+ }
+}
\ No newline at end of file
diff --git a/Chip/Bus.cs b/Chip/Bus.cs
new file mode 100644
index 0000000..e8cf5ad
--- /dev/null
+++ b/Chip/Bus.cs
@@ -0,0 +1,55 @@
+namespace Q1Emu.Chip;
+
+using System.Collections.Generic;
+using System.Linq;
+
+public class Bus : BusDevice
+{
+ public List Devices;
+
+ public Bus(u16 addressableStart, u16 addressableEnd) : base(addressableStart, addressableEnd)
+ {
+ this.Devices = [];
+ }
+ private BusDevice? GetDevice(u16 address)
+ {
+ BusDevice? device = this.Devices.FirstOrDefault(x => address >= x.AddressableStart && address < x.AddressableEnd);
+ return device;
+ }
+
+ public override u8 Read(u16 address)
+ {
+ BusDevice? device = GetDevice(address);
+ if (device == null)
+ return 0;
+
+ return device.Read(address);
+ }
+
+ public override void Write(u16 address, u8 value)
+ {
+ BusDevice? device = GetDevice(address);
+ if (device == null)
+ return;
+
+ device.Write(address, value);
+ }
+
+ public override ref8 ReadSeq(u16 address, u16 length)
+ {
+ BusDevice? device = this.GetDevice(address);
+ if (device == null)
+ return [];
+
+ return device.ReadSeq(address, length);
+ }
+
+ public override void WriteSeq(u16 address, u16 length, ref8 value)
+ {
+ BusDevice? device = this.GetDevice(address);
+ if (device == null)
+ return;
+
+ device.WriteSeq(address, length, value);
+ }
+}
\ No newline at end of file
diff --git a/Chip/BusDevice.cs b/Chip/BusDevice.cs
new file mode 100644
index 0000000..e1f559c
--- /dev/null
+++ b/Chip/BusDevice.cs
@@ -0,0 +1,50 @@
+namespace Q1Emu.Chip;
+
+using System.Runtime.CompilerServices;
+
+public abstract class BusDevice
+{
+ public BusDevice(u16 addressableStart, u16 addressableEnd)
+ {
+ this.AddressableStart = addressableStart;
+ this.AddressableEnd = addressableEnd;
+ }
+
+ public Bus Bus { get; set; }
+ public abstract u8 Read(u16 address);
+ public abstract void Write(u16 address, u8 value);
+
+ public abstract ref8 ReadSeq(u16 address, u16 length);
+ public abstract void WriteSeq(u16 address, u16 length, ref8 value);
+
+ public u16 ReadWord(u16 address)
+ {
+ ref8 data = this.ReadSeq(address, 2);
+ u16 value = Make.u16(data);
+ return value;
+ }
+
+ public void WriteWord(u16 address, u16 value)
+ {
+ ref8 data = Make.ref8(value);
+ this.WriteSeq(address, 2, data);
+ }
+
+ public virtual void Clock() { }
+ public virtual void Reset() { }
+
+ public void AttachToBus(Bus bus)
+ {
+ this.Bus = bus;
+ bus.Devices.Add(this);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected u16 GetRelativeAddress(u16 address)
+ {
+ return (u16) (address - this.AddressableStart);
+ }
+
+ public u16 AddressableStart { get; }
+ public u16 AddressableEnd { get; }
+}
\ No newline at end of file
diff --git a/Chip/DisplayDevice.cs b/Chip/DisplayDevice.cs
new file mode 100644
index 0000000..ea34cfb
--- /dev/null
+++ b/Chip/DisplayDevice.cs
@@ -0,0 +1,76 @@
+namespace Q1Emu.Chip;
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+public class DisplayDevice : RamDevice
+{
+ public Texture2D? OutputTexture;
+ private readonly u16 _paletteSize;
+ private readonly u16 _displayWidth;
+ private readonly u16 _displayHeight;
+
+ public DisplayDevice(u16 paletteSize, u16 displayWidth, u16 displayHeight, u16 addressableStart)
+ : base(addressableStart, DisplayDevice.DetermineSize(paletteSize, displayWidth, displayHeight))
+ {
+ this._paletteSize = paletteSize;
+ this._displayWidth = displayWidth;
+ this._displayHeight = displayHeight;
+ }
+
+ private static u16 DetermineSize(u16 paletteSize, u16 displayWidth, u16 displayHeight)
+ {
+ return (u16) ((paletteSize * 3) + (displayWidth * displayHeight));
+ }
+
+ public override void Clock()
+ {
+ Color[] palette = this.ReadPalette();
+ ref8 pixelData = this.ReadSeq((u16) (this.AddressableStart + this._paletteSize * 3), (u16) (this._displayWidth * this._displayHeight));
+
+ Color[] textureData = new Color[this._displayWidth * this._displayHeight];
+ for (u16 i = 0; i < textureData.Length; i++)
+ {
+ u8 pixelValue = pixelData[i];
+ textureData[i] = palette[pixelValue];
+ }
+
+ if (this.OutputTexture != null)
+ this.OutputTexture.SetData(textureData);
+ }
+
+ private Color[] ReadPalette()
+ {
+ ref8 paletteData = this.ReadSeq(this.AddressableStart, (u16) (this._paletteSize * 3));
+ Color[] colors = new Color[this._paletteSize + 1];
+ colors[0] = Color.Transparent;
+
+ for (u16 i = 0; i < this._paletteSize; i++)
+ {
+ u16 offset = (u16) (i * 3);
+ byte r = paletteData[offset];
+ byte g = paletteData[offset + 1];
+ byte b = paletteData[offset + 2];
+ colors[(u16) (i + 1)] = new Color(r, g, b);
+ }
+
+ return colors;
+ }
+
+ public void SetPalette(Color[] palette)
+ {
+ if (palette.Length != this._paletteSize)
+ throw new ArgumentException($"Palette must have {this._paletteSize} colors.");
+
+ ref8 paletteData = this.ReadSeq(this.AddressableStart, (u16) (this._paletteSize * 3));
+ for (u16 i = 0; i < this._paletteSize; i++)
+ {
+ u16 offset = (u16) (i * 3);
+ paletteData[offset] = palette[i].R;
+ paletteData[offset + 1] = palette[i].G;
+ paletteData[offset + 2] = palette[i].B;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chip/Q1Cpu.cs b/Chip/Q1Cpu.cs
new file mode 100644
index 0000000..1124578
--- /dev/null
+++ b/Chip/Q1Cpu.cs
@@ -0,0 +1,152 @@
+using System;
+
+namespace Q1Emu.Chip;
+
+using System.Collections.Generic;
+
+public partial class Q1Cpu
+{
+ public const u16 StackStart = 0x30;
+ public const u16 StackLength = 16 * 2; // 16 16-bit values
+
+ public Random Random;
+
+ public u16 AX = 0; // Accumulator register
+ public u16 DX = 0; // Data register
+ public u16 LX = 0; // Logical register
+
+ public u16 SP { get; set; } // Stack pointer
+ public u16 PC { get; set; } // Program counter
+
+ public u16[] V { get; set; } // Variable registers (64 total)
+ public u16 I { get; set; } // Index register
+ public u16 Cycles { get; set; } // Number of cycles
+
+ public Bus Bus { get; set; }
+
+ public Q1Cpu()
+ {
+ this.LoadInstructionLookup();
+
+ this.Reset();
+ }
+
+ public void Reset()
+ {
+ this.V = new u16[16];
+
+ this.SP = StackStart;
+
+ this.I = 0;
+ }
+
+ public void Clock()
+ {
+ u16 instruction = this.FetchInstruction(out u8 op, out u8 m1, out u8 m2, out _);
+
+ bool ex = m2 > 0;
+ switch (m1, ex)
+ {
+ case (0x00, false):
+ this.InstructionLookup[op].Item1(); // Implicit
+ break;
+ case (> 0x00, false):
+ this.InstructionLookup[op].Item2(m1); // Simple addressing mode
+ break;
+ case (0x00, true):
+ this.InstructionLookup[op].Item3(); // Extended implicit
+ break;
+ case (> 0x00, true):
+ this.InstructionLookup[op].Item4(m1, m2); // Extended addressing mode
+ break;
+ }
+
+ this.Cycles++;
+ }
+ public u16 FetchInstruction(out u8 op, out u8 m1, out u8 m2, out u8 length)
+ {
+ u16 instruction = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ op = (u8) ((instruction >> 8) & 0xFF); // 256 opcodes
+ m1 = (u8) ((instruction >> 4) & 0x0F); // 16 modes
+ m2 = (u8) (instruction & 0x0F); // 16 modes
+
+ length = (u8) (this.AddressingModeLength(m1) + this.AddressingModeLength(m2));
+ return instruction;
+ }
+
+ private void PushStack(u16 value)
+ {
+ if (this.SP + 2 >= StackStart + StackLength)
+ throw new InvalidOperationException("Stack overflow");
+
+ this.Bus.WriteSeq(this.SP, 2, Make.ref8(value));
+ this.SP += 2;
+ }
+
+ private u16 PopStack()
+ {
+ if (this.SP - 2 < StackStart)
+ throw new InvalidOperationException("Stack underflow");
+
+ this.SP -= 2;
+ return Make.u16(this.Bus.ReadSeq(this.SP, 2));
+ }
+
+ private u16 FetchRegister(u8 regIndex)
+ {
+ return regIndex switch
+ {
+ <= 0x3F => this.V[regIndex],
+
+ 0x40 => this.AX,
+ 0x41 => (u16) (this.AX >> 8), // High byte of AX
+ 0x42 => (u16) (this.AX & 0xFF), // Low byte of AX
+
+ 0x43 => this.DX,
+ 0x44 => (u16) (this.DX >> 8), // High byte of DX
+ 0x45 => (u16) (this.DX & 0xFF), // Low byte of DX
+
+ 0x46 => this.LX,
+ 0x50 => this.PC,
+ 0x51 => this.SP,
+
+ _ => throw new InvalidOperationException($"Unknown register index: {regIndex:X2}"),
+ };
+ }
+
+ private void StoreRegister(u8 regIndex, u16 value)
+ {
+ switch (regIndex)
+ {
+ case <= 0x3F:
+ this.V[regIndex] = value;
+ break;
+
+ case 0x40:
+ this.AX = value; break;
+ case 0x41:
+ this.AX = (u16) ((this.AX & 0x00FF) | (value << 8)); break; // Set high byte
+ case 0x42:
+ this.AX = (u16) ((this.AX & 0xFF00) | (value & 0x00FF)); break; // Set low byte
+
+ case 0x43:
+ this.DX = value; break;
+ case 0x44:
+ this.DX = (u16) ((this.DX & 0x00FF) | (value << 8)); break; // Set high byte
+ case 0x45:
+ this.DX = (u16) ((this.DX & 0xFF00) | (value & 0x00FF)); break; // Set low byte
+
+ case 0x46:
+ this.LX = value; break;
+ case 0x50:
+ this.PC = value; break;
+ case 0x51:
+ this.SP = value; break;
+
+ default:
+ throw new InvalidOperationException($"Unknown register index: {regIndex:X2}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chip/Q1CpuAddressingModes.cs b/Chip/Q1CpuAddressingModes.cs
new file mode 100644
index 0000000..0d664f7
--- /dev/null
+++ b/Chip/Q1CpuAddressingModes.cs
@@ -0,0 +1,150 @@
+namespace Q1Emu.Chip;
+
+using System;
+using System.Collections.Generic;
+
+public partial class Q1Cpu
+{
+ public u16 FetchData(u8 mode)
+ {
+ return mode switch
+ {
+ 0x0 => this.AddressingMode_Implicit(),
+ 0x1 => this.AddressingMode_Immediate(),
+ 0x2 => this.AddressingMode_Direct(),
+ 0x3 => this.AddressingMode_Indirect(),
+ 0x4 => this.AddressingMode_Relative(),
+ 0x5 => this.AddressingMode_Register(),
+ 0x6 => this.AddressingMode_RegisterIndirect(),
+ 0x7 => this.AddressingMode_RegisterIndirectOffset(),
+ _ => throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}"),
+ };
+ }
+
+ public void StoreData(u8 mode, u16 value)
+ {
+ Console.WriteLine($"StoreData: mode={mode:X8}, value={value:X16}");
+ if (mode != 0x05)
+ {
+ u16 address = this.FetchData(mode);
+ Console.WriteLine($"StoreData: address={address:X16}");
+ this.Bus.WriteWord(address, value);
+ }
+ else
+ {
+ u8 regIndex = this.Bus.Read(this.PC);
+ this.PC += 1;
+
+ this.StoreRegister(regIndex, value);
+ }
+ }
+
+ public void TransformData(u8 mode, Func transform)
+ {
+ if (mode != 0x05)
+ {
+ u16 address = this.FetchData(mode);
+ u16 value = this.Bus.ReadWord(address);
+ u16 transformedValue = transform(value);
+ this.Bus.WriteWord(address, transformedValue);
+ }
+ else
+ {
+ u8 regIndex = this.Bus.Read(this.PC);
+ this.PC += 1;
+
+ u16 value = this.FetchRegister(regIndex);
+ u16 transformedValue = transform(value);
+ this.StoreRegister(regIndex, transformedValue);
+ }
+ }
+
+ public u8 AddressingModeLength(u8 mode)
+ {
+ return mode switch
+ {
+ 0x0 => 0, // Implicit
+ 0x1 => 2, // Immediate
+ 0x2 => 2, // Direct
+ 0x3 => 2, // Indirect
+ 0x4 => 2, // Relative
+ 0x5 => 1, // Register
+ 0x6 => 1, // Register indirect
+ 0x7 => 3, // Register indirect with offset
+ _ => throw new InvalidOperationException($"Unknown addressing mode: {mode:X2}"),
+ };
+ }
+
+ public u16 AddressingMode_Implicit()
+ {
+ return 0;
+ }
+
+
+ public u16 AddressingMode_Immediate()
+ {
+ u16 value = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ return value;
+ }
+
+ public u16 AddressingMode_Direct()
+ {
+ u16 address = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ u16 value = this.Bus.ReadWord(address);
+ return value;
+ }
+
+ public u16 AddressingMode_Indirect()
+ {
+ u16 address = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ u16 indirectAddress = this.Bus.ReadWord(address);
+ u16 value = this.Bus.ReadWord(indirectAddress);
+ return value;
+ }
+
+ public u16 AddressingMode_Relative()
+ {
+ u16 offset = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ u16 address = (u16) (this.PC + offset);
+ return address;
+ }
+
+ public u16 AddressingMode_Register()
+ {
+ u8 regIndex = this.Bus.Read(this.PC);
+ this.PC += 1;
+
+ return this.FetchRegister(regIndex);
+ }
+
+ public u16 AddressingMode_RegisterIndirect()
+ {
+ u8 regIndex = this.Bus.Read(this.PC);
+ this.PC += 1;
+
+ u16 address = this.FetchRegister(regIndex);
+ u16 value = this.Bus.ReadWord(address);
+ return value;
+ }
+
+ public u16 AddressingMode_RegisterIndirectOffset()
+ {
+ u8 regIndex = this.Bus.Read(this.PC);
+ this.PC += 1;
+
+ u16 offset = this.Bus.ReadWord(this.PC);
+ this.PC += 2;
+
+ u16 address = (u16) (this.FetchRegister(regIndex) + offset);
+ u16 value = this.Bus.ReadWord(address);
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/Chip/Q1CpuInstructions.cs b/Chip/Q1CpuInstructions.cs
new file mode 100644
index 0000000..8b79a1d
--- /dev/null
+++ b/Chip/Q1CpuInstructions.cs
@@ -0,0 +1,225 @@
+namespace Q1Emu.Chip;
+
+using System;
+using System.Collections.Generic;
+
+public partial class Q1Cpu
+{
+ public Dictionary, Action, Action)> InstructionLookup;
+ private void LoadInstructionLookup()
+ {
+ this.InstructionLookup = new Dictionary, Action, Action)>
+ {
+ { 0x00, (this.Instruction_NOP, this.Instruction_JMP, this.Instruction_HALT, this.Instruction_MOV) },
+ { 0x01, (this.Instruction_RET, this.Instruction_CALL, this.Instruction_SUS, this.Instruction_CMP) },
+ { 0x02, (this.Instruction_BR, this.Instruction_BZ, this.Instruction_BZ_LX, this.Instruction_LT) },
+ { 0x03, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_GT) },
+
+ { 0x04, (this.Instruction_NOT_LX, this.Instruction_NOT, this.Instruction_NOP, this.Instruction_AND) },
+ { 0x05, (this.Instruction_INV_DX, this.Instruction_INV, this.Instruction_NOP, this.Instruction_OR) },
+ { 0x06, (this.Instruction_SHL_DX, this.Instruction_SHL, this.Instruction_NOP, this.Instruction_XOR) },
+ { 0x07, (this.Instruction_SHR_DX, this.Instruction_SHR, this.Instruction_NOP, this.Instruction_NOP) },
+
+ { 0x08, (this.Instruction_INC_AX, this.Instruction_INC, this.Instruction_NOP, this.Instruction_ADD) },
+ { 0x09, (this.Instruction_DEC_AX, this.Instruction_DEC, this.Instruction_NOP, this.Instruction_SUB) },
+ { 0x0A, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_DIV) },
+ { 0x0B, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MUL) },
+ { 0x0C, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MOD) },
+ };
+ }
+ private void Instruction_ADD(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16) (v1 + v2);
+ this.AX = result;
+ }
+ private void Instruction_SUB(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16)(v1 - v2);
+ this.AX = result;
+ }
+ private void Instruction_MUL(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16) (v1 * v2);
+ this.AX = result;
+ }
+ private void Instruction_DIV(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ if (v2 == 0)
+ throw new DivideByZeroException("Cannot perform division by zero.");
+ u16 result = (u16)(v1 / v2);
+ this.AX = result;
+ }
+ private void Instruction_MOD(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ if (v2 == 0)
+ throw new DivideByZeroException("Cannot perform modulo operation with divisor zero.");
+ u16 result = (u16)(v1 % v2);
+ this.AX = result;
+ }
+ private void Instruction_INC(u8 obj)
+ {
+ this.TransformData(obj, value => (u16)(value + 1));
+ }
+ private void Instruction_DEC(u8 obj)
+ {
+ this.TransformData(obj, value => (u16)(value - 1));
+ }
+ private void Instruction_INC_AX()
+ {
+ this.AX = (u16)(this.AX + 1);
+ }
+ private void Instruction_DEC_AX()
+ {
+ this.AX = (u16)(this.AX - 1);
+ }
+ private void Instruction_SHL(u8 obj)
+ {
+ this.TransformData(obj, value => (u16)(value << 1));
+ }
+ private void Instruction_SHR(u8 obj)
+ {
+ this.TransformData(obj, value => (u16)(value >> 1));
+ }
+ private void Instruction_SHL_DX()
+ {
+ this.DX = (u16)(this.DX << 1);
+ }
+ private void Instruction_SHR_DX()
+ {
+ this.DX = (u16)(this.DX >> 1);
+ }
+ private void Instruction_XOR(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16)(v1 ^ v2);
+ this.DX = result;
+ }
+ private void Instruction_OR(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16)(v1 | v2);
+ this.DX = result;
+ }
+ private void Instruction_INV(u8 obj)
+ {
+ u16 value = this.FetchData(obj);
+ u16 result = (u16)~value;
+ this.DX = result;
+ }
+ private void Instruction_INV_DX()
+ {
+ this.DX = (u16)~this.DX;
+ }
+ private void Instruction_AND(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ u16 result = (u16)(v1 & v2);
+ this.DX = result;
+ }
+ private void Instruction_NOT(u8 obj)
+ {
+ u16 value = this.FetchData(obj);
+ u16 result = value == 0 ? (u16)1 : (u16)0;
+ this.LX = result;
+ }
+ private void Instruction_NOT_LX()
+ {
+ this.LX = this.LX == 0 ? (u16)1 : (u16)0;
+ }
+ private void Instruction_GT(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ this.LX = (u16)(v1 > v2 ? 1 : 0);
+ }
+ private void Instruction_LT(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ this.LX = (u16)(v1 < v2 ? 1 : 0);
+ }
+
+ private void Instruction_BR()
+ {
+ this.FetchInstruction(out _, out _, out _, out u8 length);
+ this.PC += length;
+ }
+ private void Instruction_BZ_LX()
+ {
+ if (this.LX == 0)
+ this.Instruction_BR();
+ }
+ private void Instruction_BZ(u8 obj)
+ {
+ u16 value = this.FetchData(obj);
+ if (value == 0)
+ this.Instruction_BR();
+ }
+ private void Instruction_CMP(u8 m1, u8 m2)
+ {
+ u16 v1 = this.FetchData(m1);
+ u16 v2 = this.FetchData(m2);
+
+ // Set LX based on comparison
+ this.LX = (u16)(v1 == v2 ? 1 : 0);
+ }
+ private void Instruction_MOV(u8 m1, u8 m2)
+ {
+ u16 value = this.FetchData(m1);
+ this.StoreData(m2, value);
+ }
+
+ public void Instruction_NOP() { }
+ public void Instruction_NOP(u8 m1) { }
+ public void Instruction_NOP(u8 m1, u8 m2) { }
+
+ public void Instruction_JMP(u8 m1)
+ {
+ this.PC = this.FetchData(m1);
+ }
+
+ public void Instruction_RET()
+ {
+ this.PC = this.PopStack();
+ }
+
+ public void Instruction_CALL(u8 m1)
+ {
+ u16 address = this.FetchData(m1);
+ this.PushStack(this.PC);
+ this.PC = address;
+ }
+
+ public void Instruction_HALT()
+ {
+ throw new NotImplementedException("Halt instruction not implemented yet.");
+ }
+
+ public void Instruction_SUS()
+ {
+ this.PC -= 2;
+ }
+}
\ No newline at end of file
diff --git a/Chip/RamDevice.cs b/Chip/RamDevice.cs
new file mode 100644
index 0000000..6811d22
--- /dev/null
+++ b/Chip/RamDevice.cs
@@ -0,0 +1,49 @@
+namespace Q1Emu.Chip;
+
+using System;
+using System.Runtime.CompilerServices;
+
+public class RamDevice : BusDevice
+{
+ public u8[] Memory;
+
+ public RamDevice(u16 start, u16 size) : base(start, (u16) (start + size))
+ {
+ this.Memory = new u8[size];
+ }
+
+ public override void Reset()
+ {
+ this.Memory = new u8[this.AddressableEnd - this.AddressableStart];
+ }
+
+ public override void Clock()
+ {
+ // No clocking needed for RAM
+ }
+
+ public override u8 Read(u16 address)
+ {
+ u16 relativeAddress = this.GetRelativeAddress(address);
+ return this.Memory[relativeAddress];
+ }
+
+ public override void Write(u16 address, u8 value)
+ {
+ u16 relativeAddress = this.GetRelativeAddress(address);
+ this.Memory[relativeAddress] = value;
+ }
+
+ public override ref8 ReadSeq(u16 address, u16 length)
+ {
+ u16 relativeAddress = this.GetRelativeAddress(address);
+ // Console.WriteLine($"{address:X4} {relativeAddress:X4} {length:X4}");
+ return new ref8(this.Memory, relativeAddress, length);
+ }
+
+ public override void WriteSeq(u16 address, u16 length, ref8 value)
+ {
+ ref8 destination = this.ReadSeq(address, length);
+ value.CopyTo(destination);
+ }
+}
\ No newline at end of file
diff --git a/Chip8Emu2.sln.DotSettings.user b/Chip8Emu2.sln.DotSettings.user
new file mode 100644
index 0000000..d66483f
--- /dev/null
+++ b/Chip8Emu2.sln.DotSettings.user
@@ -0,0 +1,2 @@
+
+ ForceIncluded
\ No newline at end of file
diff --git a/Content/Content.mgcb b/Content/Content.mgcb
new file mode 100644
index 0000000..ddc4c36
--- /dev/null
+++ b/Content/Content.mgcb
@@ -0,0 +1,15 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:DesktopGL
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+#---------------------------------- Content ---------------------------------#
+
diff --git a/DisplayEngine.cs b/DisplayEngine.cs
new file mode 100644
index 0000000..f77598f
--- /dev/null
+++ b/DisplayEngine.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using Q1Emu.Chip;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+
+namespace Q1Emu;
+
+public class DisplayEngine : Game
+{
+ private readonly Action _onScreenTextureLoaded;
+ private GraphicsDeviceManager _graphics;
+ private SpriteBatch _spriteBatch;
+
+ private Texture2D _texture;
+ public int Scaling { get; }
+ public int Width { get; }
+ public int Height { get; }
+
+ public DisplayEngine(int width, int height, int scaling, Action onScreenTextureLoaded)
+ {
+ this.Width = width;
+ this.Height = height;
+ this.Scaling = scaling;
+ this._onScreenTextureLoaded = onScreenTextureLoaded;
+
+ _graphics = new GraphicsDeviceManager(this);
+ Content.RootDirectory = "Content";
+ IsMouseVisible = true;
+ }
+
+ protected override void Initialize()
+ {
+ this._texture = new Texture2D(GraphicsDevice, Width, Height);
+ this._onScreenTextureLoaded?.Invoke(this._texture);
+ // set window size
+ _graphics.PreferredBackBufferWidth = Width * Scaling;
+ _graphics.PreferredBackBufferHeight = Height * Scaling;
+ _graphics.ApplyChanges();
+
+ base.Initialize();
+ }
+
+ protected override void LoadContent()
+ {
+ _spriteBatch = new SpriteBatch(GraphicsDevice);
+ }
+
+ protected override void Update(GameTime gameTime)
+ {
+ var state = Keyboard.GetState();
+
+ if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || state.IsKeyDown(Keys.Escape))
+ Exit();
+
+ base.Update(gameTime);
+ }
+
+ protected override void Draw(GameTime gameTime)
+ {
+ GraphicsDevice.Clear(Color.CornflowerBlue);
+
+ _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
+ // Draw the texture to the screen
+ _spriteBatch.Draw(_texture, new Rectangle(0, 0, Width * Scaling, Height * Scaling), Color.White);
+
+ _spriteBatch.End();
+
+ base.Draw(gameTime);
+ }
+}
\ No newline at end of file
diff --git a/GlobalUsings.cs b/GlobalUsings.cs
new file mode 100644
index 0000000..ff38a75
--- /dev/null
+++ b/GlobalUsings.cs
@@ -0,0 +1,12 @@
+// Integer type aliases
+global using i8 = sbyte; // -128 to 127
+global using u8 = byte; // 0 to 255
+global using i16 = short; // -32,768 to 32,767
+global using u16 = ushort; // 0 to 65,535
+global using i32 = int; // -2,147,483,648 to 2,147,483,647
+global using u32 = uint; // 0 to 4,294,967,295
+global using i64 = long; // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+global using u64 = ulong; // 0 to 18,446,744,073,709,551,615
+
+global using ref8 = System.Span;
+global using data8 = byte[];
diff --git a/Icon.bmp b/Icon.bmp
new file mode 100644
index 0000000..2b48165
Binary files /dev/null and b/Icon.bmp differ
diff --git a/Icon.ico b/Icon.ico
new file mode 100644
index 0000000..7d9dec1
Binary files /dev/null and b/Icon.ico differ
diff --git a/Make.cs b/Make.cs
new file mode 100644
index 0000000..6999c4d
--- /dev/null
+++ b/Make.cs
@@ -0,0 +1,48 @@
+namespace Q1Emu;
+
+using System;
+
+public static class Make
+{
+ public static u16 u16(u8 high, u8 low)
+ {
+ return (u16) ((high << 8) | low);
+ }
+
+ public static u16 u16(ref8 data)
+ {
+ if (data.Length < 2)
+ throw new ArgumentException("Data must contain at least 2 bytes to form a u16.");
+
+ return u16(data[0], data[1]);
+ }
+
+ public static u32 u32(u8 b1, u8 b2, u8 b3, u8 b4)
+ {
+ return (u32) ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
+ }
+
+ public static u32 u32(ref8 data)
+ {
+ if (data.Length < 4)
+ throw new ArgumentException("Data must contain at least 4 bytes to form a u32.");
+
+ return u32(data[0], data[1], data[2], data[3]);
+ }
+
+ public static ref8 ref8(u16 value)
+ {
+ u8 high = (u8) (value >> 8);
+ u8 low = (u8) (value & 0xFF);
+ return new ref8([high, low], 0, 2);
+ }
+
+ public static ref8 ref8(u32 value)
+ {
+ u8 b1 = (u8) (value >> 24);
+ u8 b2 = (u8) (value >> 16);
+ u8 b3 = (u8) (value >> 8);
+ u8 b4 = (u8) (value & 0xFF);
+ return new ref8([b1, b2, b3, b4], 0, 4);
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..fab84f7
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using Microsoft.Xna.Framework;
+using Q1Emu.Chip;
+using Q1Emu;
+
+internal class Program
+{
+ public static void Main(string[] args)
+ {
+ if (args.Length > 0)
+ {
+ string filePath = args[0];
+ if (filePath.EndsWith(".q1"))
+ {
+ Run(filePath);
+ }
+ else
+ {
+ Program.AssembleAndRun(filePath);
+ }
+ }
+ else
+ {
+ Console.WriteLine("Usage: Q1Emu ");
+ Console.WriteLine("Example: Q1Emu game.q1");
+ Console.WriteLine("Example: Q1Emu game.asm");
+ }
+ }
+ private static void AssembleAndRun(string filePath)
+ {
+
+ }
+ private static void Run(string filePath)
+ {
+ Q1Cpu cpu = new();
+ Bus bus = new(u16.MinValue, u16.MaxValue);
+ RamDevice ram = new(0x1FFF, 1024 * 8);
+ File.ReadAllBytes(filePath).CopyTo(ram.Memory, 0);
+
+ cpu.PC = ram.AddressableStart;
+
+ DisplayDevice display = new(16, 64, 32, 0x3FFF);
+ display.SetPalette([
+ Color.Red, Color.Green, Color.Blue, Color.Yellow,
+ Color.Cyan, Color.Magenta, Color.White, Color.Black,
+ Color.Gray, Color.Orange, Color.Purple, Color.Brown,
+ Color.LightGray, Color.DarkGray, Color.LightBlue, Color.LightGreen]);
+
+ cpu.Bus = bus;
+ ram.AttachToBus(bus);
+
+ int frameRate = 600;
+ Thread t = new(() =>
+ {
+ while (true)
+ {
+ cpu.Clock();
+ display.Clock();
+ Thread.Sleep(1000 / frameRate);
+ }
+ });
+ t.IsBackground = true;
+ t.Start();
+
+ using DisplayEngine game = new(64, 32, 10,
+ tex =>
+ {
+ display.OutputTexture = tex;
+ Console.WriteLine("Display texture initialized.");
+ });
+ game.Run();
+ }
+}
\ No newline at end of file
diff --git a/Q1Emu.csproj b/Q1Emu.csproj
new file mode 100644
index 0000000..f57964b
--- /dev/null
+++ b/Q1Emu.csproj
@@ -0,0 +1,38 @@
+
+
+ WinExe
+ net8.0
+ Major
+ false
+ false
+ Chip8Emu2
+ enable
+
+
+ app.manifest
+ Icon.ico
+
+
+
+
+
+
+
+ Icon.ico
+
+
+ Icon.bmp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Q1Emu.sln b/Q1Emu.sln
new file mode 100644
index 0000000..801cf19
--- /dev/null
+++ b/Q1Emu.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Q1Emu", "Q1Emu.csproj", "{04CE77A9-E0DC-4CAB-9382-5F84B4275B6B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {04CE77A9-E0DC-4CAB-9382-5F84B4275B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {04CE77A9-E0DC-4CAB-9382-5F84B4275B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {04CE77A9-E0DC-4CAB-9382-5F84B4275B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {04CE77A9-E0DC-4CAB-9382-5F84B4275B6B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Q1Emu.sln.DotSettings.user b/Q1Emu.sln.DotSettings.user
new file mode 100644
index 0000000..1f447ab
--- /dev/null
+++ b/Q1Emu.sln.DotSettings.user
@@ -0,0 +1,2 @@
+
+ ForceIncluded
\ No newline at end of file
diff --git a/ROMs/.DS_Store b/ROMs/.DS_Store
new file mode 100644
index 0000000..28ca9c8
Binary files /dev/null and b/ROMs/.DS_Store differ
diff --git a/ROMs/test.q1 b/ROMs/test.q1
new file mode 100644
index 0000000..d0c089f
Binary files /dev/null and b/ROMs/test.q1 differ
diff --git a/app.manifest b/app.manifest
new file mode 100644
index 0000000..d9c2d8d
--- /dev/null
+++ b/app.manifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true/pm
+ permonitorv2,permonitor
+
+
+
+
--
cgit v1.3.1