From c416bea9acb86622ea54adcca021c9fe682a4956 Mon Sep 17 00:00:00 2001 From: parsons Date: Thu, 14 May 2026 23:30:26 +0100 Subject: [PATCH] Everything works great now. Phantasy Star loads and plays --- Core/Io/SmsIoBus.cs | 13 ++--- Core/Memory/SmsMemoryBus.cs | 103 ++++++++++++++++++++++++++++++++++-- 2 files changed, 105 insertions(+), 11 deletions(-) diff --git a/Core/Io/SmsIoBus.cs b/Core/Io/SmsIoBus.cs index 045ed5b..ee4fb57 100644 --- a/Core/Io/SmsIoBus.cs +++ b/Core/Io/SmsIoBus.cs @@ -43,19 +43,16 @@ namespace Core.Io { byte lowerPort = (byte)(port & 0xFF); - // Audio Ports if (lowerPort == 0x7E || lowerPort == 0x7F) { AudioProcessor.WritePort7F(value); } - // Video Ports - else if (lowerPort == 0xBE) + // THE FIX: Video Ports are mirrored across the entire 0x80 to 0xBF range! + else if (lowerPort >= 0x80 && lowerPort <= 0xBF) { - VideoProcessor.WriteDataPort(value); - } - else if (lowerPort == 0xBF) - { - VideoProcessor.WriteControlPort(value); + // Even ports are Data, Odd ports are Control + if ((lowerPort & 0x01) == 0) VideoProcessor.WriteDataPort(value); + else VideoProcessor.WriteControlPort(value); } else if (lowerPort <= 0x3F) { diff --git a/Core/Memory/SmsMemoryBus.cs b/Core/Memory/SmsMemoryBus.cs index dbcd319..3a53045 100644 --- a/Core/Memory/SmsMemoryBus.cs +++ b/Core/Memory/SmsMemoryBus.cs @@ -17,6 +17,10 @@ namespace Core.Memory private int _romBank1 = 1; private int _romBank2 = 2; + // --- NEW: Cartridge RAM --- + private byte _mapperControl = 0; // 0xFFFC + private byte[] _cartridgeRam = new byte[0x8000]; // 32KB Max Cart RAM + // A flag to handle cartridges that don't use paging (like early 32KB games) private bool _isCartridgeLoaded = false; @@ -39,7 +43,6 @@ namespace Core.Memory if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF) { // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged. - // It is hardwired to Bank 0 so the interrupt handlers don't crash. if (address < 0x0400) return ReadFromCartridge(0, address); return ReadFromCartridge(_romBank0, address); @@ -50,18 +53,98 @@ namespace Core.Memory } if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF) { + // --- THE MISSING LINK: Read from Save RAM if enabled! --- + if ((_mapperControl & 0x08) != 0) + { + int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; + int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + return _cartridgeRam[ramOffset]; + } + + // Otherwise, read from the ROM as usual return ReadFromCartridge(_romBank2, address & 0x3FFF); } - // If we are here, we are in System RAM (0xC000 - 0xFFFF) - // The & 0x1FFF handles the 8KB mirroring automatically! + // System RAM (0xC000 - 0xFFFF) return _workRam[address & 0x1FFF]; } + //public byte Read(ushort address) + //{ + // if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF) + // { + // // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged. + // // It is hardwired to Bank 0 so the interrupt handlers don't crash. + // if (address < 0x0400) return ReadFromCartridge(0, address); + + // return ReadFromCartridge(_romBank0, address); + // } + // if (address < 0x8000) // ROM Slot 1 (0x4000 - 0x7FFF) + // { + // return ReadFromCartridge(_romBank1, address & 0x3FFF); + // } + // if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF) + // { + // // Check if Cartridge RAM is enabled (Bit 3 of Mapper Control) + // if ((_mapperControl & 0x08) != 0) + // { + // // Bit 2 decides if we read the first 16KB half or the second 16KB half of the RAM chip + // int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; + // int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + // return _cartridgeRam[ramOffset]; + // } + + // return ReadFromCartridge(_romBank2, address & 0x3FFF); + // } + + // // If we are here, we are in System RAM (0xC000 - 0xFFFF) + // // The & 0x1FFF handles the 8KB mirroring automatically! + // return _workRam[address & 0x1FFF]; + //} + + //public void Write(ushort address, byte value) + //{ + // // --- 1. CARTRIDGE RAM (Save Data) --- + // if (address < 0xC000) + // { + // if (address >= 0x8000 && (_mapperControl & 0x08) != 0) + // { + // int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; + // int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + // _cartridgeRam[ramOffset] = value; + // } + + // // You cannot write to a ROM cartridge! + // return; + // } + + // // --- 2. SYSTEM RAM --- + // _workRam[address & 0x1FFF] = value; + + // // --- 3. SEGA MAPPER (With Hardware Mirroring!) --- + // // By checking the masked address (address & 0x1FFF), we perfectly + // // catch writes to 0xFFFC-0xFFFF *and* their 0xDFFC-0xDFFF mirrors! + // int mapperAddress = address & 0x1FFF; + + // if (mapperAddress == 0x1FFC) _mapperControl = value; + // else if (mapperAddress == 0x1FFD) _romBank0 = value; + // else if (mapperAddress == 0x1FFE) _romBank1 = value; + // else if (mapperAddress == 0x1FFF) _romBank2 = value; + //} + public void Write(ushort address, byte value) { if (address < 0xC000) { + // Bypass the lock if they are trying to save their game! + if (address >= 0x8000 && (_mapperControl & 0x08) != 0) + { + int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; + int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + _cartridgeRam[ramOffset] = value; + return; + } + // You cannot write to a ROM cartridge! return; } @@ -69,6 +152,19 @@ namespace Core.Memory // Write to System RAM _workRam[address & 0x1FFF] = value; + // --- THE SEGA MAPPER --- + if (address == 0xFFFC) // <-- ADD THIS + { + _mapperControl = value; + } + else if (address == 0xFFFD) + { + _romBank0 = value; + } + + // Write to System RAM + _workRam[address & 0x1FFF] = value; + // --- THE SEGA MAPPER --- // If the CPU wrote to the very top of memory, it is commanding a bank swap! if (address == 0xFFFD) @@ -104,6 +200,7 @@ namespace Core.Memory public void CleanRAMData() { Array.Clear(_workRam, 0, _workRam.Length); + Array.Clear(_cartridgeRam, 0, _cartridgeRam.Length); } } } \ No newline at end of file