Added Master System Memory Bus
This commit is contained in:
@@ -9,9 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Audio\" />
|
<Folder Include="Audio\" />
|
||||||
<Folder Include="Cpu\" />
|
<Folder Include="Cpu\" />
|
||||||
<Folder Include="Interfaces\" />
|
|
||||||
<Folder Include="Io\" />
|
<Folder Include="Io\" />
|
||||||
<Folder Include="Memory\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
8
Core/Interfaces/IAudioDevice.cs
Normal file
8
Core/Interfaces/IAudioDevice.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Core/Interfaces/IIoBus.cs
Normal file
8
Core/Interfaces/IIoBus.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface IIoBus
|
||||||
|
{
|
||||||
|
byte Read(ushort port);
|
||||||
|
void Write(ushort port, byte value);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Core/Interfaces/IMemory.cs
Normal file
9
Core/Interfaces/IMemory.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface IMemory
|
||||||
|
{
|
||||||
|
byte Read(ushort address);
|
||||||
|
void Write(ushort address, byte value);
|
||||||
|
void CleanRAMData();
|
||||||
|
}
|
||||||
|
}
|
||||||
109
Core/Memory/SmsMemoryBus.cs
Normal file
109
Core/Memory/SmsMemoryBus.cs
Normal file
@@ -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<byte>();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user