summaryrefslogtreecommitdiff
path: root/Emulator
diff options
context:
space:
mode:
Diffstat (limited to 'Emulator')
-rw-r--r--Emulator/Chip/DebugDevice.cs13
-rw-r--r--Emulator/Chip/DisplayDevice.cs50
-rw-r--r--Emulator/Chip/HIDDevice.cs36
-rw-r--r--Emulator/Chip/Q1Cpu.cs4
-rw-r--r--Emulator/DisplayEngine.cs63
-rw-r--r--Emulator/Options.cs2
-rw-r--r--Emulator/Program.cs18
-rw-r--r--Emulator/Q1Layout.cs22
8 files changed, 143 insertions, 65 deletions
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<Texture2D> _onScreenTextureLoaded;
- private readonly Action _requestUpdate;
- private readonly Func<bool> _checkAbort;
- private GraphicsDeviceManager _graphics;
- private SpriteBatch _spriteBatch;
+ private readonly HIDDevice _hid;
+ private readonly Action<Texture2D> _onScreenTextureLoaded;
+ private readonly Action _requestUpdate;
+ private readonly Func<bool> _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<Texture2D> onScreenTextureLoaded, Action requestUpdate, Func<bool> checkAbort)
+ public DisplayEngine(
+ int width,
+ int height,
+ int scaling,
+ HIDDevice hid,
+ Action<Texture2D> onScreenTextureLoaded,
+ Action requestUpdate,
+ Func<bool> 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<Options>(args).WithParsed(Program.Main);
+ Parser.Default.ParseArguments<EmulatorOptions>(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