Some minor changes and playing about

This commit is contained in:
2026-04-16 14:40:05 +01:00
parent 92625498bf
commit 968141056b
9 changed files with 135 additions and 46 deletions

View File

@@ -1,5 +1,6 @@
using System; using System;
using Core.Interfaces; using Core.Interfaces;
using Core.Io;
namespace Core.Cpu namespace Core.Cpu
{ {
@@ -38,12 +39,12 @@ namespace Core.Cpu
// The Memory Bus // The Memory Bus
private readonly IMemory _memory; private readonly IMemory _memory;
private readonly IIoBus _ioBus; private readonly IO_Bus _simpleIoBus;
public Z80(IMemory memory, IIoBus ioBus) public Z80(IMemory memory, IO_Bus ioBus)
{ {
_memory = memory; _memory = memory;
_ioBus = ioBus; _simpleIoBus = ioBus;
Reset(); Reset();
} }
@@ -122,15 +123,7 @@ namespace Core.Cpu
// Placeholder for your hardware I/O // Placeholder for your hardware I/O
private byte ReadPort(ushort portAddress) private byte ReadPort(ushort portAddress)
{ {
// If the port is 0xFE, the ROM is asking for keyboard/tape/ULA data! return _simpleIoBus.ReadPort(portAddress);
// For now, we will return 0xFF (meaning "No keys are currently pressed")
if ((portAddress & 0xFF) == 0xFE)
{
return 0xFF;
}
// Default floating bus return
return 0xFF;
} }
public int Step() public int Step()
@@ -1086,7 +1079,7 @@ namespace Core.Cpu
// The Z80 puts 'A' on the top 8 bits, and 'n' on the bottom 8 bits of the port address // The Z80 puts 'A' on the top 8 bits, and 'n' on the bottom 8 bits of the port address
ushort portAddress = (ushort)((AF.High << 8) | portOffset); ushort portAddress = (ushort)((AF.High << 8) | portOffset);
_ioBus.Write(portAddress, AF.High); _simpleIoBus.WritePort(portAddress, AF.High);
return 11; return 11;
case 0xd5: //push bc case 0xd5: //push bc

43
Core/Io/IO_Bus.cs Normal file
View File

@@ -0,0 +1,43 @@
using System.Diagnostics;
using Core.Interfaces;
namespace Core.Io
{
public class IO_Bus
{
// 8 rows representing the Spectrum keyboard matrix. Default to 0xFF (unpressed).
public byte[] KeyboardRows = new byte[8] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
public byte ReadPort(ushort portAddress)
{
// The Spectrum ULA responds to any even port address (where the lowest bit is 0)
if ((portAddress & 0x01) == 0)
{
byte highByte = (byte)(portAddress >> 8); // The B register!
byte result = 0xFF; // Start assuming no keys are pressed
// The ROM pulls a specific bit low (0) in the high byte to request a row.
// Sometimes it pulls multiple bits low to scan multiple rows at once, so we AND the results.
if ((highByte & 0x01) == 0) result &= KeyboardRows[0]; // 0xFE: CAPS, Z, X, C, V
if ((highByte & 0x02) == 0) result &= KeyboardRows[1]; // 0xFD: A, S, D, F, G
if ((highByte & 0x04) == 0) result &= KeyboardRows[2]; // 0xFB: Q, W, E, R, T
if ((highByte & 0x08) == 0) result &= KeyboardRows[3]; // 0xF7: 1, 2, 3, 4, 5
if ((highByte & 0x10) == 0) result &= KeyboardRows[4]; // 0xEF: 0, 9, 8, 7, 6
if ((highByte & 0x20) == 0) result &= KeyboardRows[5]; // 0xDF: P, O, I, U, Y
if ((highByte & 0x40) == 0) result &= KeyboardRows[6]; // 0xBF: ENTER, L, K, J, H
if ((highByte & 0x80) == 0) result &= KeyboardRows[7]; // 0x7F: SPACE, SYM, M, N, B
// The top 3 bits (5, 6, 7) are unused by the keyboard and usually return 1 on a real Spectrum
return (byte)(result | 0xE0);
}
// Return 0xFF for unhandled ports
return 0xFF;
}
public void WritePort(ushort portAddress, byte portValue)
{
}
}
}

View File

@@ -1,20 +0,0 @@
using System.Diagnostics;
using Core.Interfaces;
namespace Core.Io
{
public class SimpleIoBus : IIoBus
{
public byte Read(ushort port)
{
// If the CPU reads an unconnected port, the Z80 usually sees 0xFF
return 0xFF;
}
public void Write(ushort port, byte value)
{
// For now, let's just log it to the Visual Studio Output window
Debug.WriteLine($"Hardware I/O Write -> Port: 0x{port:X4}, Value: 0x{value:X2}");
}
}
}

View File

@@ -35,12 +35,20 @@ namespace Core.Memory
public void CrapRAMData() public void CrapRAMData()
{ {
Random random = new Random(); Random random = new Random();
for (int i = 0x4000; i < 0x5AFF; i++) for (int i = 0x4000; i < 0xFFFF; i++)
{ {
_memory[i] = (byte)random.Next(0x00, 0xFF); _memory[i] = (byte)random.Next(0x00, 0xFF);
} }
} }
public void CleanRAMData()
{
for (int i = 0x4000; i < 0xFFFF; i++)
{
_memory[i] = 0x00;
}
}
// Load the ROM file // Load the ROM file
public void LoadRom(byte[] romData) public void LoadRom(byte[] romData)
{ {

Binary file not shown.

BIN
Desktop/48.rom.bak Normal file

Binary file not shown.

View File

@@ -53,7 +53,7 @@ namespace Desktop
private async void btnRun_Click(object sender, EventArgs e) private async void btnRun_Click(object sender, EventArgs e)
{ {
// If it is already running, this button acts as a STOP button //Stops
if (_isRunning) if (_isRunning)
{ {
_isRunning = false; _isRunning = false;
@@ -61,7 +61,7 @@ namespace Desktop
return; return;
} }
// --- NEW: Parse the Breakpoint --- //Parse the Breakpoint
if (!string.IsNullOrWhiteSpace(txtBreakpoint.Text)) if (!string.IsNullOrWhiteSpace(txtBreakpoint.Text))
{ {
if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp)) if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp))
@@ -76,18 +76,14 @@ namespace Desktop
} }
else else
{ {
_breakpoint = null; // No text means no breakpoint _breakpoint = null;
} }
// ---------------------------------
// Start the run state // Start the run state
_isRunning = true; _isRunning = true;
btnRun.Text = "Stop"; btnRun.Text = "Stop";
//Background thread
// Fire up a background thread
// Fire up a background thread
await Task.Run(() => await Task.Run(() =>
{ {
try try
@@ -111,6 +107,13 @@ namespace Desktop
// --- Execute Instruction --- // --- Execute Instruction ---
_cpu.Step(); _cpu.Step();
//if (_cpu.TotalTStates % 1000 == 0)
//{
// this.Invoke((MethodInvoker)delegate
// {
// UpdateDisplay();
// });
//}
// --- Check for End of Frame --- // --- Check for End of Frame ---
if (_cpu.TotalTStates >= nextFrameTargetTStates) if (_cpu.TotalTStates >= nextFrameTargetTStates)
@@ -164,8 +167,7 @@ namespace Desktop
} }
}); });
// Whether it stopped because of a breakpoint, a crash, or the user clicking "Stop", //update the UI when the background thread finishes
// we MUST update the UI when the background thread finishes!
btnRun.Text = "Run"; btnRun.Text = "Run";
UpdateDisplay(); UpdateDisplay();
} }

View File

@@ -48,6 +48,7 @@
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(791, 596); ClientSize = new Size(791, 596);
Controls.Add(picScreen); Controls.Add(picScreen);
KeyPreview = true;
Name = "Form1"; Name = "Form1";
Text = "Parsons Sinclair ZX Spectrum 48K - 2026"; Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
((System.ComponentModel.ISupportInitialize)picScreen).EndInit(); ((System.ComponentModel.ISupportInitialize)picScreen).EndInit();

View File

@@ -13,7 +13,7 @@ namespace Desktop
{ {
private Z80 _cpu = null!; private Z80 _cpu = null!;
private MemoryBus _memoryBus = null!; private MemoryBus _memoryBus = null!;
private SimpleIoBus _simpleIoBus = null!; private IO_Bus _simpleIoBus = null!;
// The 16 physical colors of the ZX Spectrum (ARGB format) // The 16 physical colors of the ZX Spectrum (ARGB format)
private readonly int[] SpectrumColors = new int[] private readonly int[] SpectrumColors = new int[]
@@ -50,7 +50,7 @@ namespace Desktop
try try
{ {
_memoryBus = new MemoryBus(); _memoryBus = new MemoryBus();
_simpleIoBus = new SimpleIoBus(); _simpleIoBus = new IO_Bus();
_memoryBus.CrapRAMData(); _memoryBus.CrapRAMData();
byte[] romData = RomLoader.Load("48.rom"); byte[] romData = RomLoader.Load("48.rom");
@@ -120,5 +120,67 @@ namespace Desktop
if (picScreen.Image != null) picScreen.Image.Dispose(); if (picScreen.Image != null) picScreen.Image.Dispose();
picScreen.Image = bmp; picScreen.Image = bmp;
} }
// Helper method to update the IO Bus state
private void UpdateMatrix(int row, int col, bool isPressed)
{
if (isPressed)
{
// Clear the bit to 0 (Active-Low = Pressed)
_simpleIoBus.KeyboardRows[row] &= (byte)~(1 << col);
}
else
{
// Set the bit back to 1 (Unpressed)
_simpleIoBus.KeyboardRows[row] |= (byte)(1 << col);
}
}
// Hook this to Form1's KeyDown event
protected override void OnKeyDown(KeyEventArgs e)
{
HandleKey(e.KeyCode, true);
base.OnKeyDown(e);
}
// Hook this to Form1's KeyUp event
protected override void OnKeyUp(KeyEventArgs e)
{
HandleKey(e.KeyCode, false);
base.OnKeyUp(e);
}
private void HandleKey(Keys key, bool isPressed)
{
switch (key)
{
// Row 6: ENTER, L, K, J, H
case Keys.Enter: UpdateMatrix(6, 0, isPressed); break;
case Keys.L: UpdateMatrix(6, 1, isPressed); break;
case Keys.K: UpdateMatrix(6, 2, isPressed); break;
case Keys.J: UpdateMatrix(6, 3, isPressed); break;
case Keys.H: UpdateMatrix(6, 4, isPressed); break;
// Row 7: SPACE, SYM SHIFT, M, N, B
case Keys.Space: UpdateMatrix(7, 0, isPressed); break;
case Keys.M: UpdateMatrix(7, 2, isPressed); break;
case Keys.N: UpdateMatrix(7, 3, isPressed); break;
case Keys.B: UpdateMatrix(7, 4, isPressed); break;
// Row 1: A, S, D, F, G
case Keys.A: UpdateMatrix(1, 0, isPressed); break;
case Keys.S: UpdateMatrix(1, 1, isPressed); break;
case Keys.D: UpdateMatrix(1, 2, isPressed); break;
case Keys.F: UpdateMatrix(1, 3, isPressed); break;
case Keys.G: UpdateMatrix(1, 4, isPressed); break;
// Map the rest of the alphabet and numbers here as you need them!
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
}
} }
} }