GBA.Unity/Assets/emulator/Memory.cs
2024-08-16 14:51:15 +08:00

206 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static OptimeGBA.MemoryUtil;
namespace OptimeGBA
{
public unsafe abstract class Memory
{
public SaveProvider SaveProvider;
public SortedDictionary<uint, uint> HwioWriteLog = new SortedDictionary<uint, uint>();
public SortedDictionary<uint, uint> HwioReadLog = new SortedDictionary<uint, uint>();
public bool LogHwioAccesses = false;
public abstract void InitPageTable(byte*[] pageTable, uint[] maskTable, bool write);
public const int PageSize = 1024;
public uint[] MemoryRegionMasks = new uint[1048576];
public byte[] EmptyPage = new byte[PageSize];
public byte*[] PageTableRead = new byte*[1048576];
public byte*[] PageTableWrite = new byte*[1048576];
public abstract byte Read8Unregistered(bool debug, uint addr);
public abstract void Write8Unregistered(bool debug, uint addr, byte val);
public abstract ushort Read16Unregistered(bool debug, uint addr);
public abstract void Write16Unregistered(bool debug, uint addr, ushort val);
public abstract uint Read32Unregistered(bool debug, uint addr);
public abstract void Write32Unregistered(bool debug, uint addr, uint val);
public Dictionary<byte[], GCHandle> Handles = new Dictionary<byte[], GCHandle>();
public byte* TryPinByteArray(byte[] arr)
{
GCHandle handle;
if (!Handles.TryGetValue(arr, out handle))
{
handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
Handles[arr] = handle;
}
return (byte*)handle.AddrOfPinnedObject();
}
public void UnpinByteArray(byte[] arr)
{
Handles[arr].Free();
if (!Handles.Remove(arr))
{
throw new ArgumentException("Tried to unpin already unpinned array.");
}
}
public void InitPageTables()
{
InitPageTable(PageTableRead, MemoryRegionMasks, false);
InitPageTable(PageTableWrite, MemoryRegionMasks, true);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint MaskAddress(uint addr)
{
return addr & MemoryRegionMasks[addr >> 12];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte* ResolvePageRead(uint addr)
{
return PageTableRead[addr >> 12];
}
public byte* ResolvePageWrite(uint addr)
{
return PageTableWrite[addr >> 12];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte Read8(uint addr)
{
var page = ResolvePageRead(addr);
if (page != null)
{
return GetByte(page, MaskAddress(addr));
}
return Read8Unregistered(false, addr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort Read16(uint addr)
{
#if DEBUG
if ((addr & 1) != 0)
{
Console.Error.WriteLine("Misaligned Read16! " + Util.HexN(addr, 8));
}
#endif
var page = ResolvePageRead(addr);
if (page != null)
{
return GetUshort(page, MaskAddress(addr));
}
return Read16Unregistered(false, addr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ReadDebug16(uint addr)
{
var page = ResolvePageRead(addr);
if (page != null)
{
return GetUshort(page, MaskAddress(addr));
}
return Read16Unregistered(true, addr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Read32(uint addr)
{
#if DEBUG
if ((addr & 3) != 0)
{
Console.Error.WriteLine("Misaligned Read32! " + Util.HexN(addr, 8));
}
#endif
var page = ResolvePageRead(addr);
if (page != null)
{
return GetUint(page, MaskAddress(addr));
}
return Read32Unregistered(false, addr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ReadDebug32(uint addr)
{
var page = ResolvePageRead(addr);
if (page != null)
{
return GetUint(page, MaskAddress(addr));
}
return Read32Unregistered(true, addr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write8(uint addr, byte val)
{
var page = ResolvePageWrite(addr);
if (page != null)
{
SetByte(page, MaskAddress(addr), val);
return;
}
Write8Unregistered(false, addr, val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write16(uint addr, ushort val)
{
#if DEBUG
if ((addr & 1) != 0)
{
Console.Error.WriteLine("Misaligned Write16! " + Util.HexN(addr, 8));
}
#endif
var page = ResolvePageWrite(addr);
if (page != null)
{
SetUshort(page, MaskAddress(addr), val);
return;
}
Write16Unregistered(false, addr, val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write32(uint addr, uint val)
{
#if DEBUG
if ((addr & 3) != 0)
{
Console.Error.WriteLine("Misaligned Write32! " + Util.HexN(addr, 8));
}
#endif
var page = ResolvePageWrite(addr);
if (page != null)
{
SetUint(page, MaskAddress(addr), val);
return;
}
Write32Unregistered(false, addr, val);
}
}
}