2219 lines
118 KiB
C#
2219 lines
118 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Iris.GBA
|
|
{
|
|
internal sealed class Memory : IDisposable
|
|
{
|
|
private Communication _communication;
|
|
private Timer _timer;
|
|
private Sound _sound;
|
|
private DMA _dma;
|
|
private KeyInput _keyInput;
|
|
private SystemControl _systemControl;
|
|
private InterruptControl _interruptControl;
|
|
private Video _video;
|
|
private BIOS _bios;
|
|
|
|
[Flags]
|
|
internal enum Flag
|
|
{
|
|
Read8 = 1 << 0,
|
|
Read16 = 1 << 1,
|
|
Read32 = 1 << 2,
|
|
Write8 = 1 << 3,
|
|
Write16 = 1 << 4,
|
|
Write32 = 1 << 5,
|
|
Mirrored = 1 << 6,
|
|
|
|
None = 0,
|
|
AllRead = Read8 | Read16 | Read32,
|
|
AllWrite = Write8 | Write16 | Write32,
|
|
All = AllRead | AllWrite | Mirrored,
|
|
}
|
|
|
|
private const int KB = 1024;
|
|
|
|
private const int EWRAM_Size = 256 * KB;
|
|
private const int IWRAM_Size = 32 * KB;
|
|
//private const int SRAM_Size = 64 * KB;
|
|
|
|
private readonly IntPtr _ewram = Marshal.AllocHGlobal(EWRAM_Size);
|
|
private readonly IntPtr _iwram = Marshal.AllocHGlobal(IWRAM_Size);
|
|
//private readonly IntPtr _sram = Marshal.AllocHGlobal(SRAM_Size);
|
|
|
|
private const UInt32 EWRAM_StartAddress = 0x0200_0000;
|
|
private const UInt32 EWRAM_EndAddress = 0x0300_0000;
|
|
|
|
private const UInt32 IWRAM_StartAddress = 0x0300_0000;
|
|
private const UInt32 IWRAM_EndAddress = 0x0400_0000;
|
|
|
|
//private const UInt32 SRAM_StartAddress = 0x0e00_0000;
|
|
//private const UInt32 SRAM_EndAddress = 0x1000_0000;
|
|
|
|
private int _romSize;
|
|
private IntPtr _rom;
|
|
|
|
private const UInt32 ROM_WaitState0_StartAddress = 0x0800_0000;
|
|
private const UInt32 ROM_WaitState0_EndAddress = 0x0a00_0000;
|
|
|
|
private const UInt32 ROM_WaitState1_StartAddress = 0x0a00_0000;
|
|
private const UInt32 ROM_WaitState1_EndAddress = 0x0c00_0000;
|
|
|
|
private const UInt32 ROM_WaitState2_StartAddress = 0x0c00_0000;
|
|
private const UInt32 ROM_WaitState2_EndAddress = 0x0e00_0000;
|
|
|
|
private const int PageSize = 1 * KB;
|
|
private const int PageTableSize = 1 << 18;
|
|
|
|
private readonly IntPtr[] _read8PageTable = new IntPtr[PageTableSize];
|
|
private readonly IntPtr[] _read16PageTable = new IntPtr[PageTableSize];
|
|
private readonly IntPtr[] _read32PageTable = new IntPtr[PageTableSize];
|
|
private readonly IntPtr[] _write8PageTable = new IntPtr[PageTableSize];
|
|
private readonly IntPtr[] _write16PageTable = new IntPtr[PageTableSize];
|
|
private readonly IntPtr[] _write32PageTable = new IntPtr[PageTableSize];
|
|
|
|
private bool _disposed;
|
|
|
|
~Memory()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
Marshal.FreeHGlobal(_ewram);
|
|
Marshal.FreeHGlobal(_iwram);
|
|
//Marshal.FreeHGlobal(_sram);
|
|
|
|
GC.SuppressFinalize(this);
|
|
_disposed = true;
|
|
}
|
|
|
|
internal void Initialize(Communication communication, Timer timer, Sound sound, DMA dma, KeyInput keyInput, SystemControl systemControl, InterruptControl interruptControl, Video video, BIOS bios)
|
|
{
|
|
_communication = communication;
|
|
_timer = timer;
|
|
_sound = sound;
|
|
_dma = dma;
|
|
_keyInput = keyInput;
|
|
_systemControl = systemControl;
|
|
_interruptControl = interruptControl;
|
|
_video = video;
|
|
_bios = bios;
|
|
|
|
Map(_ewram, EWRAM_Size, EWRAM_StartAddress, EWRAM_EndAddress, Flag.All);
|
|
Map(_iwram, IWRAM_Size, IWRAM_StartAddress, IWRAM_EndAddress, Flag.All);
|
|
//Map(_sram, SRAM_Size, SRAM_StartAddress, SRAM_EndAddress, Flag.Read8 | Flag.Write8 | Flag.Mirrored);
|
|
}
|
|
|
|
internal void ResetState()
|
|
{
|
|
byte[] ewramData = new byte[EWRAM_Size];
|
|
byte[] iwramData = new byte[IWRAM_Size];
|
|
//byte[] sramData = new byte[SRAM_Size];
|
|
|
|
Marshal.Copy(ewramData, 0, _ewram, EWRAM_Size);
|
|
Marshal.Copy(iwramData, 0, _iwram, IWRAM_Size);
|
|
//Marshal.Copy(sramData, 0, _sram, SRAM_Size);
|
|
}
|
|
|
|
internal void LoadState(BinaryReader reader)
|
|
{
|
|
byte[] ewramData = reader.ReadBytes(EWRAM_Size);
|
|
byte[] iwramData = reader.ReadBytes(IWRAM_Size);
|
|
//byte[] sramData = reader.ReadBytes(SRAM_Size);
|
|
|
|
Marshal.Copy(ewramData, 0, _ewram, EWRAM_Size);
|
|
Marshal.Copy(iwramData, 0, _iwram, IWRAM_Size);
|
|
//Marshal.Copy(sramData, 0, _sram, SRAM_Size);
|
|
}
|
|
|
|
internal void SaveState(BinaryWriter writer)
|
|
{
|
|
byte[] ewramData = new byte[EWRAM_Size];
|
|
byte[] iwramData = new byte[IWRAM_Size];
|
|
//byte[] sramData = new byte[SRAM_Size];
|
|
|
|
Marshal.Copy(_ewram, ewramData, 0, EWRAM_Size);
|
|
Marshal.Copy(_iwram, iwramData, 0, IWRAM_Size);
|
|
//Marshal.Copy(_sram, sramData, 0, SRAM_Size);
|
|
|
|
writer.Write(ewramData);
|
|
writer.Write(iwramData);
|
|
//writer.Write(sramData);
|
|
}
|
|
|
|
internal void Map(IntPtr data, int size, UInt32 startAddress, UInt32 endAddress, Flag flags)
|
|
{
|
|
int pageCount = size / PageSize;
|
|
int startTablePageIndex = (int)(startAddress >> 10);
|
|
int endPageTableIndex = (int)(endAddress >> 10);
|
|
|
|
bool readable8 = flags.HasFlag(Flag.Read8);
|
|
bool readable16 = flags.HasFlag(Flag.Read16);
|
|
bool readable32 = flags.HasFlag(Flag.Read32);
|
|
bool writable8 = flags.HasFlag(Flag.Write8);
|
|
bool writable16 = flags.HasFlag(Flag.Write16);
|
|
bool writable32 = flags.HasFlag(Flag.Write32);
|
|
bool mirrored = flags.HasFlag(Flag.Mirrored);
|
|
|
|
for (int pageTableIndex = startTablePageIndex, pageIndex = 0; pageTableIndex != endPageTableIndex; ++pageTableIndex, ++pageIndex)
|
|
{
|
|
if (pageIndex < pageCount)
|
|
{
|
|
int pageOffset = pageIndex * PageSize;
|
|
IntPtr page = data + pageOffset;
|
|
_read8PageTable[pageTableIndex] = readable8 ? page : IntPtr.Zero;
|
|
_read16PageTable[pageTableIndex] = readable16 ? page : IntPtr.Zero;
|
|
_read32PageTable[pageTableIndex] = readable32 ? page : IntPtr.Zero;
|
|
_write8PageTable[pageTableIndex] = writable8 ? page : IntPtr.Zero;
|
|
_write16PageTable[pageTableIndex] = writable16 ? page : IntPtr.Zero;
|
|
_write32PageTable[pageTableIndex] = writable32 ? page : IntPtr.Zero;
|
|
}
|
|
else if (mirrored)
|
|
{
|
|
int pageOffset = (pageIndex % pageCount) * PageSize;
|
|
IntPtr page = data + pageOffset;
|
|
_read8PageTable[pageTableIndex] = readable8 ? page : IntPtr.Zero;
|
|
_read16PageTable[pageTableIndex] = readable16 ? page : IntPtr.Zero;
|
|
_read32PageTable[pageTableIndex] = readable32 ? page : IntPtr.Zero;
|
|
_write8PageTable[pageTableIndex] = writable8 ? page : IntPtr.Zero;
|
|
_write16PageTable[pageTableIndex] = writable16 ? page : IntPtr.Zero;
|
|
_write32PageTable[pageTableIndex] = writable32 ? page : IntPtr.Zero;
|
|
}
|
|
else
|
|
{
|
|
_read8PageTable[pageTableIndex] = IntPtr.Zero;
|
|
_read16PageTable[pageTableIndex] = IntPtr.Zero;
|
|
_read32PageTable[pageTableIndex] = IntPtr.Zero;
|
|
_write8PageTable[pageTableIndex] = IntPtr.Zero;
|
|
_write16PageTable[pageTableIndex] = IntPtr.Zero;
|
|
_write32PageTable[pageTableIndex] = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
if (writable8 || writable16 || writable32)
|
|
{
|
|
int length = pageCount * PageSize;
|
|
|
|
for (int offset = 0; offset < length; ++offset)
|
|
Marshal.WriteByte(data, offset, 0);
|
|
}
|
|
}
|
|
|
|
internal void LoadROM(string filename)
|
|
{
|
|
Byte[] data = File.ReadAllBytes(filename);
|
|
|
|
_romSize = data.Length;
|
|
|
|
if (_rom != IntPtr.Zero)
|
|
Marshal.FreeHGlobal(_rom);
|
|
|
|
_rom = Marshal.AllocHGlobal(_romSize);
|
|
Marshal.Copy(data, 0, _rom, _romSize);
|
|
|
|
Map(_rom, _romSize, ROM_WaitState0_StartAddress, ROM_WaitState0_EndAddress, Flag.AllRead);
|
|
Map(_rom, _romSize, ROM_WaitState1_StartAddress, ROM_WaitState1_EndAddress, Flag.AllRead);
|
|
Map(_rom, _romSize, ROM_WaitState2_StartAddress, ROM_WaitState2_EndAddress, Flag.AllRead);
|
|
}
|
|
|
|
internal Byte Read8(UInt32 address)
|
|
{
|
|
address &= 0x0fff_ffff;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_read8PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadByte
|
|
return Unsafe.Read<Byte>((Byte*)page + (address & 0x3ff));
|
|
}
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
return _bios!.Read8(address);
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
static Byte GetLowByte(UInt16 value) => (Byte)value;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
static Byte GetHighByte(UInt16 value) => (Byte)(value >> 8);
|
|
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
return offset switch
|
|
{
|
|
0x000 => GetLowByte(_video!.ReadRegister(Video.Register.DISPCNT)),
|
|
0x001 => GetHighByte(_video!.ReadRegister(Video.Register.DISPCNT)),
|
|
|
|
0x004 => GetLowByte(_video!.ReadRegister(Video.Register.DISPSTAT)),
|
|
0x005 => GetHighByte(_video!.ReadRegister(Video.Register.DISPSTAT)),
|
|
|
|
0x006 => GetLowByte(_video!.ReadRegister(Video.Register.VCOUNT)),
|
|
0x007 => GetHighByte(_video!.ReadRegister(Video.Register.VCOUNT)),
|
|
|
|
0x008 => GetLowByte(_video!.ReadRegister(Video.Register.BG0CNT)),
|
|
0x009 => GetHighByte(_video!.ReadRegister(Video.Register.BG0CNT)),
|
|
|
|
0x00a => GetLowByte(_video!.ReadRegister(Video.Register.BG1CNT)),
|
|
0x00b => GetHighByte(_video!.ReadRegister(Video.Register.BG1CNT)),
|
|
|
|
0x00c => GetLowByte(_video!.ReadRegister(Video.Register.BG2CNT)),
|
|
0x00d => GetHighByte(_video!.ReadRegister(Video.Register.BG2CNT)),
|
|
|
|
0x00e => GetLowByte(_video!.ReadRegister(Video.Register.BG3CNT)),
|
|
0x00f => GetHighByte(_video!.ReadRegister(Video.Register.BG3CNT)),
|
|
|
|
0x048 => GetLowByte(_video!.ReadRegister(Video.Register.WININ)),
|
|
0x049 => GetHighByte(_video!.ReadRegister(Video.Register.WININ)),
|
|
|
|
0x04a => GetLowByte(_video!.ReadRegister(Video.Register.WINOUT)),
|
|
0x04b => GetHighByte(_video!.ReadRegister(Video.Register.WINOUT)),
|
|
|
|
0x050 => GetLowByte(_video!.ReadRegister(Video.Register.BLDCNT)),
|
|
0x051 => GetHighByte(_video!.ReadRegister(Video.Register.BLDCNT)),
|
|
|
|
0x052 => GetLowByte(_video!.ReadRegister(Video.Register.BLDALPHA)),
|
|
0x053 => GetHighByte(_video!.ReadRegister(Video.Register.BLDALPHA)),
|
|
|
|
0x060 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_L)),
|
|
0x061 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_L)),
|
|
|
|
0x062 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_H)),
|
|
0x063 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_H)),
|
|
|
|
0x064 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_X)),
|
|
0x065 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND1CNT_X)),
|
|
|
|
0x068 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND2CNT_L)),
|
|
0x069 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND2CNT_L)),
|
|
|
|
0x06c => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND2CNT_H)),
|
|
0x06d => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND2CNT_H)),
|
|
|
|
0x070 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_L)),
|
|
0x071 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_L)),
|
|
|
|
0x072 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_H)),
|
|
0x073 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_H)),
|
|
|
|
0x074 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_X)),
|
|
0x075 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND3CNT_X)),
|
|
|
|
0x078 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND4CNT_L)),
|
|
0x079 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND4CNT_L)),
|
|
|
|
0x07c => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUND4CNT_H)),
|
|
0x07d => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUND4CNT_H)),
|
|
|
|
0x080 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_L)),
|
|
0x081 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_L)),
|
|
|
|
0x082 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_H)),
|
|
0x083 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_H)),
|
|
|
|
0x084 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_X)),
|
|
0x085 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUNDCNT_X)),
|
|
|
|
0x088 => GetLowByte(_sound!.ReadRegister(Sound.Register.SOUNDBIAS)),
|
|
0x089 => GetHighByte(_sound!.ReadRegister(Sound.Register.SOUNDBIAS)),
|
|
|
|
0x090 => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM0_L)),
|
|
0x091 => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM0_L)),
|
|
|
|
0x092 => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM0_H)),
|
|
0x093 => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM0_H)),
|
|
|
|
0x094 => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM1_L)),
|
|
0x095 => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM1_L)),
|
|
|
|
0x096 => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM1_H)),
|
|
0x097 => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM1_H)),
|
|
|
|
0x098 => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM2_L)),
|
|
0x099 => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM2_L)),
|
|
|
|
0x09a => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM2_H)),
|
|
0x09b => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM2_H)),
|
|
|
|
0x09c => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM3_L)),
|
|
0x09d => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM3_L)),
|
|
|
|
0x09e => GetLowByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM3_H)),
|
|
0x09f => GetHighByte(_sound!.ReadRegister(Sound.Register.WAVE_RAM3_H)),
|
|
|
|
0x0ba => GetLowByte(_dma!.ReadRegister(DMA.Register.DMA0CNT_H)),
|
|
0x0bb => GetHighByte(_dma!.ReadRegister(DMA.Register.DMA0CNT_H)),
|
|
|
|
0x0c6 => GetLowByte(_dma!.ReadRegister(DMA.Register.DMA1CNT_H)),
|
|
0x0c7 => GetHighByte(_dma!.ReadRegister(DMA.Register.DMA1CNT_H)),
|
|
|
|
0x0d2 => GetLowByte(_dma!.ReadRegister(DMA.Register.DMA2CNT_H)),
|
|
0x0d3 => GetHighByte(_dma!.ReadRegister(DMA.Register.DMA2CNT_H)),
|
|
|
|
0x0de => GetLowByte(_dma!.ReadRegister(DMA.Register.DMA3CNT_H)),
|
|
0x0df => GetHighByte(_dma!.ReadRegister(DMA.Register.DMA3CNT_H)),
|
|
|
|
0x100 => GetLowByte(_timer!.ReadRegister(Timer.Register.TM0CNT_L)),
|
|
0x101 => GetHighByte(_timer!.ReadRegister(Timer.Register.TM0CNT_L)),
|
|
|
|
0x102 => GetLowByte(_timer!.ReadRegister(Timer.Register.TM0CNT_H)),
|
|
0x103 => GetHighByte(_timer!.ReadRegister(Timer.Register.TM0CNT_H)),
|
|
|
|
0x104 => GetLowByte(_timer!.ReadRegister(Timer.Register.TM1CNT_L)),
|
|
0x105 => GetHighByte(_timer!.ReadRegister(Timer.Register.TM1CNT_L)),
|
|
|
|
0x106 => GetLowByte(_timer!.ReadRegister(Timer.Register.TM1CNT_H)),
|
|
0x107 => GetHighByte(_timer!.ReadRegister(Timer.Register.TM1CNT_H)),
|
|
|
|
0x108 => GetLowByte(_timer!.ReadRegister(Timer.Register.TM2CNT_L)),
|
|
0x109 => GetHighByte(_timer!.ReadRegister(Timer.Register.TM2CNT_L)),
|
|
|
|
0x10a => GetLowByte(_timer!.ReadRegister(Timer.Register.TM2CNT_H)),
|
|
0x10b => GetHighByte(_timer!.ReadRegister(Timer.Register.TM2CNT_H)),
|
|
|
|
0x10c => GetLowByte(_timer!.ReadRegister(Timer.Register.TM3CNT_L)),
|
|
0x10d => GetHighByte(_timer!.ReadRegister(Timer.Register.TM3CNT_L)),
|
|
|
|
0x10e => GetLowByte(_timer!.ReadRegister(Timer.Register.TM3CNT_H)),
|
|
0x10f => GetHighByte(_timer!.ReadRegister(Timer.Register.TM3CNT_H)),
|
|
|
|
0x120 => GetLowByte(_communication!.ReadRegister(Communication.Register.SIODATA0)),
|
|
0x121 => GetHighByte(_communication!.ReadRegister(Communication.Register.SIODATA0)),
|
|
|
|
0x122 => GetLowByte(_communication!.ReadRegister(Communication.Register.SIODATA1)),
|
|
0x123 => GetHighByte(_communication!.ReadRegister(Communication.Register.SIODATA1)),
|
|
|
|
0x124 => GetLowByte(_communication!.ReadRegister(Communication.Register.SIODATA2)),
|
|
0x125 => GetHighByte(_communication!.ReadRegister(Communication.Register.SIODATA2)),
|
|
|
|
0x126 => GetLowByte(_communication!.ReadRegister(Communication.Register.SIODATA3)),
|
|
0x127 => GetHighByte(_communication!.ReadRegister(Communication.Register.SIODATA3)),
|
|
|
|
0x128 => GetLowByte(_communication!.ReadRegister(Communication.Register.SIOCNT)),
|
|
0x129 => GetHighByte(_communication!.ReadRegister(Communication.Register.SIOCNT)),
|
|
|
|
0x12a => GetLowByte(_communication!.ReadRegister(Communication.Register.SIODATA_SEND)),
|
|
0x12b => GetHighByte(_communication!.ReadRegister(Communication.Register.SIODATA_SEND)),
|
|
|
|
0x130 => GetLowByte(_keyInput!.ReadRegister(KeyInput.Register.KEYINPUT)),
|
|
0x131 => GetHighByte(_keyInput!.ReadRegister(KeyInput.Register.KEYINPUT)),
|
|
|
|
0x132 => GetLowByte(_keyInput!.ReadRegister(KeyInput.Register.KEYCNT)),
|
|
0x133 => GetHighByte(_keyInput!.ReadRegister(KeyInput.Register.KEYCNT)),
|
|
|
|
0x134 => GetLowByte(_communication!.ReadRegister(Communication.Register.RCNT)),
|
|
0x135 => GetHighByte(_communication!.ReadRegister(Communication.Register.RCNT)),
|
|
|
|
0x200 => GetLowByte(_interruptControl!.ReadRegister(InterruptControl.Register.IE)),
|
|
0x201 => GetHighByte(_interruptControl!.ReadRegister(InterruptControl.Register.IE)),
|
|
|
|
0x202 => GetLowByte(_interruptControl!.ReadRegister(InterruptControl.Register.IF)),
|
|
0x203 => GetHighByte(_interruptControl!.ReadRegister(InterruptControl.Register.IF)),
|
|
|
|
0x204 => GetLowByte(_systemControl!.ReadRegister(SystemControl.Register.WAITCNT)),
|
|
0x205 => GetHighByte(_systemControl!.ReadRegister(SystemControl.Register.WAITCNT)),
|
|
|
|
0x208 => GetLowByte(_interruptControl!.ReadRegister(InterruptControl.Register.IME)),
|
|
0x209 => GetHighByte(_interruptControl!.ReadRegister(InterruptControl.Register.IME)),
|
|
|
|
0x300 => GetLowByte(_systemControl!.ReadRegister(SystemControl.Register.SYSCNT_UND0)),
|
|
0x301 => GetHighByte(_systemControl!.ReadRegister(SystemControl.Register.SYSCNT_UND0)),
|
|
|
|
_ => 0,
|
|
};
|
|
}
|
|
|
|
// ROM wait state 0
|
|
case 0x8:
|
|
case 0x9:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState0_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadByte
|
|
return Unsafe.Read<Byte>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 1
|
|
case 0xa:
|
|
case 0xb:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState1_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadByte
|
|
return Unsafe.Read<Byte>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 2
|
|
case 0xc:
|
|
case 0xd:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState2_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadByte
|
|
return Unsafe.Read<Byte>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// SRAM/Flash
|
|
case 0xe:
|
|
return address switch
|
|
{
|
|
0xe00_0000 => 0x62,
|
|
0xe00_0001 => 0x13,
|
|
_ => 0,
|
|
};
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal UInt16 Read16(UInt32 address)
|
|
{
|
|
address &= 0x0fff_fffe;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_read16PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt16
|
|
return Unsafe.Read<UInt16>((Byte*)page + (address & 0x3ff));
|
|
}
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
return _bios!.Read16(address);
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
return offset switch
|
|
{
|
|
0x000 => _video!.ReadRegister(Video.Register.DISPCNT),
|
|
0x004 => _video!.ReadRegister(Video.Register.DISPSTAT),
|
|
0x006 => _video!.ReadRegister(Video.Register.VCOUNT),
|
|
0x008 => _video!.ReadRegister(Video.Register.BG0CNT),
|
|
0x00a => _video!.ReadRegister(Video.Register.BG1CNT),
|
|
0x00c => _video!.ReadRegister(Video.Register.BG2CNT),
|
|
0x00e => _video!.ReadRegister(Video.Register.BG3CNT),
|
|
0x048 => _video!.ReadRegister(Video.Register.WININ),
|
|
0x04a => _video!.ReadRegister(Video.Register.WINOUT),
|
|
0x050 => _video!.ReadRegister(Video.Register.BLDCNT),
|
|
0x052 => _video!.ReadRegister(Video.Register.BLDALPHA),
|
|
0x060 => _sound!.ReadRegister(Sound.Register.SOUND1CNT_L),
|
|
0x062 => _sound!.ReadRegister(Sound.Register.SOUND1CNT_H),
|
|
0x064 => _sound!.ReadRegister(Sound.Register.SOUND1CNT_X),
|
|
0x068 => _sound!.ReadRegister(Sound.Register.SOUND2CNT_L),
|
|
0x06c => _sound!.ReadRegister(Sound.Register.SOUND2CNT_H),
|
|
0x070 => _sound!.ReadRegister(Sound.Register.SOUND3CNT_L),
|
|
0x072 => _sound!.ReadRegister(Sound.Register.SOUND3CNT_H),
|
|
0x074 => _sound!.ReadRegister(Sound.Register.SOUND3CNT_X),
|
|
0x078 => _sound!.ReadRegister(Sound.Register.SOUND4CNT_L),
|
|
0x07c => _sound!.ReadRegister(Sound.Register.SOUND4CNT_H),
|
|
0x080 => _sound!.ReadRegister(Sound.Register.SOUNDCNT_L),
|
|
0x082 => _sound!.ReadRegister(Sound.Register.SOUNDCNT_H),
|
|
0x084 => _sound!.ReadRegister(Sound.Register.SOUNDCNT_X),
|
|
0x088 => _sound!.ReadRegister(Sound.Register.SOUNDBIAS),
|
|
0x090 => _sound!.ReadRegister(Sound.Register.WAVE_RAM0_L),
|
|
0x092 => _sound!.ReadRegister(Sound.Register.WAVE_RAM0_H),
|
|
0x094 => _sound!.ReadRegister(Sound.Register.WAVE_RAM1_L),
|
|
0x096 => _sound!.ReadRegister(Sound.Register.WAVE_RAM1_H),
|
|
0x098 => _sound!.ReadRegister(Sound.Register.WAVE_RAM2_L),
|
|
0x09a => _sound!.ReadRegister(Sound.Register.WAVE_RAM2_H),
|
|
0x09c => _sound!.ReadRegister(Sound.Register.WAVE_RAM3_L),
|
|
0x09e => _sound!.ReadRegister(Sound.Register.WAVE_RAM3_H),
|
|
0x0ba => _dma!.ReadRegister(DMA.Register.DMA0CNT_H),
|
|
0x0c6 => _dma!.ReadRegister(DMA.Register.DMA1CNT_H),
|
|
0x0d2 => _dma!.ReadRegister(DMA.Register.DMA2CNT_H),
|
|
0x0de => _dma!.ReadRegister(DMA.Register.DMA3CNT_H),
|
|
0x100 => _timer!.ReadRegister(Timer.Register.TM0CNT_L),
|
|
0x102 => _timer!.ReadRegister(Timer.Register.TM0CNT_H),
|
|
0x104 => _timer!.ReadRegister(Timer.Register.TM1CNT_L),
|
|
0x106 => _timer!.ReadRegister(Timer.Register.TM1CNT_H),
|
|
0x108 => _timer!.ReadRegister(Timer.Register.TM2CNT_L),
|
|
0x10a => _timer!.ReadRegister(Timer.Register.TM2CNT_H),
|
|
0x10c => _timer!.ReadRegister(Timer.Register.TM3CNT_L),
|
|
0x10e => _timer!.ReadRegister(Timer.Register.TM3CNT_H),
|
|
0x120 => _communication!.ReadRegister(Communication.Register.SIODATA0),
|
|
0x122 => _communication!.ReadRegister(Communication.Register.SIODATA1),
|
|
0x124 => _communication!.ReadRegister(Communication.Register.SIODATA2),
|
|
0x126 => _communication!.ReadRegister(Communication.Register.SIODATA3),
|
|
0x128 => _communication!.ReadRegister(Communication.Register.SIOCNT),
|
|
0x12a => _communication!.ReadRegister(Communication.Register.SIODATA_SEND),
|
|
0x130 => _keyInput!.ReadRegister(KeyInput.Register.KEYINPUT),
|
|
0x132 => _keyInput!.ReadRegister(KeyInput.Register.KEYCNT),
|
|
0x134 => _communication!.ReadRegister(Communication.Register.RCNT),
|
|
0x200 => _interruptControl!.ReadRegister(InterruptControl.Register.IE),
|
|
0x202 => _interruptControl!.ReadRegister(InterruptControl.Register.IF),
|
|
0x204 => _systemControl!.ReadRegister(SystemControl.Register.WAITCNT),
|
|
0x208 => _interruptControl!.ReadRegister(InterruptControl.Register.IME),
|
|
0x300 => _systemControl!.ReadRegister(SystemControl.Register.SYSCNT_UND0),
|
|
_ => 0,
|
|
};
|
|
}
|
|
|
|
// ROM wait state 0
|
|
case 0x8:
|
|
case 0x9:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState0_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt16
|
|
return Unsafe.Read<UInt16>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 1
|
|
case 0xa:
|
|
case 0xb:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState1_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt16
|
|
return Unsafe.Read<UInt16>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 2
|
|
case 0xc:
|
|
case 0xd:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState2_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt16
|
|
return Unsafe.Read<UInt16>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal UInt32 Read32(UInt32 address)
|
|
{
|
|
address &= 0x0fff_fffc;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_read32PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt32
|
|
return Unsafe.Read<UInt32>((Byte*)page + (address & 0x3ff));
|
|
}
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
return _bios!.Read32(address);
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
return offset switch
|
|
{
|
|
0x000 => _video!.ReadRegister(Video.Register.DISPCNT),
|
|
0x004 => (UInt32)((_video!.ReadRegister(Video.Register.VCOUNT) << 16) | _video.ReadRegister(Video.Register.DISPSTAT)),
|
|
0x008 => (UInt32)((_video!.ReadRegister(Video.Register.BG1CNT) << 16) | _video.ReadRegister(Video.Register.BG0CNT)),
|
|
0x00c => (UInt32)((_video!.ReadRegister(Video.Register.BG3CNT) << 16) | _video.ReadRegister(Video.Register.BG2CNT)),
|
|
0x0b8 => (UInt32)(_dma!.ReadRegister(DMA.Register.DMA0CNT_H) << 16),
|
|
0x0c4 => (UInt32)(_dma!.ReadRegister(DMA.Register.DMA1CNT_H) << 16),
|
|
0x0d0 => (UInt32)(_dma!.ReadRegister(DMA.Register.DMA2CNT_H) << 16),
|
|
0x0dc => (UInt32)(_dma!.ReadRegister(DMA.Register.DMA3CNT_H) << 16),
|
|
0x150 => (UInt32)((_communication.ReadRegister(Communication.Register.JOY_RECV_H) << 16) | _communication.ReadRegister(Communication.Register.JOY_RECV_L)),
|
|
0x200 => (UInt32)((_interruptControl!.ReadRegister(InterruptControl.Register.IF) << 16) | _interruptControl.ReadRegister(InterruptControl.Register.IE)),
|
|
0x208 => _interruptControl!.ReadRegister(InterruptControl.Register.IME),
|
|
_ => 0,
|
|
};
|
|
}
|
|
|
|
// ROM wait state 0
|
|
case 0x8:
|
|
case 0x9:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState0_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt32
|
|
return Unsafe.Read<UInt32>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 1
|
|
case 0xa:
|
|
case 0xb:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState1_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt32
|
|
return Unsafe.Read<UInt32>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM wait state 2
|
|
case 0xc:
|
|
case 0xd:
|
|
{
|
|
UInt32 offset = address - ROM_WaitState2_StartAddress;
|
|
|
|
if (offset < _romSize)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.ReadInt32
|
|
return Unsafe.Read<UInt32>((Byte*)_rom + offset);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal enum RegisterWriteMode
|
|
{
|
|
LowByte,
|
|
HighByte,
|
|
HalfWord
|
|
}
|
|
|
|
internal static void WriteRegisterHelper(ref UInt16 registerValue, UInt16 value, RegisterWriteMode mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case RegisterWriteMode.LowByte:
|
|
registerValue = (UInt16)((registerValue & 0xff00) | value);
|
|
break;
|
|
case RegisterWriteMode.HighByte:
|
|
registerValue = (UInt16)((registerValue & 0x00ff) | (value << 8));
|
|
break;
|
|
case RegisterWriteMode.HalfWord:
|
|
registerValue = value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal void Write8(UInt32 address, Byte value)
|
|
{
|
|
address &= 0x0fff_ffff;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_write8PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.WriteByte
|
|
Unsafe.Write<Byte>((Byte*)page + (address & 0x3ff), value);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
break;
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0x000:
|
|
_video!.WriteRegister(Video.Register.DISPCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x001:
|
|
_video!.WriteRegister(Video.Register.DISPCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x004:
|
|
_video!.WriteRegister(Video.Register.DISPSTAT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x005:
|
|
_video!.WriteRegister(Video.Register.DISPSTAT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x008:
|
|
_video!.WriteRegister(Video.Register.BG0CNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x009:
|
|
_video!.WriteRegister(Video.Register.BG0CNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x00a:
|
|
_video!.WriteRegister(Video.Register.BG1CNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x00b:
|
|
_video!.WriteRegister(Video.Register.BG1CNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x00c:
|
|
_video!.WriteRegister(Video.Register.BG2CNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x00d:
|
|
_video!.WriteRegister(Video.Register.BG2CNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x00e:
|
|
_video!.WriteRegister(Video.Register.BG3CNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x00f:
|
|
_video!.WriteRegister(Video.Register.BG3CNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x010:
|
|
_video!.WriteRegister(Video.Register.BG0HOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x011:
|
|
_video!.WriteRegister(Video.Register.BG0HOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x012:
|
|
_video!.WriteRegister(Video.Register.BG0VOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x013:
|
|
_video!.WriteRegister(Video.Register.BG0VOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x014:
|
|
_video!.WriteRegister(Video.Register.BG1HOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x015:
|
|
_video!.WriteRegister(Video.Register.BG1HOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x016:
|
|
_video!.WriteRegister(Video.Register.BG1VOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x017:
|
|
_video!.WriteRegister(Video.Register.BG1VOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x018:
|
|
_video!.WriteRegister(Video.Register.BG2HOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x019:
|
|
_video!.WriteRegister(Video.Register.BG2HOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x01a:
|
|
_video!.WriteRegister(Video.Register.BG2VOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x01b:
|
|
_video!.WriteRegister(Video.Register.BG2VOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x01c:
|
|
_video!.WriteRegister(Video.Register.BG3HOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x01d:
|
|
_video!.WriteRegister(Video.Register.BG3HOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x01e:
|
|
_video!.WriteRegister(Video.Register.BG3VOFS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x01f:
|
|
_video!.WriteRegister(Video.Register.BG3VOFS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x040:
|
|
_video!.WriteRegister(Video.Register.WIN0H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x041:
|
|
_video!.WriteRegister(Video.Register.WIN0H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x042:
|
|
_video!.WriteRegister(Video.Register.WIN1H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x043:
|
|
_video!.WriteRegister(Video.Register.WIN1H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x044:
|
|
_video!.WriteRegister(Video.Register.WIN0V, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x045:
|
|
_video!.WriteRegister(Video.Register.WIN0V, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x046:
|
|
_video!.WriteRegister(Video.Register.WIN1V, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x047:
|
|
_video!.WriteRegister(Video.Register.WIN1V, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x048:
|
|
_video!.WriteRegister(Video.Register.WININ, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x049:
|
|
_video!.WriteRegister(Video.Register.WININ, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x04a:
|
|
_video!.WriteRegister(Video.Register.WINOUT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x04b:
|
|
_video!.WriteRegister(Video.Register.WINOUT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x04c:
|
|
_video!.WriteRegister(Video.Register.MOSAIC, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x04d:
|
|
_video!.WriteRegister(Video.Register.MOSAIC, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x050:
|
|
_video!.WriteRegister(Video.Register.BLDCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x051:
|
|
_video!.WriteRegister(Video.Register.BLDCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x052:
|
|
_video!.WriteRegister(Video.Register.BLDALPHA, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x053:
|
|
_video!.WriteRegister(Video.Register.BLDALPHA, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x054:
|
|
_video!.WriteRegister(Video.Register.BLDY, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x055:
|
|
_video!.WriteRegister(Video.Register.BLDY, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x060:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x061:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x062:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x063:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x064:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_X, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x065:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_X, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x068:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x069:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x06c:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x06d:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x070:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x071:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x072:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x073:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x074:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_X, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x075:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_X, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x078:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x079:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x07c:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x07d:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x080:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x081:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x082:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x083:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x084:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_X, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x085:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_X, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x088:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDBIAS, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x089:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDBIAS, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x090:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x091:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x092:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x093:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x094:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x095:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x096:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x097:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x098:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x099:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x09a:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x09b:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x09c:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x09d:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x09e:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x09f:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0b0:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0b1:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0b2:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0b3:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0b4:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0b5:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0b6:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0b7:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0b8:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0b9:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0ba:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0bb:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0bc:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0bd:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0be:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0bf:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0c0:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0c1:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0c2:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0c3:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0c4:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0c5:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0c6:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0c7:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0c8:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0c9:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0ca:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0cb:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0cc:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0cd:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0ce:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0cf:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0d0:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0d1:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0d2:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0d3:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0d4:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0d5:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0d6:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0d7:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0d8:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0d9:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0da:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0db:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0dc:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0dd:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x0de:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x0df:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x100:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x101:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x102:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x103:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x104:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x105:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x106:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x107:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x108:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x109:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x10a:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x10b:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x10c:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_L, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x10d:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_L, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x10e:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_H, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x10f:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_H, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x120:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA0, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x121:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA0, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x122:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA1, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x123:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA1, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x124:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA2, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x125:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA2, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x126:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA3, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x127:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA3, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x128:
|
|
_communication!.WriteRegister(Communication.Register.SIOCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x129:
|
|
_communication!.WriteRegister(Communication.Register.SIOCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x12a:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA_SEND, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x12b:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA_SEND, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x130:
|
|
case 0x131:
|
|
// KEYINPUT (read-only)
|
|
break;
|
|
|
|
case 0x132:
|
|
_keyInput!.WriteRegister(KeyInput.Register.KEYCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x133:
|
|
_keyInput!.WriteRegister(KeyInput.Register.KEYCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x134:
|
|
_communication!.WriteRegister(Communication.Register.RCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x135:
|
|
_communication!.WriteRegister(Communication.Register.RCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x140:
|
|
_communication!.WriteRegister(Communication.Register.JOYCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x141:
|
|
_communication!.WriteRegister(Communication.Register.JOYCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x200:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IE, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x201:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IE, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x202:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IF, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x203:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IF, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x204:
|
|
_systemControl!.WriteRegister(SystemControl.Register.WAITCNT, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x205:
|
|
_systemControl!.WriteRegister(SystemControl.Register.WAITCNT, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x208:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IME, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x209:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IME, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x300:
|
|
_systemControl!.WriteRegister(SystemControl.Register.SYSCNT_UND0, value, RegisterWriteMode.LowByte);
|
|
break;
|
|
case 0x301:
|
|
_systemControl!.WriteRegister(SystemControl.Register.SYSCNT_UND0, value, RegisterWriteMode.HighByte);
|
|
break;
|
|
|
|
case 0x410:
|
|
// undocumented
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Palette RAM
|
|
case 0x5:
|
|
_video!.Write8_PaletteRAM(address, value);
|
|
break;
|
|
|
|
// VRAM
|
|
case 0x6:
|
|
_video!.Write8_VRAM(address, value);
|
|
break;
|
|
|
|
// OAM
|
|
case 0x7:
|
|
break;
|
|
|
|
// ROM
|
|
case 0x8:
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal void Write16(UInt32 address, UInt16 value)
|
|
{
|
|
address &= 0x0fff_fffe;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_write16PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.WriteInt16
|
|
Unsafe.Write<UInt16>((Byte*)page + (address & 0x3ff), value);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
break;
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0x000:
|
|
_video!.WriteRegister(Video.Register.DISPCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x004:
|
|
_video!.WriteRegister(Video.Register.DISPSTAT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x008:
|
|
_video!.WriteRegister(Video.Register.BG0CNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x00a:
|
|
_video!.WriteRegister(Video.Register.BG1CNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x00c:
|
|
_video!.WriteRegister(Video.Register.BG2CNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x00e:
|
|
_video!.WriteRegister(Video.Register.BG3CNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x010:
|
|
_video!.WriteRegister(Video.Register.BG0HOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x012:
|
|
_video!.WriteRegister(Video.Register.BG0VOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x014:
|
|
_video!.WriteRegister(Video.Register.BG1HOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x016:
|
|
_video!.WriteRegister(Video.Register.BG1VOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x018:
|
|
_video!.WriteRegister(Video.Register.BG2HOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x01a:
|
|
_video!.WriteRegister(Video.Register.BG2VOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x01c:
|
|
_video!.WriteRegister(Video.Register.BG3HOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x01e:
|
|
_video!.WriteRegister(Video.Register.BG3VOFS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x020:
|
|
_video!.WriteRegister(Video.Register.BG2PA, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x022:
|
|
_video!.WriteRegister(Video.Register.BG2PB, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x024:
|
|
_video!.WriteRegister(Video.Register.BG2PC, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x026:
|
|
_video!.WriteRegister(Video.Register.BG2PD, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x028:
|
|
_video!.WriteRegister(Video.Register.BG2X_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x02a:
|
|
_video!.WriteRegister(Video.Register.BG2X_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x02c:
|
|
_video!.WriteRegister(Video.Register.BG2Y_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x02e:
|
|
_video!.WriteRegister(Video.Register.BG2Y_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x030:
|
|
_video!.WriteRegister(Video.Register.BG3PA, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x032:
|
|
_video!.WriteRegister(Video.Register.BG3PB, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x034:
|
|
_video!.WriteRegister(Video.Register.BG3PC, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x036:
|
|
_video!.WriteRegister(Video.Register.BG3PD, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x038:
|
|
_video!.WriteRegister(Video.Register.BG3X_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x03a:
|
|
_video!.WriteRegister(Video.Register.BG3X_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x03c:
|
|
_video!.WriteRegister(Video.Register.BG3Y_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x03e:
|
|
_video!.WriteRegister(Video.Register.BG3Y_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x040:
|
|
_video!.WriteRegister(Video.Register.WIN0H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x042:
|
|
_video!.WriteRegister(Video.Register.WIN1H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x044:
|
|
_video!.WriteRegister(Video.Register.WIN0V, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x046:
|
|
_video!.WriteRegister(Video.Register.WIN1V, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x048:
|
|
_video!.WriteRegister(Video.Register.WININ, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x04a:
|
|
_video!.WriteRegister(Video.Register.WINOUT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x04c:
|
|
_video!.WriteRegister(Video.Register.MOSAIC, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x050:
|
|
_video!.WriteRegister(Video.Register.BLDCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x052:
|
|
_video!.WriteRegister(Video.Register.BLDALPHA, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x054:
|
|
_video!.WriteRegister(Video.Register.BLDY, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x060:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x062:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x064:
|
|
_sound!.WriteRegister(Sound.Register.SOUND1CNT_X, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x068:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x06c:
|
|
_sound!.WriteRegister(Sound.Register.SOUND2CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x070:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x072:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x074:
|
|
_sound!.WriteRegister(Sound.Register.SOUND3CNT_X, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x078:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x07c:
|
|
_sound!.WriteRegister(Sound.Register.SOUND4CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x080:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x082:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x084:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_X, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x088:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDBIAS, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x090:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x092:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x094:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x096:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x098:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x09a:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x09c:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x09e:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b0:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b2:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b4:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b6:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b8:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0ba:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0bc:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0be:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c0:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c2:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c4:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c6:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c8:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0ca:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0cc:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0ce:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d0:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d2:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d4:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d6:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d8:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0da:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0dc:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0de:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x100:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x102:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x104:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x106:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x108:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x10a:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x10c:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_L, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x10e:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_H, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x120:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA0, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x122:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA1, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x124:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA2, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x126:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA3, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x128:
|
|
_communication!.WriteRegister(Communication.Register.SIOCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x12a:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA_SEND, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x130:
|
|
// KEYINPUT (read-only)
|
|
break;
|
|
case 0x132:
|
|
_keyInput!.WriteRegister(KeyInput.Register.KEYCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x134:
|
|
_communication!.WriteRegister(Communication.Register.RCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x140:
|
|
_communication!.WriteRegister(Communication.Register.JOYCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x158:
|
|
_communication!.WriteRegister(Communication.Register.JOYSTAT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x200:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IE, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x202:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IF, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x204:
|
|
_systemControl!.WriteRegister(SystemControl.Register.WAITCNT, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x208:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IME, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x300:
|
|
_systemControl!.WriteRegister(SystemControl.Register.SYSCNT_UND0, value, RegisterWriteMode.HalfWord);
|
|
break;
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM
|
|
case 0x8:
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal void Write32(UInt32 address, UInt32 value)
|
|
{
|
|
address &= 0x0fff_fffc;
|
|
|
|
IntPtr page = Unsafe.Add(ref MyUnSafeCommon.GetArrayDataReference(_write32PageTable), (int)(address >> 10));
|
|
|
|
if (page != IntPtr.Zero)
|
|
{
|
|
unsafe
|
|
{
|
|
// much faster than Marshal.WriteInt32
|
|
Unsafe.Write<UInt32>((Byte*)page + (address & 0x3ff), value);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// page fault
|
|
switch (address >> 24)
|
|
{
|
|
// BIOS
|
|
case 0x0:
|
|
case 0x1:
|
|
break;
|
|
|
|
// IO and registers
|
|
case 0x4:
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
static UInt16 GetLowHalfword(UInt32 value) => (UInt16)value;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
static UInt16 GetHighHalfword(UInt32 value) => (UInt16)(value >> 16);
|
|
|
|
UInt32 offset = address - 0x400_0000;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0x000:
|
|
_video!.WriteRegister(Video.Register.DISPCNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are undocumented (green swap register)
|
|
break;
|
|
case 0x004:
|
|
_video!.WriteRegister(Video.Register.DISPSTAT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x008:
|
|
_video!.WriteRegister(Video.Register.BG0CNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG1CNT, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x00c:
|
|
_video!.WriteRegister(Video.Register.BG2CNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3CNT, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x010:
|
|
_video!.WriteRegister(Video.Register.BG0HOFS, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG0VOFS, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x014:
|
|
_video!.WriteRegister(Video.Register.BG1HOFS, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG1VOFS, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x018:
|
|
_video!.WriteRegister(Video.Register.BG2HOFS, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG2VOFS, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x01c:
|
|
_video!.WriteRegister(Video.Register.BG3HOFS, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3VOFS, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x020:
|
|
_video!.WriteRegister(Video.Register.BG2PA, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG2PB, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x024:
|
|
_video!.WriteRegister(Video.Register.BG2PC, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG2PD, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x028:
|
|
_video!.WriteRegister(Video.Register.BG2X_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG2X_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x02c:
|
|
_video!.WriteRegister(Video.Register.BG2Y_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG2Y_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x030:
|
|
_video!.WriteRegister(Video.Register.BG3PA, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3PB, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x034:
|
|
_video!.WriteRegister(Video.Register.BG3PC, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3PD, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x038:
|
|
_video!.WriteRegister(Video.Register.BG3X_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3X_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x03c:
|
|
_video!.WriteRegister(Video.Register.BG3Y_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BG3Y_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x040:
|
|
_video!.WriteRegister(Video.Register.WIN0H, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.WIN1H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x044:
|
|
_video!.WriteRegister(Video.Register.WIN0V, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.WIN1V, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x048:
|
|
_video!.WriteRegister(Video.Register.WININ, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.WINOUT, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x04c:
|
|
_video!.WriteRegister(Video.Register.MOSAIC, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x050:
|
|
_video!.WriteRegister(Video.Register.BLDCNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_video!.WriteRegister(Video.Register.BLDALPHA, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x054:
|
|
_video!.WriteRegister(Video.Register.BLDY, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x58:
|
|
case 0x5c:
|
|
// unused
|
|
break;
|
|
case 0x080:
|
|
_sound!.WriteRegister(Sound.Register.SOUNDCNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.SOUNDCNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x090:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM0_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.WAVE_RAM0_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x094:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM1_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.WAVE_RAM1_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x098:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM2_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.WAVE_RAM2_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x09c:
|
|
_sound!.WriteRegister(Sound.Register.WAVE_RAM3_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.WAVE_RAM3_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0a0:
|
|
_sound!.WriteRegister(Sound.Register.FIFO_A_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.FIFO_A_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0a4:
|
|
_sound!.WriteRegister(Sound.Register.FIFO_B_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_sound.WriteRegister(Sound.Register.FIFO_B_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0a8:
|
|
case 0x0ac:
|
|
// unused
|
|
break;
|
|
case 0x0b0:
|
|
_dma!.WriteRegister(DMA.Register.DMA0SAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA0SAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b4:
|
|
_dma!.WriteRegister(DMA.Register.DMA0DAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA0DAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0b8:
|
|
_dma!.WriteRegister(DMA.Register.DMA0CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA0CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0bc:
|
|
_dma!.WriteRegister(DMA.Register.DMA1SAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA1SAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c0:
|
|
_dma!.WriteRegister(DMA.Register.DMA1DAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA1DAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c4:
|
|
_dma!.WriteRegister(DMA.Register.DMA1CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA1CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0c8:
|
|
_dma!.WriteRegister(DMA.Register.DMA2SAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA2SAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0cc:
|
|
_dma!.WriteRegister(DMA.Register.DMA2DAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA2DAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d0:
|
|
_dma!.WriteRegister(DMA.Register.DMA2CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA2CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d4:
|
|
_dma!.WriteRegister(DMA.Register.DMA3SAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA3SAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0d8:
|
|
_dma!.WriteRegister(DMA.Register.DMA3DAD_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA3DAD_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0dc:
|
|
_dma!.WriteRegister(DMA.Register.DMA3CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_dma.WriteRegister(DMA.Register.DMA3CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x0e0:
|
|
case 0x0e4:
|
|
case 0x0e8:
|
|
case 0x0ec:
|
|
case 0x0f0:
|
|
case 0x0f4:
|
|
case 0x0f8:
|
|
case 0x0fc:
|
|
// unused
|
|
break;
|
|
case 0x100:
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_timer!.WriteRegister(Timer.Register.TM0CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x104:
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_timer!.WriteRegister(Timer.Register.TM1CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x108:
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_timer!.WriteRegister(Timer.Register.TM2CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x10c:
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_timer!.WriteRegister(Timer.Register.TM3CNT_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x110:
|
|
case 0x114:
|
|
case 0x118:
|
|
case 0x11c:
|
|
// unused
|
|
break;
|
|
case 0x120:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA0, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_communication.WriteRegister(Communication.Register.SIODATA1, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x124:
|
|
_communication!.WriteRegister(Communication.Register.SIODATA2, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_communication.WriteRegister(Communication.Register.SIODATA3, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x128:
|
|
_communication!.WriteRegister(Communication.Register.SIOCNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_communication.WriteRegister(Communication.Register.SIODATA_SEND, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x12c:
|
|
// unused
|
|
break;
|
|
case 0x130:
|
|
// 16 lower bits are read-only (KEYINPUT)
|
|
_keyInput!.WriteRegister(KeyInput.Register.KEYCNT, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x140:
|
|
_communication!.WriteRegister(Communication.Register.JOYCNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x144:
|
|
case 0x148:
|
|
case 0x14c:
|
|
// unused
|
|
break;
|
|
case 0x150:
|
|
_communication!.WriteRegister(Communication.Register.JOY_RECV_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_communication.WriteRegister(Communication.Register.JOY_RECV_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x154:
|
|
_communication!.WriteRegister(Communication.Register.JOY_TRANS_L, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_communication.WriteRegister(Communication.Register.JOY_TRANS_H, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x158:
|
|
_communication!.WriteRegister(Communication.Register.JOYSTAT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x15c:
|
|
// unused
|
|
break;
|
|
case 0x200:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IE, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IF, GetHighHalfword(value), RegisterWriteMode.HalfWord);
|
|
break;
|
|
case 0x204:
|
|
_systemControl!.WriteRegister(SystemControl.Register.WAITCNT, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x208:
|
|
_interruptControl!.WriteRegister(InterruptControl.Register.IME, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x20c:
|
|
case 0x210:
|
|
case 0x214:
|
|
case 0x218:
|
|
case 0x21c:
|
|
// unused
|
|
break;
|
|
case 0x300:
|
|
_systemControl!.WriteRegister(SystemControl.Register.SYSCNT_UND0, GetLowHalfword(value), RegisterWriteMode.HalfWord);
|
|
// 16 upper bits are unused
|
|
break;
|
|
case 0x800:
|
|
// 16 lower bits are undocumented (internal memory control)
|
|
// 16 upper bits are unused
|
|
break;
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ROM
|
|
case 0x8:
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"[Iris.GBA.Memory] Unhandled write to address 0x{address:x8}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|