From 18868128e18d38b706e7df14e8e683e1a9739c17 Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:49:22 +1000 Subject: chore: Initial commit --- Emulator/Assembler/Disassembler.cs | 74 --------- Emulator/Chip/Bus.cs | 37 ----- Emulator/Chip/BusDevice.cs | 49 ------ Emulator/Chip/DisplayDevice.cs | 94 ------------ Emulator/Chip/GraphicsDevice.cs | 6 - Emulator/Chip/HIDDevice.cs | 49 ------ Emulator/Chip/Q1Cpu.cs | 167 -------------------- Emulator/Chip/Q1CpuAddressingModes.cs | 187 ----------------------- Emulator/Chip/Q1CpuInstructions.cs | 278 ---------------------------------- Emulator/Chip/RamDevice.cs | 33 ---- Emulator/Content/Content.mgcb | 15 -- Emulator/DisplayEngine.cs | 116 -------------- Emulator/Emulator.csproj | 32 ---- Emulator/Extensions.cs | 23 --- Emulator/GlobalUsings.cs | 12 -- Emulator/Icon.bmp | Bin 262282 -> 0 bytes Emulator/Icon.ico | Bin 147541 -> 0 bytes Emulator/Make.cs | 58 ------- Emulator/Options.cs | 7 - Emulator/Program.cs | 98 ------------ Emulator/Q1Layout.cs | 23 --- 21 files changed, 1358 deletions(-) delete mode 100644 Emulator/Assembler/Disassembler.cs delete mode 100644 Emulator/Chip/Bus.cs delete mode 100644 Emulator/Chip/BusDevice.cs delete mode 100644 Emulator/Chip/DisplayDevice.cs delete mode 100644 Emulator/Chip/GraphicsDevice.cs delete mode 100644 Emulator/Chip/HIDDevice.cs delete mode 100644 Emulator/Chip/Q1Cpu.cs delete mode 100644 Emulator/Chip/Q1CpuAddressingModes.cs delete mode 100644 Emulator/Chip/Q1CpuInstructions.cs delete mode 100644 Emulator/Chip/RamDevice.cs delete mode 100644 Emulator/Content/Content.mgcb delete mode 100644 Emulator/DisplayEngine.cs delete mode 100644 Emulator/Emulator.csproj delete mode 100644 Emulator/Extensions.cs delete mode 100644 Emulator/GlobalUsings.cs delete mode 100644 Emulator/Icon.bmp delete mode 100644 Emulator/Icon.ico delete mode 100644 Emulator/Make.cs delete mode 100644 Emulator/Options.cs delete mode 100644 Emulator/Program.cs delete mode 100644 Emulator/Q1Layout.cs (limited to 'Emulator') diff --git a/Emulator/Assembler/Disassembler.cs b/Emulator/Assembler/Disassembler.cs deleted file mode 100644 index d49d2eb..0000000 --- a/Emulator/Assembler/Disassembler.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace Q1.Emulator.Assembler; - -using System; -using System.Collections.Generic; -using System.IO; -using Chip; - -public class Disassembler -{ - public readonly StringWriter Writer; - public readonly BinaryReader Reader; - - public Disassembler(StringWriter writer, BinaryReader reader) - { - this.Writer = writer; - this.Reader = reader; - } - - private static Dictionary ModeLookup = new() - { - { 0x00, "" }, - { 0x01, "c" }, - { 0x02, "[a]" }, - { 0x03, "@[a]" }, - { 0x04, "pc + c" }, - { 0x05, "r" }, - { 0x06, "[r]" }, - { 0x07, "[r + c]" }, - }; - - public static string ParseInstruction(u16 instruction) - { - u8 op = (u8) ((instruction >> 8) & 0xFF); // 256 opcodes - u8 m1 = (u8) ((instruction >> 4) & 0x0F); // 16 modes - u8 m2 = (u8) (instruction & 0x0F); // 16 modes - - u8 i = (m1, m2) switch - { - (0x00, 0x00) => 0, // Implicit - (0x00, 0x01) => 2, // Extended implicit - (_, 0x00) => 1, // Simple addressing mode - _ => 3 // Extended addressing mode - }; - - string opcodeName = Disassembler.GetOpcodeName(op, i); - - if (i == 0 || i == 2) - return opcodeName; - - if (i == 1) - return $"{opcodeName} {Disassembler.GetModeName(m1)}"; - - if (i == 3) - return $"{opcodeName} {Disassembler.GetModeName(m1)}, {Disassembler.GetModeName(m2)}"; - - throw new InvalidOperationException($"Unknown instruction format for opcode {op:X2} with modes {m1:X2}, {m2:X2}"); - } - - private static string GetOpcodeName(u8 opcode, u8 i) - { - if (!Q1Cpu.InstructionLookupStrings.TryGetValue(opcode, out string[]? opNames)) - return $"[{opcode:X2}]"; // Fallback for unknown opcodes - - return opNames[i]; - } - - private static string GetModeName(u8 mode) - { - if (Disassembler.ModeLookup.TryGetValue(mode, out string? modeName)) - return modeName; - - return $"[{mode:X2}]"; // Fallback for unknown modes - } -} \ No newline at end of file diff --git a/Emulator/Chip/Bus.cs b/Emulator/Chip/Bus.cs deleted file mode 100644 index 0907f61..0000000 --- a/Emulator/Chip/Bus.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Q1.Emulator.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 = this.GetDevice(address); - if (device == null) - return 0; - - return device.Read(address); - } - - public override void Write(u16 address, u8 value) - { - BusDevice? device = this.GetDevice(address); - if (device == null) - return; - - device.Write(address, value); - } -} \ No newline at end of file diff --git a/Emulator/Chip/BusDevice.cs b/Emulator/Chip/BusDevice.cs deleted file mode 100644 index 1e1e972..0000000 --- a/Emulator/Chip/BusDevice.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Q1.Emulator.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 u16 ReadWord(u16 address) - { - u8 high = this.Read(address); - u8 low = this.Read((u16) (address + 1)); - u16 value = Make.u16(high, low); - return value; - } - - public void WriteWord(u16 address, u16 value) - { - ref8 data = Make.ref8(value); - this.Write(address, data[0]); - this.Write((u16) (address + 1), data[1]); - } - - 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/Emulator/Chip/DisplayDevice.cs b/Emulator/Chip/DisplayDevice.cs deleted file mode 100644 index c8889ba..0000000 --- a/Emulator/Chip/DisplayDevice.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; -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; - - private Color[] textureData; - private Color[] palette; - private bool _paletteDirty; - - 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; - this.textureData = new Color[this._displayWidth * this._displayHeight]; - } - - public static u16 DetermineSize(u16 paletteSize, u16 displayWidth, u16 displayHeight) - { - return (u16) ((paletteSize * 3) + (displayWidth * displayHeight)); - } - - public override void Clock() - { - if (this._paletteDirty) - this.ReadPalette(); - - ref8 pixelData = this.Memory.AsSpan( - (u16) (this._paletteSize * 3), (u16) (this._displayWidth * this._displayHeight)); - - for (u16 i = 0; i < this.textureData.Length; i++) - { - u8 pixelValue = pixelData[i]; - this.textureData[i] = palette[pixelValue % this._paletteSize]; - } - - if (this.OutputTexture != null) - this.OutputTexture.SetData(this.textureData); - } - - private void ReadPalette() - { - ref8 paletteData = this.Memory.AsSpan(0, (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); - } - - this._paletteDirty = false; - this.palette = colors; - } - - public void SetPalette(Color[] palette) - { - if (palette.Length != this._paletteSize) - throw new ArgumentException($"Palette must have {this._paletteSize} colors."); - - ref8 paletteData = this.Memory.AsSpan(0, (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; - } - - this._paletteDirty = true; - } - - public override u8 Read(u16 address) => base.Read(address); - public override void Write(u16 address, u8 value) - { - this._paletteDirty = true; - base.Write(address, value); - } -} \ No newline at end of file diff --git a/Emulator/Chip/GraphicsDevice.cs b/Emulator/Chip/GraphicsDevice.cs deleted file mode 100644 index 0f649ac..0000000 --- a/Emulator/Chip/GraphicsDevice.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Q1.Emulator.Chip; - -public class GraphicsDevice -{ - -} \ No newline at end of file diff --git a/Emulator/Chip/HIDDevice.cs b/Emulator/Chip/HIDDevice.cs deleted file mode 100644 index c8a7d04..0000000 --- a/Emulator/Chip/HIDDevice.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; - -public class HIDDevice : RamDevice -{ - public HIDDevice(u16 start) : base(start, HIDDevice.CalculateSize()) { } - - public static u16 CalculateSize() - { - return 4 + 256; - } - - public void SetMouse(u16 positionalIndex, bool leftDown, bool rightDown, bool middleDown) - { - this.WriteWord(this.AddressableStart, positionalIndex); - u16 buttons = (u16) ((leftDown ? 1 : 0) - | ((rightDown ? 1 : 0) << 1) - | ((middleDown ? 1 : 0) << 2)); - this.WriteWord((u16) (this.AddressableStart + 2), buttons); - } - - public void SetKeyboard(u8 scanCode, bool pressed) - { - int index = scanCode >> 3; // keyCode / 8 - int bit = scanCode & 7; // keyCode % 8 - - ushort addr = (ushort) (4 + index); - u8 value = this.Read((u16) (this.AddressableStart + addr)); - - if (pressed) - value |= (u8) (1 << bit); - else - value &= (u8) ~(1 << bit); - - this.Write((u16) (this.AddressableStart + addr), value); - } - - public bool GetKeyboard(int scanCode) - { - int index = scanCode >> 3; - int bit = scanCode & 7; - - ushort addr = (ushort) (4 + index); - byte value = this.Read((u16) (this.AddressableStart + addr)); - - return (value & (1 << bit)) != 0; - } -} \ No newline at end of file diff --git a/Emulator/Chip/Q1Cpu.cs b/Emulator/Chip/Q1Cpu.cs deleted file mode 100644 index 81f9908..0000000 --- a/Emulator/Chip/Q1Cpu.cs +++ /dev/null @@ -1,167 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; -using Assembler; - -public partial class Q1Cpu -{ - 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 Cycles { get; set; } // Number of cycles - - public Bus Bus { get; set; } - public bool AbortRequested = false; - - public Q1Cpu() - { - this.LoadInstructionLookup(); - } - - public void Reset() - { - this.AbortRequested = false; - - this.V = new u16[64]; - - this.SP = Q1Layout.StackStart; - this.PushStack(0xFFFF); - - this.PC = Q1Layout.ProgramStart; - } - - public void Clock() - { - var pc = this.PC; - if (pc == 0xFFFF) - { - this.AbortRequested = true; - return; - } - - u16 instruction = this.FetchInstruction(out u8 op, out u8 m1, out u8 m2, out _); - // if (instruction != 0x0101) - // Console.WriteLine($"CPU.Clock: {pc:X4} | {Disassembler.ParseInstruction(instruction)}"); - - 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 >= Q1Layout.StackStart + Q1Layout.StackLength) - throw new InvalidOperationException("Stack overflow"); - - this.Bus.WriteWord(this.SP, value); - this.SP += 2; - } - - private u16 PopStack() - { - if (this.SP - 2 < Q1Layout.StackStart) - throw new InvalidOperationException("Stack underflow"); - - this.SP -= 2; - return this.Bus.ReadWord(this.SP); - } - - 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/Emulator/Chip/Q1CpuAddressingModes.cs b/Emulator/Chip/Q1CpuAddressingModes.cs deleted file mode 100644 index 40020f1..0000000 --- a/Emulator/Chip/Q1CpuAddressingModes.cs +++ /dev/null @@ -1,187 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; - -public partial class Q1Cpu -{ - public (Func read, Action write, Action writeByte) Address(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}"), - }; - } - - private 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 (Func read, Action write, Action writeByte) AddressingMode_Implicit() - { - u16 Read() => throw new InvalidOperationException("Cannot read from implicit mode."); - void Write(u16 value) => throw new InvalidOperationException("Cannot write to implicit mode."); - void WriteByte(u8 value) => throw new InvalidOperationException("Cannot write byte to implicit mode."); - - return (Read, Write, WriteByte); - } - - - public (Func read, Action write, Action writeByte) AddressingMode_Immediate() - { - u16 value = this.Bus.ReadWord(this.PC); - this.PC += 2; - - u16 Read() => value; - void Write(u16 v) => throw new InvalidOperationException("Cannot write to immediate mode."); - void WriteByte(u8 v) => throw new InvalidOperationException("Cannot write byte to immediate mode."); - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_Direct() - { - u16 address = this.Bus.ReadWord(this.PC); - this.PC += 2; - - u16 Read() => this.Bus.ReadWord(address); - void Write(u16 value) => this.Bus.WriteWord(address, value); - void WriteByte(u8 value) => this.Bus.Write(address, value); - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_Indirect() - { - u16 address = this.Bus.ReadWord(this.PC); - this.PC += 2; - - u16 Read() - { - u16 indirectAddress = this.Bus.ReadWord(address); - u16 value = this.Bus.ReadWord(indirectAddress); - return value; - } - - void Write(u16 value) - { - u16 indirectAddress = this.Bus.ReadWord(address); - this.Bus.WriteWord(indirectAddress, value); - } - - void WriteByte(u8 value) - { - u16 indirectAddress = this.Bus.ReadWord(address); - this.Bus.Write(indirectAddress, value); - } - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_Relative() - { - u16 offset = this.Bus.ReadWord(this.PC); - this.PC += 2; - - u16 address = (u16) (this.PC + offset); - - u16 Read() => address; - void Write(u16 value) => throw new InvalidOperationException("Cannot write to relative mode."); - void WriteByte(u8 value) => throw new InvalidOperationException("Cannot write byte to relative mode."); - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_Register() - { - u8 regIndex = this.Bus.Read(this.PC); - this.PC += 1; - - u16 Read() => this.FetchRegister(regIndex); - void Write(u16 value) => this.StoreRegister(regIndex, value); - void WriteByte(u8 value) - { - u16 currentValue = this.FetchRegister(regIndex); - u16 newValue = (u16) ((currentValue & 0xFF00) | (value & 0x00FF)); // Update low byte - this.StoreRegister(regIndex, newValue); - } - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_RegisterIndirect() - { - u8 regIndex = this.Bus.Read(this.PC); - this.PC += 1; - - u16 Read() - { - u16 address = this.FetchRegister(regIndex); - u16 value = this.Bus.ReadWord(address); - return value; - } - - void Write(u16 value) - { - u16 address = this.FetchRegister(regIndex); - this.Bus.WriteWord(address, value); - } - - void WriteByte(u8 value) - { - u16 address = this.FetchRegister(regIndex); - this.Bus.Write(address, value); - } - - return (Read, Write, WriteByte); - } - - public (Func read, Action write, Action writeByte) AddressingMode_RegisterIndirectOffset() - { - u8 regIndex = this.Bus.Read(this.PC); - this.PC += 1; - - u16 offset = this.Bus.ReadWord(this.PC); - this.PC += 2; - - u16 Read() - { - u16 address = (u16) (this.FetchRegister(regIndex) + offset); - u16 value = this.Bus.ReadWord(address); - return value; - } - - void Write(u16 value) - { - u16 address = (u16) (this.FetchRegister(regIndex) + offset); - this.Bus.WriteWord(address, value); - } - - void WriteByte(u8 value) - { - u16 address = (u16) (this.FetchRegister(regIndex) + offset); - this.Bus.Write(address, value); - } - - return (Read, Write, WriteByte); - } -} \ No newline at end of file diff --git a/Emulator/Chip/Q1CpuInstructions.cs b/Emulator/Chip/Q1CpuInstructions.cs deleted file mode 100644 index 0041b25..0000000 --- a/Emulator/Chip/Q1CpuInstructions.cs +++ /dev/null @@ -1,278 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; -using System.Collections.Generic; - -public partial class Q1Cpu -{ - public static Dictionary InstructionLookupStrings = new() - { - { 0x00, ["NOP", "JMP", "HALT", "MOV"] }, - { 0x01, ["RET", "CALL", "SUS", "MOVB"] }, - { 0x02, ["BZ", "BZ", "BR", "NOP"] }, - { 0x03, ["NOP", "PUSH", "NOP", "NOP"] }, - { 0x04, ["NOP", "POP", "NOP", "NOP"] }, - - { 0x10, ["INV", "INV", "NOP", "AND"] }, - { 0x11, ["NOP", "NOP", "NOP", "OR"] }, - { 0x12, ["NOP", "NOP", "NOP", "XOR"] }, - { 0x13, ["NOP", "NOP", "NOP", "SHL"] }, - { 0x14, ["NOP", "NOP", "NOP", "SHR"] }, - - { 0x20, ["NOT", "NOT", "NOP", "CMP"] }, - { 0x21, ["NOP", "NOP", "NOP", "LT"] }, - { 0x22, ["NOP", "NOP", "NOP", "GT"] }, - - { 0x30, ["INC_AX", "INC", "NOP", "ADD"] }, - { 0x31, ["DEC_AX", "DEC", "NOP", "SUB"] }, - { 0x32, ["NOP", "NOP", "NOP", "DIV"] }, - { 0x33, ["NOP", "NOP", "NOP", "MUL"] }, - { 0x34, ["NOP", "NOP", "NOP", "MOD"] }, - }; - - 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_MOVB) }, - { 0x02, (this.Instruction_BZ_LX, this.Instruction_BZ, this.Instruction_BR, this.Instruction_NOP) }, - { 0x03, (this.Instruction_NOP, this.Instruction_PUSH, this.Instruction_NOP, this.Instruction_NOP) }, - { 0x04, (this.Instruction_NOP, this.Instruction_POP, this.Instruction_NOP, this.Instruction_NOP) }, - - { 0x10, (this.Instruction_INV_DX, this.Instruction_INV, this.Instruction_NOP, this.Instruction_AND) }, - { 0x11, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_OR) }, - { 0x12, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_XOR) }, - { 0x13, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_SHL) }, - { 0x14, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_SHR) }, - - { 0x20, (this.Instruction_NOT_LX, this.Instruction_NOT, this.Instruction_NOP, this.Instruction_CMP) }, - { 0x21, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_LT) }, - { 0x22, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_GT) }, - - { 0x30, (this.Instruction_INC_AX, this.Instruction_INC, this.Instruction_NOP, this.Instruction_ADD) }, - { 0x31, (this.Instruction_DEC_AX, this.Instruction_DEC, this.Instruction_NOP, this.Instruction_SUB) }, - { 0x32, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_DIV) }, - { 0x33, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MUL) }, - { 0x34, (this.Instruction_NOP, this.Instruction_NOP, this.Instruction_NOP, this.Instruction_MOD) }, - }; - } - private void Instruction_ADD(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 + v2); - this.AX = result; - } - private void Instruction_SUB(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 - v2); - this.AX = result; - } - private void Instruction_MUL(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 * v2); - this.AX = result; - } - private void Instruction_DIV(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - 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.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - 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) - { - var address = this.Address(obj); - u16 value = address.read(); - address.write((u16) (value + 1)); - } - private void Instruction_DEC(u8 obj) - { - var address = this.Address(obj); - u16 value = address.read(); - address.write((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 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - u16 shifted = (u16) (v1 << v2); - this.DX = shifted; - } - private void Instruction_SHR(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - u16 shifted = (u16) (v1 >> v2); - this.DX = shifted; - } - private void Instruction_XOR(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 ^ v2); - this.DX = result; - } - private void Instruction_OR(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 | v2); - this.DX = result; - } - private void Instruction_INV(u8 obj) - { - var address = this.Address(obj); - u16 value = address.read(); - 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.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - u16 result = (u16) (v1 & v2); - this.DX = result; - } - private void Instruction_NOT(u8 obj) - { - u16 value = this.Address(obj).read(); - 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.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - this.LX = (u16) (v1 > v2 ? 1 : 0); - } - private void Instruction_LT(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - 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.Address(obj).read(); - if (value == 0) - this.Instruction_BR(); - } - private void Instruction_CMP(u8 m1, u8 m2) - { - u16 v1 = this.Address(m1).read(); - u16 v2 = this.Address(m2).read(); - - // Set LX based on comparison - this.LX = (u16) (v1 == v2 ? 1 : 0); - } - private void Instruction_MOV(u8 m1, u8 m2) - { - u16 value = this.Address(m1).read(); - this.Address(m2).write(value); - } - - private void Instruction_MOVB(u8 m1, u8 m2) - { - u16 value = this.Address(m1).read(); - u8 low = Make.ref8(value)[1]; - this.Address(m2).writeByte(low); - } - - 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.Address(m1).read(); - } - - public void Instruction_RET() - { - this.PC = this.PopStack(); - } - - public void Instruction_CALL(u8 m1) - { - u16 address = this.Address(m1).read(); - this.PushStack(this.PC); - this.PC = address; - } - - public void Instruction_HALT() - { - this.AbortRequested = true; - } - - public void Instruction_SUS() - { - this.PC -= 2; - } - - public void Instruction_PUSH(u8 m1) - { - u16 value = this.Address(m1).read(); - this.PushStack(value); - } - - public void Instruction_POP(u8 m1) - { - u16 value = this.PopStack(); - this.Address(m1).write(value); - } -} \ No newline at end of file diff --git a/Emulator/Chip/RamDevice.cs b/Emulator/Chip/RamDevice.cs deleted file mode 100644 index a40713a..0000000 --- a/Emulator/Chip/RamDevice.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Q1.Emulator.Chip; - -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; - } -} \ No newline at end of file diff --git a/Emulator/Content/Content.mgcb b/Emulator/Content/Content.mgcb deleted file mode 100644 index ddc4c36..0000000 --- a/Emulator/Content/Content.mgcb +++ /dev/null @@ -1,15 +0,0 @@ - -#----------------------------- Global Properties ----------------------------# - -/outputDir:bin/$(Platform) -/intermediateDir:obj/$(Platform) -/platform:DesktopGL -/config: -/profile:Reach -/compress:False - -#-------------------------------- References --------------------------------# - - -#---------------------------------- Content ---------------------------------# - diff --git a/Emulator/DisplayEngine.cs b/Emulator/DisplayEngine.cs deleted file mode 100644 index 7aa8e2e..0000000 --- a/Emulator/DisplayEngine.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace Q1.Emulator; - -using System; -using System.Linq; -using Chip; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; - -public class DisplayEngine : Game -{ - private readonly HIDDevice _hid; - private readonly Action _onScreenTextureLoaded; - private readonly Action _requestUpdate; - private readonly Func _checkAbort; - private GraphicsDeviceManager _graphics; - private SpriteBatch _spriteBatch; - - private Texture2D _texture; - private Keys[]? _oldKeys = null; - public int Scaling { get; } - public int Width { get; } - public int Height { get; } - - public DisplayEngine( - int width, - int height, - int scaling, - HIDDevice hid, - Action onScreenTextureLoaded, - Action requestUpdate, - Func checkAbort) - { - this.Width = width; - this.Height = height; - this.Scaling = scaling; - this._hid = hid; - this._onScreenTextureLoaded = onScreenTextureLoaded; - this._requestUpdate = requestUpdate; - this._checkAbort = checkAbort; - - this._graphics = new GraphicsDeviceManager(this); - this.Content.RootDirectory = "Content"; - this.IsMouseVisible = true; - } - - protected override void Initialize() - { - this._texture = new Texture2D(this.GraphicsDevice, this.Width, this.Height); - this._onScreenTextureLoaded?.Invoke(this._texture); - // set window size - this._graphics.PreferredBackBufferWidth = this.Width * this.Scaling; - this._graphics.PreferredBackBufferHeight = this.Height * this.Scaling; - this._graphics.ApplyChanges(); - - base.Initialize(); - } - - protected override void LoadContent() - { - this._spriteBatch = new SpriteBatch(this.GraphicsDevice); - } - - protected override void Update(GameTime gameTime) - { - var keyboard = Keyboard.GetState(); - var mouse = Mouse.GetState(); - - u16 mousePos = (u16) (((mouse.Y / this.Scaling) * this.Width) + (mouse.X / this.Scaling)); - this._hid.SetMouse( - mousePos, - mouse.LeftButton == ButtonState.Pressed, - mouse.RightButton == ButtonState.Pressed, - mouse.MiddleButton == ButtonState.Pressed); - - Keys[] keys = keyboard.GetPressedKeys(); - foreach (Keys newKey in keys.Except(this._oldKeys ?? [])) - { - this._hid.SetKeyboard((u8) newKey, true); - } - - foreach (Keys oldKey in this._oldKeys?.Except(keys) ?? []) - { - this._hid.SetKeyboard((u8) oldKey, false); - } - - this._oldKeys = keys; - - if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed - || keyboard.IsKeyDown(Keys.Escape) - || this._checkAbort()) - this.Exit(); - - base.Update(gameTime); - } - - protected override void Draw(GameTime gameTime) - { - // GraphicsDevice.Clear(Color.CornflowerBlue); - - this._requestUpdate(); - - this._spriteBatch.Begin( - SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, - DepthStencilState.None, RasterizerState.CullNone); - // Draw the texture to the screen - this._spriteBatch.Draw( - this._texture, - new Rectangle(0, 0, this.Width * this.Scaling, this.Height * this.Scaling), - Color.White); - - this._spriteBatch.End(); - - base.Draw(gameTime); - } -} \ No newline at end of file diff --git a/Emulator/Emulator.csproj b/Emulator/Emulator.csproj deleted file mode 100644 index 8cb327c..0000000 --- a/Emulator/Emulator.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - WinExe - net8.0 - Major - false - false - Q1.Emulator - enable - - - - - - - - Icon.ico - - - Icon.bmp - - - - - - - - - - - - \ No newline at end of file diff --git a/Emulator/Extensions.cs b/Emulator/Extensions.cs deleted file mode 100644 index 3cc980d..0000000 --- a/Emulator/Extensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Q1.Emulator; - -using System.Collections.Generic; - -public static class Extensions -{ - public static IEnumerable AllIndexesOf(this IEnumerable arr, T q) - { - var indexes = new List(); - int index = 0; - - foreach (var item in arr) - { - if (EqualityComparer.Default.Equals(item, q)) - { - indexes.Add(index); - } - index++; - } - - return indexes; - } -} \ No newline at end of file diff --git a/Emulator/GlobalUsings.cs b/Emulator/GlobalUsings.cs deleted file mode 100644 index ff38a75..0000000 --- a/Emulator/GlobalUsings.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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/Emulator/Icon.bmp b/Emulator/Icon.bmp deleted file mode 100644 index 2b48165..0000000 Binary files a/Emulator/Icon.bmp and /dev/null differ diff --git a/Emulator/Icon.ico b/Emulator/Icon.ico deleted file mode 100644 index 7d9dec1..0000000 Binary files a/Emulator/Icon.ico and /dev/null differ diff --git a/Emulator/Make.cs b/Emulator/Make.cs deleted file mode 100644 index aad9644..0000000 --- a/Emulator/Make.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Q1.Emulator; - -using System; -using System.Buffers.Binary; - -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 Make.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 Make.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); - } - public static i16 i16(ref8 value) - { - return BinaryPrimitives.ReadInt16BigEndian(value); - } - - public static i16 i16(u8 high, u8 low) - { - return BinaryPrimitives.ReadInt16BigEndian(new ReadOnlySpan([high, low])); - } -} \ No newline at end of file diff --git a/Emulator/Options.cs b/Emulator/Options.cs deleted file mode 100644 index f27b55d..0000000 --- a/Emulator/Options.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Q1.Emulator; - -public class EmulatorOptions -{ - [CommandLine.Option('i', "input", Required = true, HelpText = "Input file to run.")] - public string InputFile { get; set; } -} \ No newline at end of file diff --git a/Emulator/Program.cs b/Emulator/Program.cs deleted file mode 100644 index f6fa932..0000000 --- a/Emulator/Program.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Q1.Emulator; - -using System; -using System.IO; -using System.Threading; -using Chip; -using CommandLine; -using Microsoft.Xna.Framework; - -public class Program -{ - public static void Main(string[] args) - { - Parser.Default.ParseArguments(args).WithParsed(Program.Main); - } - - public static void Main(EmulatorOptions emulatorOptions) - { - Q1Cpu cpu = new(); - Bus bus = new(u16.MinValue, u16.MaxValue); - RamDevice rom = new(Q1Layout.Rom.start, Q1Layout.Rom.size); - File.ReadAllBytes(emulatorOptions.InputFile).CopyTo(rom.Memory, 0); - - DisplayDevice display = new(Q1Layout.DisplayPaletteSize, Q1Layout.DisplayWidth, Q1Layout.DisplayHeight, Q1Layout.Display.start); - 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 - ]); - - HIDDevice hid = new(Q1Layout.Hid.start); - - RamDevice memory = new(Q1Layout.Memory.start, Q1Layout.Memory.size); - - cpu.Bus = bus; - rom.AttachToBus(bus); - display.AttachToBus(bus); - memory.AttachToBus(bus); - hid.AttachToBus(bus); - - Thread t = new(() => - { - cpu.Reset(); - while (!cpu.AbortRequested) - { - // for (int i = 0; i < 64; i++) - // cpu.Clock(); - // - // Thread.Sleep(1); - // - cpu.Clock(); - } - Console.WriteLine($"Exit code: 0x{cpu.V[0]:X4}"); - MemoryDump(memory, Q1Layout.FreeMemoryStart, (u16) (Q1Layout.Memory.start + Q1Layout.Memory.size - 1)); - }); - t.IsBackground = true; - t.Start(); - - using DisplayEngine game = new(Q1Layout.DisplayWidth, Q1Layout.DisplayHeight, 5, - hid, - tex => - { - display.OutputTexture = tex; - Console.WriteLine("Display surface initialized."); - }, () => display.Clock(), () => cpu.AbortRequested); - game.Run(); - - Console.WriteLine("Display surface closed. Waiting for CPU thread to finish..."); - t.Join(); - } - - private static void MemoryDump(RamDevice memory, u16 start, u16 end) - { - for (u16 addr = start; addr <= end; addr += 16) - { - Console.Write($"0x{addr:X4}: "); - for (u16 offset = 0; offset < 16; offset++) - { - if (addr + offset > end) break; - Console.Write($"{memory.Read((u16) (addr + offset)):X2} "); - } - Console.WriteLine(); - } - } -} \ No newline at end of file diff --git a/Emulator/Q1Layout.cs b/Emulator/Q1Layout.cs deleted file mode 100644 index 60a99c0..0000000 --- a/Emulator/Q1Layout.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Q1.Emulator; - -using Chip; - -public static class Q1Layout -{ - public static readonly u16 DisplayWidth = 128; - public static readonly u16 DisplayHeight = 64; - public static readonly u16 DisplayPaletteSize = 16; - - // Bus locations - public static readonly (u16 start, u16 size) Rom = (0x1FFF, 1024 * 8); - public static readonly (u16 start, u16 size) Hid = (0x602F, HIDDevice.CalculateSize()); - public static readonly (u16 start, u16 size) Memory = (0x6FFF, 1024 * 16); - public static readonly (u16 start, u16 size) Display = (0x3FFF, - DisplayDevice.DetermineSize(Q1Layout.DisplayPaletteSize, Q1Layout.DisplayWidth, Q1Layout.DisplayHeight)); - - - public static readonly u16 StackStart = Q1Layout.Memory.start; - public static readonly u16 StackLength = 256 * 2; // 256 16-bit values - public static readonly u16 FreeMemoryStart = (u16) (Q1Layout.StackStart + Q1Layout.StackLength); - public static readonly u16 ProgramStart = Q1Layout.Rom.start; -} \ No newline at end of file -- cgit v1.3.1