Interrupts added at 50fps. Dummy keyboard. Ready for graphics!
This commit is contained in:
@@ -31,6 +31,16 @@ namespace Core.Memory
|
|||||||
_memory[address] = value;
|
_memory[address] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Put some initial random data into RAM for authenticity
|
||||||
|
public void CrapRAMData()
|
||||||
|
{
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0x4000; i < 0x5AFF; i++)
|
||||||
|
{
|
||||||
|
_memory[i] = (byte)random.Next(0x00, 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load the ROM file
|
// Load the ROM file
|
||||||
public void LoadRom(byte[] romData)
|
public void LoadRom(byte[] romData)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,20 +10,23 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
private readonly Z80 _cpu;
|
private readonly Z80 _cpu;
|
||||||
private readonly MemoryBus _memoryBus;
|
private readonly MemoryBus _memoryBus;
|
||||||
|
private readonly Form1 _mainForm;
|
||||||
private bool _isRunning = false;
|
private bool _isRunning = false;
|
||||||
private ushort? _breakpoint = null;
|
private ushort? _breakpoint = null;
|
||||||
|
|
||||||
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
public DebuggerForm(Z80 cpu, MemoryBus memoryBus, Form1 mainForm)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_cpu = cpu;
|
_cpu = cpu;
|
||||||
_memoryBus = memoryBus;
|
_memoryBus = memoryBus;
|
||||||
|
_mainForm = mainForm;
|
||||||
|
|
||||||
// Set default memory view address
|
// Set default memory view address
|
||||||
txtMemoryStart.Text = "0000";
|
txtMemoryStart.Text = "0000";
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
UpdateStackView();
|
UpdateStackView();
|
||||||
UpdateDisassemblyView();
|
UpdateDisassemblyView();
|
||||||
|
_mainForm = mainForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnStep_Click(object sender, EventArgs e)
|
private void btnStep_Click(object sender, EventArgs e)
|
||||||
@@ -119,8 +122,14 @@ namespace Desktop
|
|||||||
nextFrameTargetTStates += TStatesPerFrame;
|
nextFrameTargetTStates += TStatesPerFrame;
|
||||||
frameCount++;
|
frameCount++;
|
||||||
|
|
||||||
// 3. Throttle to real-time (50 frames per second = 20ms per frame)
|
//3 Render the screen
|
||||||
long targetTimeMs = frameCount * 20;
|
_mainForm.Invoke((MethodInvoker)delegate
|
||||||
|
{
|
||||||
|
_mainForm.RenderScreen();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Throttle to real-time (50 frames per second = 20ms per frame)
|
||||||
|
long targetTimeMs = frameCount * 20;
|
||||||
long elapsedMs = stopwatch.ElapsedMilliseconds;
|
long elapsedMs = stopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (elapsedMs < targetTimeMs)
|
if (elapsedMs < targetTimeMs)
|
||||||
@@ -131,7 +140,7 @@ namespace Desktop
|
|||||||
|
|
||||||
// Optional: Update the UI every 10 frames so you can watch it run safely
|
// Optional: Update the UI every 10 frames so you can watch it run safely
|
||||||
// without overwhelming the WinForms rendering engine.
|
// without overwhelming the WinForms rendering engine.
|
||||||
if (frameCount % 10 == 0)
|
if (frameCount % 1 == 0)
|
||||||
{
|
{
|
||||||
this.Invoke((MethodInvoker)delegate
|
this.Invoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
|
|||||||
18
Desktop/Form1.Designer.cs
generated
18
Desktop/Form1.Designer.cs
generated
@@ -28,18 +28,34 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
|
picScreen = new PictureBox();
|
||||||
|
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
|
// picScreen
|
||||||
|
//
|
||||||
|
picScreen.BackColor = Color.Black;
|
||||||
|
picScreen.Location = new Point(12, 12);
|
||||||
|
picScreen.Name = "picScreen";
|
||||||
|
picScreen.Size = new Size(768, 576);
|
||||||
|
picScreen.SizeMode = PictureBoxSizeMode.Zoom;
|
||||||
|
picScreen.TabIndex = 0;
|
||||||
|
picScreen.TabStop = false;
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(800, 450);
|
ClientSize = new Size(791, 596);
|
||||||
|
Controls.Add(picScreen);
|
||||||
Name = "Form1";
|
Name = "Form1";
|
||||||
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
||||||
|
((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private PictureBox picScreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Core.Cpu;
|
using Core.Cpu;
|
||||||
using Core.Io;
|
using Core.Io;
|
||||||
@@ -12,6 +15,30 @@ namespace Desktop
|
|||||||
private MemoryBus _memoryBus = null!;
|
private MemoryBus _memoryBus = null!;
|
||||||
private SimpleIoBus _simpleIoBus = null!;
|
private SimpleIoBus _simpleIoBus = null!;
|
||||||
|
|
||||||
|
// The 16 physical colors of the ZX Spectrum (ARGB format)
|
||||||
|
private readonly int[] SpectrumColors = new int[]
|
||||||
|
{
|
||||||
|
// Normal Colors (Bright = 0)
|
||||||
|
unchecked((int)0xFF000000), // 0: Black
|
||||||
|
unchecked((int)0xFF0000D7), // 1: Blue
|
||||||
|
unchecked((int)0xFFD70000), // 2: Red
|
||||||
|
unchecked((int)0xFFD700D7), // 3: Magenta
|
||||||
|
unchecked((int)0xFF00D700), // 4: Green
|
||||||
|
unchecked((int)0xFF00D7D7), // 5: Cyan
|
||||||
|
unchecked((int)0xFFD7D700), // 6: Yellow
|
||||||
|
unchecked((int)0xFFD7D7D7), // 7: White
|
||||||
|
|
||||||
|
// Bright Colors (Bright = 1)
|
||||||
|
unchecked((int)0xFF000000), // 8: Bright Black
|
||||||
|
unchecked((int)0xFF0000FF), // 9: Bright Blue
|
||||||
|
unchecked((int)0xFFFF0000), // 10: Bright Red
|
||||||
|
unchecked((int)0xFFFF00FF), // 11: Bright Magenta
|
||||||
|
unchecked((int)0xFF00FF00), // 12: Bright Green
|
||||||
|
unchecked((int)0xFF00FFFF), // 13: Bright Cyan
|
||||||
|
unchecked((int)0xFFFFFF00), // 14: Bright Yellow
|
||||||
|
unchecked((int)0xFFFFFFFF) // 15: Bright White
|
||||||
|
};
|
||||||
|
|
||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -22,27 +49,76 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. Initialize the memory bus
|
|
||||||
_memoryBus = new MemoryBus();
|
_memoryBus = new MemoryBus();
|
||||||
_simpleIoBus = new SimpleIoBus();
|
_simpleIoBus = new SimpleIoBus();
|
||||||
|
|
||||||
// 2. Load the ROM
|
_memoryBus.CrapRAMData();
|
||||||
byte[] romData = RomLoader.Load("48.rom");
|
byte[] romData = RomLoader.Load("48.rom");
|
||||||
|
|
||||||
// 3. Inject the ROM into the memory bus
|
|
||||||
_memoryBus.LoadRom(romData);
|
_memoryBus.LoadRom(romData);
|
||||||
|
|
||||||
// 4. Initialize the CPU with the populated memory
|
|
||||||
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
||||||
|
|
||||||
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus);
|
// Pass 'this' so the DebuggerForm can talk back to this main window
|
||||||
|
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this);
|
||||||
debugger.Show();
|
debugger.Show();
|
||||||
//MessageBox.Show("ROM loaded and CPU initialized successfully!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"Failed to initialize emulator:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"Failed to initialize emulator:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public so the Debugger's background thread can call it 50 times a second
|
||||||
|
public void RenderScreen()
|
||||||
|
{
|
||||||
|
int[] pixelData = new int[256 * 192];
|
||||||
|
|
||||||
|
// Loop through the 6144 bytes of Pixel RAM
|
||||||
|
for (int offset = 0; offset < 6144; offset++)
|
||||||
|
{
|
||||||
|
ushort address = (ushort)(0x4000 + offset);
|
||||||
|
byte pixels = _memoryBus.Read(address);
|
||||||
|
|
||||||
|
// Unwind the Sinclair interlace
|
||||||
|
int y = ((offset & 0x0700) >> 8) |
|
||||||
|
((offset & 0x00E0) >> 2) |
|
||||||
|
((offset & 0x1800) >> 5);
|
||||||
|
|
||||||
|
int x = (offset & 0x001F) * 8;
|
||||||
|
|
||||||
|
// Fetch Color Attributes
|
||||||
|
int attrRow = y / 8;
|
||||||
|
int attrCol = x / 8;
|
||||||
|
ushort attrAddress = (ushort)(0x5800 + (attrRow * 32) + attrCol);
|
||||||
|
byte attr = _memoryBus.Read(attrAddress);
|
||||||
|
|
||||||
|
int ink = attr & 0x07;
|
||||||
|
int paper = (attr >> 3) & 0x07;
|
||||||
|
int brightOffset = (attr & 0x40) != 0 ? 8 : 0;
|
||||||
|
|
||||||
|
int inkColor = SpectrumColors[ink + brightOffset];
|
||||||
|
int paperColor = SpectrumColors[paper + brightOffset];
|
||||||
|
|
||||||
|
// Draw the 8 pixels
|
||||||
|
for (int bit = 0; bit < 8; bit++)
|
||||||
|
{
|
||||||
|
bool isPixelSet = (pixels & (1 << (7 - bit))) != 0;
|
||||||
|
pixelData[(y * 256) + (x + bit)] = isPixelSet ? inkColor : paperColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blast it to the PictureBox
|
||||||
|
Bitmap bmp = new Bitmap(256, 192, PixelFormat.Format32bppArgb);
|
||||||
|
BitmapData bmpData = bmp.LockBits(
|
||||||
|
new Rectangle(0, 0, 256, 192),
|
||||||
|
ImageLockMode.WriteOnly,
|
||||||
|
bmp.PixelFormat);
|
||||||
|
|
||||||
|
Marshal.Copy(pixelData, 0, bmpData.Scan0, pixelData.Length);
|
||||||
|
bmp.UnlockBits(bmpData);
|
||||||
|
|
||||||
|
if (picScreen.Image != null) picScreen.Image.Dispose();
|
||||||
|
picScreen.Image = bmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user