diff --git a/Core/Core.csproj b/Core/Core.csproj index 7be6fbe..6886036 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -9,9 +9,7 @@ - - diff --git a/Core/Interfaces/IAudioDevice.cs b/Core/Interfaces/IAudioDevice.cs new file mode 100644 index 0000000..98d2426 --- /dev/null +++ b/Core/Interfaces/IAudioDevice.cs @@ -0,0 +1,8 @@ +namespace Core.Interfaces +{ + public interface IAudioDevice + { + // Now accepts the Beeper state + 3 AY frequencies + 3 AY volumes + void AddSample(bool isHigh, float freqA, float volA, float freqB, float volB, float freqC, float volC); + } +} \ No newline at end of file diff --git a/Core/Interfaces/IIoBus.cs b/Core/Interfaces/IIoBus.cs new file mode 100644 index 0000000..c28821e --- /dev/null +++ b/Core/Interfaces/IIoBus.cs @@ -0,0 +1,8 @@ +namespace Core.Interfaces +{ + public interface IIoBus + { + byte Read(ushort port); + void Write(ushort port, byte value); + } +} diff --git a/Core/Interfaces/IMemory.cs b/Core/Interfaces/IMemory.cs new file mode 100644 index 0000000..cd84d64 --- /dev/null +++ b/Core/Interfaces/IMemory.cs @@ -0,0 +1,9 @@ +namespace Core.Interfaces +{ + public interface IMemory + { + byte Read(ushort address); + void Write(ushort address, byte value); + void CleanRAMData(); + } +} diff --git a/Core/Memory/SmsMemoryBus.cs b/Core/Memory/SmsMemoryBus.cs new file mode 100644 index 0000000..dbcd319 --- /dev/null +++ b/Core/Memory/SmsMemoryBus.cs @@ -0,0 +1,109 @@ +using Core.Interfaces; +using System; + +namespace Core.Memory +{ + public class SmsMemoryBus : IMemory + { + // SMS has 8KB of internal Work RAM + private readonly byte[] _workRam = new byte[0x2000]; + + // The entire game cartridge loaded into one big array + private byte[] _cartridgeRom = Array.Empty(); + + // The Paging Registers (Which 16KB chunk is in which slot?) + // Default startup state: Bank 0, 1, and 2 in order. + private int _romBank0 = 0; + private int _romBank1 = 1; + private int _romBank2 = 2; + + // A flag to handle cartridges that don't use paging (like early 32KB games) + private bool _isCartridgeLoaded = false; + + public void LoadCartridge(byte[] romData) + { + _cartridgeRom = romData; + _isCartridgeLoaded = true; + + // Reset the mapper on boot + _romBank0 = 0; + _romBank1 = 1; + _romBank2 = 2; + + // Clean the RAM + CleanRAMData(); + } + + 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) + { + 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) + { + if (address < 0xC000) + { + // You cannot write to a ROM cartridge! + return; + } + + // 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) + { + _romBank0 = value; + } + else if (address == 0xFFFE) + { + _romBank1 = value; + } + else if (address == 0xFFFF) + { + _romBank2 = value; + } + } + + private byte ReadFromCartridge(int bankIndex, int offset) + { + if (!_isCartridgeLoaded) return 0xFF; // Floating bus if no game + + int absoluteAddress = (bankIndex * 0x4000) + offset; + + // Safety check in case a game asks for a bank outside its file size + if (absoluteAddress >= _cartridgeRom.Length) + { + // Wrap around (mirrors the ROM if it's smaller than the requested bank) + absoluteAddress %= _cartridgeRom.Length; + } + + return _cartridgeRom[absoluteAddress]; + } + + public void CleanRAMData() + { + Array.Clear(_workRam, 0, _workRam.Length); + } + } +} \ No newline at end of file