Emux/Emux.GameBoy/Cartridge/MemoryBankController1.cs

102 lines
3.4 KiB
C#

using System;
namespace Emux.GameBoy.Cartridge
{
public class MemoryBankController1 : IMemoryBankController
{
private readonly IFullyAccessibleCartridge _cartridge;
private readonly byte[] _romBank = new byte[0x4000];
private int _romBankIndex = 1;
private int _ramBankIndex;
private bool _romRamMode;
private readonly byte[] _externalRam;
private bool _ramEnabled = false;
public MemoryBankController1(IFullyAccessibleCartridge cartridge)
{
if (cartridge == null)
throw new ArgumentNullException(nameof(cartridge));
_cartridge = cartridge;
_externalRam = new byte[cartridge.ExternalRamSize];
}
public byte ReadByte(ushort address)
{
if (address < 0x4000)
return _cartridge.ReadFromAbsoluteAddress(address);
if (address < 0x8000)
return _romBank[address - 0x4000];
if (_ramEnabled && address >= 0xA000 && address <= 0xBFFF)
return _externalRam[address - 0xA000 + GetRamOffset()];
return 0;
}
public void ReadBytes(ushort address, byte[] buffer, int bufferOffset, int length)
{
if (address < 0x4000)
_cartridge.ReadFromAbsoluteAddress(address, buffer, bufferOffset, length);
else if (address < 0x8000)
Buffer.BlockCopy(_romBank, address - 0x4000, buffer, bufferOffset, length);
if (_ramEnabled && address >= 0xA000 && address <= 0xBFFF)
Buffer.BlockCopy(_externalRam, address - 0xA000 + GetRamOffset(), buffer, bufferOffset, length);
}
public void WriteByte(ushort address, byte value)
{
if (address < 0x2000)
_ramEnabled = (value & 0xA) == 0xA;
else if (address < 0x4000)
SwitchRomBank(value & 0x1F);
else if (address < 0x6000)
SwitchRamBank(value & 0x3);
else if (address < 0x8000)
SwitchRomRamMode(value);
else if (_ramEnabled && address >= 0xA000 && address - 0xA000 < _externalRam.Length)
_externalRam[address - 0xA000 + GetRamOffset()] = value;
}
private void SwitchRomRamMode(byte value)
{
bool romRamMode = value == 1;
if (_romRamMode != romRamMode)
{
_romRamMode = romRamMode;
UpdateRomBank();
}
}
private void SwitchRamBank(int index)
{
if (_ramBankIndex != index)
{
_ramBankIndex = index;
UpdateRomBank();
}
}
private void SwitchRomBank(int index)
{
if (_romBankIndex != index)
{
if (index == 0 || index == 0x20 || index == 0x40 || index == 0x60)
index++;
_romBankIndex = index;
UpdateRomBank();
}
}
private void UpdateRomBank()
{
int index = _romBankIndex;
if (_romRamMode)
index |= _ramBankIndex << 5;
_cartridge.ReadFromAbsoluteAddress(_romBank.Length * index, _romBank, 0, _romBank.Length);
}
private int GetRamOffset()
{
return _ramBankIndex * 0x2000;
}
}
}