AxibugEmuOnline/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs
2024-06-28 18:08:25 +08:00

131 lines
4.2 KiB
C#

using System;
using System.Runtime.CompilerServices;
namespace AxibugEmuOnline.Client.UNES
{
partial class PPU
{
private readonly byte[] _oam = new byte[0x100];
private readonly byte[] _vRam = new byte[0x2000];
private readonly byte[] _paletteRAM = new byte[0x20];
private static readonly uint[][] _vRamMirrorLookup =
{
new uint[]{0, 0, 1, 1}, // H
new uint[]{0, 1, 0, 1}, // V
new uint[]{0, 1, 2, 3}, // All
new uint[]{0, 0, 0, 0}, // Upper
new uint[]{1, 1, 1, 1}, // Lower
};
private int _lastWrittenRegister;
public void WriteRegister(uint reg, byte val)
{
reg &= 0xF;
_lastWrittenRegister = val & 0xFF;
switch (reg)
{
case 0x0000:
PPUCTRL = val;
return;
case 0x0001:
PPUMASK = val;
return;
case 0x0002: return;
case 0x0003:
OAMADDR = val;
return;
case 0x0004:
OAMDATA = val;
return;
case 0x005:
PPUSCROLL = val;
return;
case 0x0006:
PPUADDR = val;
return;
case 0x0007:
PPUDATA = val;
return;
}
throw new NotImplementedException($"{reg:X4} = {val:X2}");
}
public byte ReadRegister(uint reg)
{
reg &= 0xF;
switch (reg)
{
case 0x0000: return (byte)_lastWrittenRegister;
case 0x0001: return (byte)_lastWrittenRegister;
case 0x0002:
return (byte)PPUSTATUS;
case 0x0003:
return (byte)OAMADDR;
case 0x0004:
return (byte)OAMDATA;
case 0x0005: return (byte)_lastWrittenRegister;
case 0x0006: return (byte)_lastWrittenRegister;
case 0x0007:
return (byte)PPUDATA;
}
throw new NotImplementedException(reg.ToString("X2"));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetVRamMirror(long address)
{
long entry;
var table = Math.DivRem(address - 0x2000, 0x400, out entry);
return _vRamMirrorLookup[(int)_emulator.Cartridge.MirroringMode][table] * 0x400 + (uint)entry;
}
protected override void InitializeMemoryMap()
{
base.InitializeMemoryMap();
MapReadHandler(0x2000, 0x2FFF, address => _vRam[GetVRamMirror(address)]);
MapReadHandler(0x3000, 0x3EFF, address => _vRam[GetVRamMirror(address - 0x1000)]);
MapReadHandler(0x3F00, 0x3FFF, address =>
{
if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F0C)
{
address -= 0x10;
}
return _paletteRAM[(address - 0x3F00) & 0x1F];
});
MapWriteHandler(0x2000, 0x2FFF, (address, val) => _vRam[GetVRamMirror(address)] = val);
MapWriteHandler(0x3000, 0x3EFF, (address, val) => _vRam[GetVRamMirror(address - 0x1000)] = val);
MapWriteHandler(0x3F00, 0x3FFF, (address, val) =>
{
if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F0C)
{
address -= 0x10;
}
_paletteRAM[(address - 0x3F00) & 0x1F] = val;
});
_emulator.Mapper.InitializeMemoryMap(this);
}
public void PerformDMA(uint from)
{
//Console.WriteLine("OAM DMA");
from <<= 8;
for (uint i = 0; i <= 0xFF; i++)
{
_oam[F.OAMAddress] = (byte)_emulator.CPU.ReadByte(from);
from++;
F.OAMAddress++;
}
_emulator.CPU.Cycle += 513 + _emulator.CPU.Cycle % 2;
}
}
}