From bc2748250d8ba930073bc1af646237420dc8a9b1 Mon Sep 17 00:00:00 2001 From: Marc Parsons Date: Thu, 30 Apr 2026 15:42:54 +0100 Subject: [PATCH] Plus 3 mode fully working. Added Plus 3 keyboard --- Core/Cpu/Z80.cs | 4 ++-- Core/Io/IO_Bus.cs | 40 ++++++++++++++++++++++++++-------------- Core/Io/ULA.cs | 16 ++++++++++++---- Core/Memory/MemoryBus.cs | 7 ++++++- Desktop/Form1.cs | 21 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 21 deletions(-) diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 3a26abd..0b73771 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -2472,8 +2472,8 @@ namespace Core.Cpu SP = IY.Word; return 10; default: - // The Z80 Ignored Prefix Quirk! - return ExecuteOpcode(ddOpcode) + 4; // Note: You named the fetched variable 'opcode' here instead of 'ddOpcode' + // Z80 nonsense loopback bug + return ExecuteOpcode(ddOpcode) + 4; } } } diff --git a/Core/Io/IO_Bus.cs b/Core/Io/IO_Bus.cs index d547c0d..2fc8c1c 100644 --- a/Core/Io/IO_Bus.cs +++ b/Core/Io/IO_Bus.cs @@ -73,28 +73,40 @@ namespace Core.Io // Bit 3 handles the cassette MIC output } - // 128K Standard Paging Port (0x7FFD) - if ((portAddress & 0x8002) == 0) + // Mask 0xC002 checks A15=0, A14=1, A1=0 to prevent 0x1FFD from triggering this! + if ((portAddress & 0xC002) == 0x4000) { _memory.HandlePaging(0x7FFD, portValue); } - // +2A/+3 Extended Paging Port (0x1FFD) - if ((portAddress & 0xF002) == 0x1000) + // Mask 0xF002 checks A15=0, A14=0, A13=0, A12=1, A1=0 + else if ((portAddress & 0xF002) == 0x1000) { _memory.HandlePaging(0x1FFD, portValue); } - // AY-3-8912 Register Select (Port 0xFFFD) - if ((portAddress & 0xC002) == 0xC000) - { - AyChip.SelectRegister(portValue); - } - // AY-3-8912 Data Write (Port 0xBFFD) - else if ((portAddress & 0xC002) == 0x8000) - { - AyChip.WriteRegister(portValue); - } + + //// 128K Standard Paging Port (0x7FFD) + //if ((portAddress & 0x8002) == 0) + //{ + // _memory.HandlePaging(0x7FFD, portValue); + //} + + //// +2A/+3 Extended Paging Port (0x1FFD) + //if ((portAddress & 0xF002) == 0x1000) + //{ + // _memory.HandlePaging(0x1FFD, portValue); + //} + //// AY-3-8912 Register Select (Port 0xFFFD) + //if ((portAddress & 0xC002) == 0xC000) + //{ + // AyChip.SelectRegister(portValue); + //} + //// AY-3-8912 Data Write (Port 0xBFFD) + //else if ((portAddress & 0xC002) == 0x8000) + //{ + // AyChip.WriteRegister(portValue); + //} } } } \ No newline at end of file diff --git a/Core/Io/ULA.cs b/Core/Io/ULA.cs index cee64b9..63f7217 100644 --- a/Core/Io/ULA.cs +++ b/Core/Io/ULA.cs @@ -54,6 +54,7 @@ namespace Core.Io // The perfectly cropped, 320x256 Scanline Renderer public void RenderScanline(int scanline) { + byte[] videoRam = _memoryBus.GetVideoRam(); // 1. Drop the invisible lines instantly (VBlank/Overscan) if (scanline < 32 || scanline > 287) return; @@ -91,11 +92,18 @@ namespace Core.Io // Draw the 32 horizontal character blocks of the visible screen for (int col = 0; col < 32; col++) { - ushort pixelAddress = (ushort)(0x4000 | (third << 11) | (pixelRow << 8) | (characterRow << 5) | col); - ushort attrAddress = (ushort)(0x5800 + (y / 8) * 32 + col); + // Calculate the exact internal array offsets (Base 0, no 0x4000 needed!) + int pixelOffset = (third << 11) | (pixelRow << 8) | (characterRow << 5) | col; + int attrOffset = 0x1800 + (y / 8) * 32 + col; - byte pixels = _memoryBus.Read(pixelAddress); - byte attr = _memoryBus.Read(attrAddress); + // Read directly from the raw array + byte pixels = videoRam[pixelOffset]; + byte attr = videoRam[attrOffset]; + //ushort pixelAddress = (ushort)(0x4000 | (third << 11) | (pixelRow << 8) | (characterRow << 5) | col); + //ushort attrAddress = (ushort)(0x5800 + (y / 8) * 32 + col); + + //byte pixels = _memoryBus.Read(pixelAddress); + //byte attr = _memoryBus.Read(attrAddress); int ink = attr & 0x07; int paper = (attr >> 3) & 0x07; diff --git a/Core/Memory/MemoryBus.cs b/Core/Memory/MemoryBus.cs index fb2aa14..9b5fa51 100644 --- a/Core/Memory/MemoryBus.cs +++ b/Core/Memory/MemoryBus.cs @@ -47,7 +47,7 @@ namespace Core.Memory // Called by the IO Bus when the CPU writes to a paging port public void HandlePaging(ushort port, byte value) { - Debug.WriteLine($"[PAGING] 0x7FFD Triggered! Hex: 0x{value:X2} | Bank: {value & 0x07}"); + //Debug.WriteLine($"[PAGING] 0x7FFD Triggered! Hex: 0x{value:X2} | Bank: {value & 0x07}"); if (_pagingDisabled || _currentRomBank == 4) return; // Ignore if locked or in 48K mode if (port == 0x7FFD) @@ -126,6 +126,11 @@ namespace Core.Memory } } + public byte[] GetVideoRam() + { + // Bit 3 of 7FFD dictates the active screen. 0 = Bank 5 (Standard), 1 = Bank 7 (Shadow). + return (_port7FFD & 0x08) != 0 ? _ramBanks[7] : _ramBanks[5]; + } public void LoadRom(byte[] romData, int bankIndex) { Array.Copy(romData, 0, _romBanks[bankIndex], 0, Math.Min(romData.Length, 0x4000)); diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index ed9122f..88e2b16 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -227,6 +227,27 @@ namespace Desktop case Keys.M: UpdateMatrix(7, 2, isPressed); break; case Keys.N: UpdateMatrix(7, 3, isPressed); break; case Keys.B: UpdateMatrix(7, 4, isPressed); break; + // --- EXTENDED +2A KEYS (Macros) --- + case Keys.Left: + UpdateMatrix(0, 0, isPressed); // CAPS SHIFT + UpdateMatrix(3, 4, isPressed); // 5 + break; + case Keys.Down: + UpdateMatrix(0, 0, isPressed); // CAPS SHIFT + UpdateMatrix(4, 4, isPressed); // 6 + break; + case Keys.Up: + UpdateMatrix(0, 0, isPressed); // CAPS SHIFT + UpdateMatrix(4, 3, isPressed); // 7 + break; + case Keys.Right: + UpdateMatrix(0, 0, isPressed); // CAPS SHIFT + UpdateMatrix(4, 2, isPressed); // 8 + break; + case Keys.Back: + UpdateMatrix(0, 0, isPressed); // CAPS SHIFT + UpdateMatrix(4, 0, isPressed); // 0 + break; } }