Some minor changes and playing about
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Core.Interfaces;
|
||||
using Core.Io;
|
||||
|
||||
namespace Core.Cpu
|
||||
{
|
||||
@@ -38,12 +39,12 @@ namespace Core.Cpu
|
||||
|
||||
// The Memory Bus
|
||||
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;
|
||||
_ioBus = ioBus;
|
||||
_simpleIoBus = ioBus;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -122,15 +123,7 @@ namespace Core.Cpu
|
||||
// Placeholder for your hardware I/O
|
||||
private byte ReadPort(ushort portAddress)
|
||||
{
|
||||
// If the port is 0xFE, the ROM is asking for keyboard/tape/ULA data!
|
||||
// For now, we will return 0xFF (meaning "No keys are currently pressed")
|
||||
if ((portAddress & 0xFF) == 0xFE)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// Default floating bus return
|
||||
return 0xFF;
|
||||
return _simpleIoBus.ReadPort(portAddress);
|
||||
}
|
||||
|
||||
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
|
||||
ushort portAddress = (ushort)((AF.High << 8) | portOffset);
|
||||
|
||||
_ioBus.Write(portAddress, AF.High);
|
||||
_simpleIoBus.WritePort(portAddress, AF.High);
|
||||
|
||||
return 11;
|
||||
case 0xd5: //push bc
|
||||
|
||||
43
Core/Io/IO_Bus.cs
Normal file
43
Core/Io/IO_Bus.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,20 @@ namespace Core.Memory
|
||||
public void CrapRAMData()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanRAMData()
|
||||
{
|
||||
for (int i = 0x4000; i < 0xFFFF; i++)
|
||||
{
|
||||
_memory[i] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the ROM file
|
||||
public void LoadRom(byte[] romData)
|
||||
{
|
||||
|
||||
BIN
Desktop/48.rom
BIN
Desktop/48.rom
Binary file not shown.
BIN
Desktop/48.rom.bak
Normal file
BIN
Desktop/48.rom.bak
Normal file
Binary file not shown.
@@ -53,7 +53,7 @@ namespace Desktop
|
||||
|
||||
private async void btnRun_Click(object sender, EventArgs e)
|
||||
{
|
||||
// If it is already running, this button acts as a STOP button
|
||||
//Stops
|
||||
if (_isRunning)
|
||||
{
|
||||
_isRunning = false;
|
||||
@@ -61,7 +61,7 @@ namespace Desktop
|
||||
return;
|
||||
}
|
||||
|
||||
// --- NEW: Parse the Breakpoint ---
|
||||
//Parse the Breakpoint
|
||||
if (!string.IsNullOrWhiteSpace(txtBreakpoint.Text))
|
||||
{
|
||||
if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp))
|
||||
@@ -76,18 +76,14 @@ namespace Desktop
|
||||
}
|
||||
else
|
||||
{
|
||||
_breakpoint = null; // No text means no breakpoint
|
||||
_breakpoint = null;
|
||||
}
|
||||
// ---------------------------------
|
||||
|
||||
// Start the run state
|
||||
_isRunning = true;
|
||||
btnRun.Text = "Stop";
|
||||
|
||||
|
||||
|
||||
// Fire up a background thread
|
||||
// Fire up a background thread
|
||||
//Background thread
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
@@ -111,6 +107,13 @@ namespace Desktop
|
||||
|
||||
// --- Execute Instruction ---
|
||||
_cpu.Step();
|
||||
//if (_cpu.TotalTStates % 1000 == 0)
|
||||
//{
|
||||
// this.Invoke((MethodInvoker)delegate
|
||||
// {
|
||||
// UpdateDisplay();
|
||||
// });
|
||||
//}
|
||||
|
||||
// --- Check for End of Frame ---
|
||||
if (_cpu.TotalTStates >= nextFrameTargetTStates)
|
||||
@@ -164,8 +167,7 @@ namespace Desktop
|
||||
}
|
||||
});
|
||||
|
||||
// Whether it stopped because of a breakpoint, a crash, or the user clicking "Stop",
|
||||
// we MUST update the UI when the background thread finishes!
|
||||
//update the UI when the background thread finishes
|
||||
btnRun.Text = "Run";
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
1
Desktop/Form1.Designer.cs
generated
1
Desktop/Form1.Designer.cs
generated
@@ -48,6 +48,7 @@
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(791, 596);
|
||||
Controls.Add(picScreen);
|
||||
KeyPreview = true;
|
||||
Name = "Form1";
|
||||
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
||||
((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Desktop
|
||||
{
|
||||
private Z80 _cpu = null!;
|
||||
private MemoryBus _memoryBus = null!;
|
||||
private SimpleIoBus _simpleIoBus = null!;
|
||||
private IO_Bus _simpleIoBus = null!;
|
||||
|
||||
// The 16 physical colors of the ZX Spectrum (ARGB format)
|
||||
private readonly int[] SpectrumColors = new int[]
|
||||
@@ -50,7 +50,7 @@ namespace Desktop
|
||||
try
|
||||
{
|
||||
_memoryBus = new MemoryBus();
|
||||
_simpleIoBus = new SimpleIoBus();
|
||||
_simpleIoBus = new IO_Bus();
|
||||
|
||||
_memoryBus.CrapRAMData();
|
||||
byte[] romData = RomLoader.Load("48.rom");
|
||||
@@ -120,5 +120,67 @@ namespace Desktop
|
||||
if (picScreen.Image != null) picScreen.Image.Dispose();
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user