From 634681ce2bb36cd686943102a0f071cd6a09049f Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Wed, 27 Aug 2025 21:22:55 +1000 Subject: fixed bugs and revolutionized the universe --- Emulator/Chip/DebugDevice.cs | 13 --------- Emulator/Chip/DisplayDevice.cs | 50 ++++++++++++++++++++++----------- Emulator/Chip/HIDDevice.cs | 36 ++++++++++++++++++++++++ Emulator/Chip/Q1Cpu.cs | 4 +-- Emulator/DisplayEngine.cs | 63 +++++++++++++++++++++++++++++------------- Emulator/Options.cs | 2 +- Emulator/Program.cs | 18 +++++++----- Emulator/Q1Layout.cs | 22 ++++++++++----- 8 files changed, 143 insertions(+), 65 deletions(-) delete mode 100644 Emulator/Chip/DebugDevice.cs create mode 100644 Emulator/Chip/HIDDevice.cs (limited to 'Emulator') diff --git a/Emulator/Chip/DebugDevice.cs b/Emulator/Chip/DebugDevice.cs deleted file mode 100644 index 4e869bb..0000000 --- a/Emulator/Chip/DebugDevice.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Q1.Emulator.Chip; - -using System; - -public class DebugDevice(u16 addressPoint) - : BusDevice(addressPoint, (u16) (addressPoint + 2)) -{ - public override u8 Read(u16 address) => throw new Exception("DebugDevice.Read not permitted"); - public override void Write(u16 address, u8 value) - { - Console.WriteLine($"DebugDevice.Write: x{value:X4} b{value:b8}"); - } -} \ No newline at end of file diff --git a/Emulator/Chip/DisplayDevice.cs b/Emulator/Chip/DisplayDevice.cs index a18dbd4..c8889ba 100644 --- a/Emulator/Chip/DisplayDevice.cs +++ b/Emulator/Chip/DisplayDevice.cs @@ -6,31 +6,39 @@ 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; + 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)) + : base( + addressableStart, DisplayDevice.DetermineSize(paletteSize, displayWidth, displayHeight)) { - this._paletteSize = paletteSize; - this._displayWidth = displayWidth; + this._paletteSize = paletteSize; + this._displayWidth = displayWidth; this._displayHeight = displayHeight; - this.textureData = new Color[this._displayWidth * this._displayHeight]; + this.textureData = new Color[this._displayWidth * this._displayHeight]; } - private static u16 DetermineSize(u16 paletteSize, u16 displayWidth, u16 displayHeight) + public 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.Memory.AsSpan((u16) (this._paletteSize * 3), (u16) (this._displayWidth * this._displayHeight)); - // Console.WriteLine($"DisplayDevice.Clock: @{(this.AddressableStart + this._paletteSize * 3):X4} palette size={this._paletteSize} display size={this._displayWidth}x{this._displayHeight}"); + 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]; @@ -41,7 +49,7 @@ public class DisplayDevice : RamDevice this.OutputTexture.SetData(this.textureData); } - private Color[] ReadPalette() + private void ReadPalette() { ref8 paletteData = this.Memory.AsSpan(0, (u16) (this._paletteSize * 3)); Color[] colors = new Color[this._paletteSize + 1]; @@ -56,7 +64,8 @@ public class DisplayDevice : RamDevice colors[(u16) (i + 1)] = new Color(r, g, b); } - return colors; + this._paletteDirty = false; + this.palette = colors; } public void SetPalette(Color[] palette) @@ -68,9 +77,18 @@ public class DisplayDevice : RamDevice for (u16 i = 0; i < this._paletteSize; i++) { u16 offset = (u16) (i * 3); - paletteData[offset] = palette[i].R; + 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/HIDDevice.cs b/Emulator/Chip/HIDDevice.cs new file mode 100644 index 0000000..87bb2c3 --- /dev/null +++ b/Emulator/Chip/HIDDevice.cs @@ -0,0 +1,36 @@ +namespace Q1.Emulator.Chip; + +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(addr); + + if (pressed) + value |= (u8) (1 << bit); + else + value &= (u8) ~(1 << bit); + + this.Write((u16) (this.AddressableStart + addr), value); + } +} \ No newline at end of file diff --git a/Emulator/Chip/Q1Cpu.cs b/Emulator/Chip/Q1Cpu.cs index 7f60ca6..81f9908 100644 --- a/Emulator/Chip/Q1Cpu.cs +++ b/Emulator/Chip/Q1Cpu.cs @@ -47,8 +47,8 @@ public partial class Q1Cpu } 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)}"); + // if (instruction != 0x0101) + // Console.WriteLine($"CPU.Clock: {pc:X4} | {Disassembler.ParseInstruction(instruction)}"); bool ex = m2 > 0; switch (m1, ex) diff --git a/Emulator/DisplayEngine.cs b/Emulator/DisplayEngine.cs index beb2bba..e5d508e 100644 --- a/Emulator/DisplayEngine.cs +++ b/Emulator/DisplayEngine.cs @@ -1,35 +1,46 @@ 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 Action _onScreenTextureLoaded; - private readonly Action _requestUpdate; - private readonly Func _checkAbort; - private GraphicsDeviceManager _graphics; - private SpriteBatch _spriteBatch; + 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; public int Scaling { get; } public int Width { get; } public int Height { get; } - public DisplayEngine(int width, int height, int scaling, Action onScreenTextureLoaded, Action requestUpdate, Func checkAbort) + 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.Width = width; + this.Height = height; + this.Scaling = scaling; + this._hid = hid; this._onScreenTextureLoaded = onScreenTextureLoaded; - this._requestUpdate = requestUpdate; - this._checkAbort = checkAbort; + this._requestUpdate = requestUpdate; + this._checkAbort = checkAbort; - this._graphics = new GraphicsDeviceManager(this); + this._graphics = new GraphicsDeviceManager(this); this.Content.RootDirectory = "Content"; - this.IsMouseVisible = true; + this.IsMouseVisible = true; } protected override void Initialize() @@ -37,7 +48,7 @@ public class DisplayEngine : Game 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.PreferredBackBufferWidth = this.Width * this.Scaling; this._graphics.PreferredBackBufferHeight = this.Height * this.Scaling; this._graphics.ApplyChanges(); @@ -51,11 +62,20 @@ public class DisplayEngine : Game protected override void Update(GameTime gameTime) { - var state = Keyboard.GetState(); + var keyboard = Keyboard.GetState(); + var mouse = Mouse.GetState(); - if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || state.IsKeyDown(Keys.Escape) || this._checkAbort()) + 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); + + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed + || keyboard.IsKeyDown(Keys.Escape) + || this._checkAbort()) this.Exit(); - + base.Update(gameTime); } @@ -65,9 +85,14 @@ public class DisplayEngine : Game this._requestUpdate(); - this._spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); + 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.Draw( + this._texture, + new Rectangle(0, 0, this.Width * this.Scaling, this.Height * this.Scaling), + Color.White); this._spriteBatch.End(); diff --git a/Emulator/Options.cs b/Emulator/Options.cs index a42b560..f27b55d 100644 --- a/Emulator/Options.cs +++ b/Emulator/Options.cs @@ -1,6 +1,6 @@ namespace Q1.Emulator; -public class Options +public class EmulatorOptions { [CommandLine.Option('i', "input", Required = true, HelpText = "Input file to run.")] public string InputFile { get; set; } diff --git a/Emulator/Program.cs b/Emulator/Program.cs index 675b8d6..26f8c26 100644 --- a/Emulator/Program.cs +++ b/Emulator/Program.cs @@ -7,21 +7,21 @@ using Chip; using CommandLine; using Microsoft.Xna.Framework; -internal class Program +public class Program { public static void Main(string[] args) { - Parser.Default.ParseArguments(args).WithParsed(Program.Main); + Parser.Default.ParseArguments(args).WithParsed(Program.Main); } - static void Main(Options options) + 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(options.InputFile).CopyTo(rom.Memory, 0); + File.ReadAllBytes(emulatorOptions.InputFile).CopyTo(rom.Memory, 0); - DisplayDevice display = new(16, 64, 32, Q1Layout.Display); + DisplayDevice display = new(Q1Layout.DisplayPaletteSize, Q1Layout.DisplayWidth, Q1Layout.DisplayHeight, Q1Layout.Display.start); display.SetPalette([ Color.Red, Color.Green, @@ -40,6 +40,8 @@ internal class Program Color.LightBlue, Color.LightGreen ]); + + HIDDevice hid = new(Q1Layout.Hid.start); RamDevice memory = new(Q1Layout.Memory.start, Q1Layout.Memory.size); @@ -47,6 +49,7 @@ internal class Program rom.AttachToBus(bus); display.AttachToBus(bus); memory.AttachToBus(bus); + hid.AttachToBus(bus); Thread t = new(() => { @@ -65,12 +68,13 @@ internal class Program t.IsBackground = true; t.Start(); - using DisplayEngine game = new(64, 32, 10, + using DisplayEngine game = new(Q1Layout.DisplayWidth, Q1Layout.DisplayHeight, 5, + hid, tex => { display.OutputTexture = tex; Console.WriteLine("Display surface initialized."); - }, () => display.Clock(), () => cpu.AbortRequested); + }, () => display.Clock(), () => false); game.Run(); Console.WriteLine("Display surface closed. Waiting for CPU thread to finish..."); diff --git a/Emulator/Q1Layout.cs b/Emulator/Q1Layout.cs index c3ccd08..60a99c0 100644 --- a/Emulator/Q1Layout.cs +++ b/Emulator/Q1Layout.cs @@ -1,15 +1,23 @@ 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) Memory = (0x4FFF, 1024 * 16); - public static readonly u16 Display = 0x3FFF; - - - public static readonly u16 StackStart = Q1Layout.Memory.start; - public static readonly u16 StackLength = 256 * 2; // 256 16-bit values + 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