diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 7ce3dec..d98ddac 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -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 diff --git a/Core/Io/IO_Bus.cs b/Core/Io/IO_Bus.cs new file mode 100644 index 0000000..b473df8 --- /dev/null +++ b/Core/Io/IO_Bus.cs @@ -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) + { + + } +} +} \ No newline at end of file diff --git a/Core/Io/SimpleIoBus.cs b/Core/Io/SimpleIoBus.cs deleted file mode 100644 index dbb90d9..0000000 --- a/Core/Io/SimpleIoBus.cs +++ /dev/null @@ -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}"); - } - } -} \ No newline at end of file diff --git a/Core/Memory/MemoryBus.cs b/Core/Memory/MemoryBus.cs index bf1595e..43d613a 100644 --- a/Core/Memory/MemoryBus.cs +++ b/Core/Memory/MemoryBus.cs @@ -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) { diff --git a/Desktop/48.rom b/Desktop/48.rom index 4d6895e..752c38a 100644 Binary files a/Desktop/48.rom and b/Desktop/48.rom differ diff --git a/Desktop/48.rom.bak b/Desktop/48.rom.bak new file mode 100644 index 0000000..ced65a8 Binary files /dev/null and b/Desktop/48.rom.bak differ diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index b15862e..07d98a1 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -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(); } diff --git a/Desktop/Form1.Designer.cs b/Desktop/Form1.Designer.cs index ced990d..389d04b 100644 --- a/Desktop/Form1.Designer.cs +++ b/Desktop/Form1.Designer.cs @@ -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(); diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index dea3f91..caa947e 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -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) + { + + } } } \ No newline at end of file