183 lines
6.1 KiB
C#
183 lines
6.1 KiB
C#
using static AxibugEmuOnline.Client.UNES.Cartridge.VRAMMirroringMode;
|
|
|
|
namespace AxibugEmuOnline.Client.UNES.Mapper
|
|
{
|
|
[MapperDef(4)]
|
|
public class MMC3 : BaseMapper
|
|
{
|
|
// Different PRG RAM write/enable controls
|
|
public enum ChipType { MMC3, MMC6 }
|
|
public enum CHRBankingMode { TwoFour, FourTwo }
|
|
public enum PRGBankingMode { SwitchFix, FixSwitch }
|
|
|
|
private readonly Cartridge.VRAMMirroringMode[] _mirroringModes = { Vertical, Horizontal };
|
|
|
|
private readonly ChipType _type;
|
|
protected CHRBankingMode _chrBankingMode;
|
|
protected PRGBankingMode _prgBankingMode;
|
|
|
|
|
|
protected readonly uint[] _chrBankOffsets = new uint[8];
|
|
protected uint[] _prgBankOffsets;
|
|
protected readonly uint[] _banks = new uint[8];
|
|
protected uint _currentBank;
|
|
|
|
private uint _irqReloadValue;
|
|
private uint _irqCounter;
|
|
protected bool _irqEnabled;
|
|
|
|
private bool _prgRAMEnabled;
|
|
|
|
public MMC3(Emulator emulator) : this(emulator, ChipType.MMC3)
|
|
{
|
|
|
|
}
|
|
|
|
public MMC3(Emulator emulator, ChipType chipType) : base(emulator)
|
|
{
|
|
_type = chipType;
|
|
_prgBankOffsets = new uint[] { 0, 0x2000, _lastBankOffset, _lastBankOffset + 0x2000 };
|
|
}
|
|
|
|
public override void InitializeMemoryMap(CPU cpu)
|
|
{
|
|
cpu.MapReadHandler(0x6000, 0x7FFF, address => _prgRAM[address - 0x6000]);
|
|
cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffsets[(address - 0x8000) / 0x2000] + address % 0x2000]);
|
|
|
|
cpu.MapWriteHandler(0x6000, 0xFFFF, WriteByte);
|
|
}
|
|
|
|
public override void InitializeMemoryMap(PPU ppu)
|
|
{
|
|
ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffsets[address / 0x400] + address % 0x400]);
|
|
ppu.MapWriteHandler(0x0000, 0x1FFF, (address, val) => _chrROM[_chrBankOffsets[address / 0x400] + address % 0x400] = val);
|
|
}
|
|
|
|
public override void ProcessCycle(int scanLine, int cycle)
|
|
{
|
|
if (_emulator.PPU.F.RenderingEnabled && cycle == 260 && (0 <= scanLine && scanLine < 240 || scanLine == -1))
|
|
{
|
|
if (_irqCounter == 0)
|
|
{
|
|
_irqCounter = _irqReloadValue;
|
|
}
|
|
else
|
|
{
|
|
_irqCounter--;
|
|
if (_irqEnabled && _irqCounter == 0)
|
|
{
|
|
_emulator.CPU.TriggerInterrupt(CPU.InterruptType.IRQ);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void WriteByte(uint addr, byte value)
|
|
{
|
|
bool even = (addr & 0x1) == 0;
|
|
|
|
if (addr < 0x8000)
|
|
{
|
|
if (_prgRAMEnabled)
|
|
{
|
|
_prgRAM[addr - 0x6000] = value;
|
|
}
|
|
}
|
|
else if (addr < 0xA000)
|
|
{
|
|
if (even)
|
|
{
|
|
_currentBank = value & 0x7u;
|
|
_prgBankingMode = (PRGBankingMode)((value >> 6) & 0x1);
|
|
_chrBankingMode = (CHRBankingMode)((value >> 7) & 0x1);
|
|
}
|
|
else
|
|
{
|
|
_banks[_currentBank] = value;
|
|
}
|
|
|
|
UpdateOffsets();
|
|
}
|
|
else if (addr < 0xC000)
|
|
{
|
|
if (even)
|
|
{
|
|
_emulator.Cartridge.MirroringMode = _mirroringModes[value & 0x1];
|
|
}
|
|
else
|
|
{
|
|
_prgRAMEnabled = (value & 0xC0) == 0x80;
|
|
}
|
|
}
|
|
else if (addr < 0xE000)
|
|
{
|
|
if (even)
|
|
{
|
|
_irqReloadValue = value;
|
|
}
|
|
else
|
|
{
|
|
_irqCounter = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_irqEnabled = !even;
|
|
}
|
|
}
|
|
|
|
protected void UpdateOffsets()
|
|
{
|
|
switch (_prgBankingMode)
|
|
{
|
|
case PRGBankingMode.SwitchFix:
|
|
_prgBankOffsets[0] = _banks[6] * 0x2000;
|
|
_prgBankOffsets[1] = _banks[7] * 0x2000;
|
|
_prgBankOffsets[2] = _lastBankOffset;
|
|
_prgBankOffsets[3] = _lastBankOffset + 0x2000;
|
|
break;
|
|
case PRGBankingMode.FixSwitch:
|
|
_prgBankOffsets[0] = _lastBankOffset;
|
|
_prgBankOffsets[1] = _banks[7] * 0x2000;
|
|
_prgBankOffsets[2] = _banks[6] * 0x2000;
|
|
_prgBankOffsets[3] = _lastBankOffset + 0x2000;
|
|
break;
|
|
}
|
|
|
|
switch (_chrBankingMode)
|
|
{
|
|
case CHRBankingMode.TwoFour:
|
|
_chrBankOffsets[0] = _banks[0] & 0xFE;
|
|
_chrBankOffsets[1] = _banks[0] | 0x01;
|
|
_chrBankOffsets[2] = _banks[1] & 0xFE;
|
|
_chrBankOffsets[3] = _banks[1] | 0x01;
|
|
_chrBankOffsets[4] = _banks[2];
|
|
_chrBankOffsets[5] = _banks[3];
|
|
_chrBankOffsets[6] = _banks[4];
|
|
_chrBankOffsets[7] = _banks[5];
|
|
break;
|
|
case CHRBankingMode.FourTwo:
|
|
_chrBankOffsets[0] = _banks[2];
|
|
_chrBankOffsets[1] = _banks[3];
|
|
_chrBankOffsets[2] = _banks[4];
|
|
_chrBankOffsets[3] = _banks[5];
|
|
_chrBankOffsets[4] = _banks[0] & 0xFE;
|
|
_chrBankOffsets[5] = _banks[0] | 0x01;
|
|
_chrBankOffsets[6] = _banks[1] & 0xFE;
|
|
_chrBankOffsets[7] = _banks[1] | 0x01;
|
|
break;
|
|
}
|
|
|
|
for (var i = 0; i < _prgBankOffsets.Length; i++)
|
|
{
|
|
_prgBankOffsets[i] %= (uint)_prgROM.Length;
|
|
}
|
|
|
|
for (var i = 0; i < _chrBankOffsets.Length; i++)
|
|
{
|
|
_chrBankOffsets[i] = (uint) (_chrBankOffsets[i] * 0x400 % _chrROM.Length);
|
|
}
|
|
}
|
|
}
|
|
}
|