GBA.Unity/Assets/emulator/MemoryNds9.cs

623 lines
32 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Collections.Concurrent;
using static OptimeGBA.Bits;
using System.Runtime.InteropServices;
using static OptimeGBA.MemoryUtil;
using static Util;
namespace OptimeGBA
{
public sealed unsafe class MemoryNds9 : Memory
{
Nds Nds;
public MemoryNds9(Nds nds, ProviderNds provider)
{
Nds = nds;
SaveProvider = new NullSaveProvider();
for (uint i = 0; i < Arm9BiosSize && i < provider.Bios9.Length; i++)
{
Arm9Bios[i] = provider.Bios9[i];
}
}
public const int Arm9BiosSize = 4096;
public byte[] Arm9Bios = new byte[Arm9BiosSize];
public const int ItcmSize = 32768;
public byte[] Itcm = new byte[ItcmSize];
public const int DtcmSize = 16384;
public byte[] Dtcm = new byte[DtcmSize];
public uint DtcmBase = 0;
public uint ItcmVirtualSize = 0;
public uint DtcmVirtualSize = 0;
public bool ItcmLoadMode = false;
public bool DtcmLoadMode = false;
public override void InitPageTable(byte*[] table, uint[] maskTable, bool write)
{
byte* mainRam = TryPinByteArray(Nds.MainRam);
byte* arm9Bios = TryPinByteArray(Arm9Bios);
byte* dtcm = TryPinByteArray(Dtcm);
byte* itcm = TryPinByteArray(Itcm);
// 12 bits shaved off already, shave off another 12 to get 24
for (uint i = 0; i < 1048576; i++)
{
table[i] = null; // Clear everything out first, since on ARM9 things can move around
uint addr = (uint)(i << 12);
switch (i >> 12)
{
case 0x2: // Main Memory
table[i] = mainRam;
maskTable[i] = 0x003FFFFF;
break;
case 0xFF: // BIOS
if (!write)
{
table[i] = arm9Bios;
}
maskTable[i] = 0x00000FFF;
break;
}
if (addr >= DtcmBase && addr < DtcmBase + DtcmVirtualSize)
{
if (write || !DtcmLoadMode)
{
// Console.WriteLine("DTCM page set at " + Util.Hex(addr, 8));
table[i] = dtcm;
}
maskTable[i] = 0x00003FFF;
}
// ITCM is immovable
// ITCM has higher priority so write pages in after DTCM
if (addr < ItcmVirtualSize)
{
if (write || !ItcmLoadMode)
{
table[i] = itcm;
}
maskTable[i] = 0x00007FFF;
}
}
}
~MemoryNds9()
{
Console.WriteLine("Cleaning up NDS9 memory...");
UnpinByteArray(Nds.MainRam);
UnpinByteArray(Arm9Bios);
UnpinByteArray(Dtcm);
UnpinByteArray(Itcm);
}
public void UpdateTcmSettings()
{
// Console.WriteLine("Data TCM Settings: " + Util.Hex(Nds.Cp15.DataTcmSettings, 8));
ItcmVirtualSize = 512U << (int)((Nds.Cp15.InstTcmSettings >> 1) & 0x1F);
DtcmVirtualSize = 512U << (int)((Nds.Cp15.DataTcmSettings >> 1) & 0x1F);
DtcmBase = (uint)(Nds.Cp15.DataTcmSettings & 0xFFFFF000);
ItcmLoadMode = BitTest(Nds.Cp15.ControlRegister, 19);
DtcmLoadMode = BitTest(Nds.Cp15.ControlRegister, 17);
Console.WriteLine("ITCM set to: " + Util.Hex(0, 8) + " - " + Util.Hex(ItcmVirtualSize - 1, 8));
Console.WriteLine("DTCM set to: " + Util.Hex(DtcmBase, 8) + " - " + Util.Hex(DtcmBase + DtcmVirtualSize - 1, 8));
InitPageTables();
}
public (byte[] array, uint offset) GetSharedRamParams(uint addr)
{
switch (Nds.MemoryControl.SharedRamControl)
{
case 0:
default:
addr &= 0x7FFF; // All 32k of Shared RAM
return (Nds.SharedRam, addr);
case 1:
addr &= 0x3FFF; // 2nd half of Shared RAM
addr += 0x4000;
return (Nds.SharedRam, addr);
case 2:
addr &= 0x3FFF; // 1st half of Shared RAM
return (Nds.SharedRam, addr);
case 3:
// throw new NotImplementedException("Implement unmapping Shared RAM from ARM9 without EmptyPage, since some game can possibly try to write to the EmptyPage");
EmptyPage[0] = 0;
return (EmptyPage, 0); // Unmapped
}
}
public override byte Read8Unregistered(bool debug, uint addr)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
return GetByte(array, offset);
case 0x4: // I/O Registers
return ReadHwio8(debug, addr);
case 0x5: // PPU Palettes
return Nds.Ppu.ReadPalettes8(addr);
case 0x6: // VRAM
return Nds.Ppu.ReadVram8Arm9(addr);
case 0x7: // PPU OAM
return Nds.Ppu.ReadOam8(addr);
}
return 0;
}
public override ushort Read16Unregistered(bool debug, uint addr)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
return GetUshort(array, offset);
case 0x4: // I/O Registers
byte f0 = ReadHwio8(debug, addr++);
byte f1 = ReadHwio8(debug, addr++);
ushort u16 = (ushort)((f1 << 8) | (f0 << 0));
return u16;
case 0x5: // PPU Palettes
return Nds.Ppu.ReadPalettes16(addr);
case 0x6: // VRAM
return (ushort)(
(Nds.Ppu.ReadVram8Arm9(addr + 0) << 0) |
(Nds.Ppu.ReadVram8Arm9(addr + 1) << 8)
);
case 0x7: // PPU OAM
return Nds.Ppu.ReadOam16(addr);
}
return 0;
}
public override uint Read32Unregistered(bool debug, uint addr)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
return GetUint(array, offset);
case 0x4: // I/O Registers
if (addr >= 0x4000320 && addr < 0x40006A4) // 3D
{
return Nds.Ppu3D.ReadHwio32(addr);
}
byte f0 = ReadHwio8(debug, addr + 0);
byte f1 = ReadHwio8(debug, addr + 1);
byte f2 = ReadHwio8(debug, addr + 2);
byte f3 = ReadHwio8(debug, addr + 3);
uint u32 = (uint)((f3 << 24) | (f2 << 16) | (f1 << 8) | (f0 << 0));
return u32;
case 0x5: // PPU Palettes
return Nds.Ppu.ReadPalettes32(addr);
case 0x6: // VRAM
return (uint)(
(Nds.Ppu.ReadVram8Arm9(addr + 0) << 0) |
(Nds.Ppu.ReadVram8Arm9(addr + 1) << 8) |
(Nds.Ppu.ReadVram8Arm9(addr + 2) << 16) |
(Nds.Ppu.ReadVram8Arm9(addr + 3) << 24)
);
case 0x7: // PPU OAM
return Nds.Ppu.ReadOam32(addr);
}
return 0;
}
public override void Write8Unregistered(bool debug, uint addr, byte val)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
SetByte(array, offset, val);
break;
case 0x4: // I/O Registers
WriteHwio8(debug, addr, val);
break;
case 0x5: // PPU Palettes - duplicated across upper-lower in 8-bit??
Console.WriteLine("NDS: 8-bit write to palettes");
// Nds.Ppu.WritePalettes8(addr + 0, val);
// Nds.Ppu.WritePalettes8(addr + 1, val);
break;
}
}
public override void Write16Unregistered(bool debug, uint addr, ushort val)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
SetUshort(array, offset, val);
break;
case 0x4: // I/O Registers
WriteHwio8(debug, addr++, (byte)(val >> 0));
WriteHwio8(debug, addr++, (byte)(val >> 8));
break;
case 0x5: // PPU Palettes
Nds.Ppu.WritePalettes16(addr, val);
break;
case 0x6: // VRAM
Nds.Ppu.WriteVram8Arm9(addr + 0, (byte)(val >> 0));
Nds.Ppu.WriteVram8Arm9(addr + 1, (byte)(val >> 8));
break;
case 0x7: // PPU OAM
Nds.Ppu.WriteOam16(addr, val);
break;
}
}
public override void Write32Unregistered(bool debug, uint addr, uint val)
{
switch (addr >> 24)
{
case 0x3: // Shared RAM
(byte[] array, uint offset) = GetSharedRamParams(addr);
SetUint(array, offset, val);
break;
case 0x4: // I/O Registers
if (addr >= 0x4000320 && addr < 0x40006A4) // 3D
{
Nds.Ppu3D.WriteHwio32(addr, val);
return;
}
WriteHwio8(debug, addr++, (byte)(val >> 0));
WriteHwio8(debug, addr++, (byte)(val >> 8));
WriteHwio8(debug, addr++, (byte)(val >> 16));
WriteHwio8(debug, addr++, (byte)(val >> 24));
break;
case 0x5: // PPU Palettes
Nds.Ppu.WritePalettes32(addr, val);
break;
case 0x6: // VRAM
Nds.Ppu.WriteVram8Arm9(addr + 0, (byte)(val >> 0));
Nds.Ppu.WriteVram8Arm9(addr + 1, (byte)(val >> 8));
Nds.Ppu.WriteVram8Arm9(addr + 2, (byte)(val >> 16));
Nds.Ppu.WriteVram8Arm9(addr + 3, (byte)(val >> 24));
break;
case 0x7: // PPU OAM
Nds.Ppu.WriteOam32(addr, val);
break;
}
}
public byte ReadHwio8(bool debug, uint addr)
{
if (LogHwioAccesses)
{
lock (HwioReadLog) {
if ((addr & ~1) != 0 && !debug)
{
uint count;
HwioReadLog.TryGetValue(addr, out count);
HwioReadLog[addr] = count + 1;
}
}
}
if (addr >= 0x4000320 && addr < 0x40006A4) // 3D
{
Console.Error.WriteLine("8-bit or 16-bit read to 3D");
return 0;
}
switch (addr)
{
// Engine A
case 0x4000000: case 0x4000001: case 0x4000002: case 0x4000003: // DISPCNT A
case 0x4000004: case 0x4000005: // DISPSTAT
case 0x4000006: case 0x4000007: // VCOUNT
case 0x4000008: case 0x4000009: // BG0CNT
case 0x400000A: case 0x400000B: // BG1CNT
case 0x400000C: case 0x400000D: // BG2CNT
case 0x400000E: case 0x400000F: // BG3CNT
case 0x4000010: case 0x4000011: case 0x4000012: case 0x4000013: // BG0OFS
case 0x4000014: case 0x4000015: case 0x4000016: case 0x4000017: // BG1OFS
case 0x4000018: case 0x4000019: case 0x400001A: case 0x400001B: // BG2OFS
case 0x400001C: case 0x400001D: case 0x400001E: case 0x400001F: // BG3OFS
case 0x4000020: case 0x4000021: case 0x4000022: case 0x4000023: // BG2PA/PB
case 0x4000024: case 0x4000025: case 0x4000026: case 0x4000027: // BG2PC/PD
case 0x4000028: case 0x4000029: case 0x400002A: case 0x400002B: // BG2X
case 0x400002C: case 0x400002D: case 0x400002E: case 0x400002F: // BG2Y
case 0x4000030: case 0x4000031: case 0x4000032: case 0x4000033: // BG3PA/PB
case 0x4000034: case 0x4000035: case 0x4000036: case 0x4000037: // BG3PC/PD
case 0x4000038: case 0x4000039: case 0x400003A: case 0x400003B: // BG3X
case 0x400003C: case 0x400003D: case 0x400003E: case 0x400003F: // BG3Y
case 0x4000040: case 0x4000041: case 0x4000042: case 0x4000043: // WINH
case 0x4000044: case 0x4000045: case 0x4000046: case 0x4000047: // WINV
case 0x4000048: case 0x4000049: case 0x400004A: case 0x400004B: // WININ/OUT
case 0x400004C: case 0x400004D: // MOSAIC
case 0x4000050: case 0x4000051: // BLDCNT
case 0x4000052: case 0x4000053: // BLDALPHA
case 0x4000054: case 0x4000055: // BLDY
case 0x4000060: case 0x4000061: // DISP3DCNT
case 0x4000064: case 0x4000065: case 0x4000066: case 0x4000067: // DISPCAPCNT
case 0x400006C: case 0x400006D: // MASTER_BRIGHT
// Engine B
case 0x4001000: case 0x4001001: case 0x4001002: case 0x4001003: // DISPCNT A
case 0x4001008: case 0x4001009: // BG0CNT
case 0x400100A: case 0x400100B: // BG1CNT
case 0x400100C: case 0x400100D: // BG2CNT
case 0x400100E: case 0x400100F: // BG3CNT
case 0x4001010: case 0x4001011: case 0x4001012: case 0x4001013: // BG0OFS
case 0x4001014: case 0x4001015: case 0x4001016: case 0x4001017: // BG1OFS
case 0x4001018: case 0x4001019: case 0x400101A: case 0x400101B: // BG2OFS
case 0x400101C: case 0x400101D: case 0x400101E: case 0x400101F: // BG3OFS
case 0x4001020: case 0x4001021: case 0x4001022: case 0x4001023: // BG2PA/PB
case 0x4001024: case 0x4001025: case 0x4001026: case 0x4001027: // BG2PC/PD
case 0x4001028: case 0x4001029: case 0x400102A: case 0x400102B: // BG2X
case 0x400102C: case 0x400102D: case 0x400102E: case 0x400102F: // BG2Y
case 0x4001030: case 0x4001031: case 0x4001032: case 0x4001033: // BG3PA/PB
case 0x4001034: case 0x4001035: case 0x4001036: case 0x4001037: // BG3PC/PD
case 0x4001038: case 0x4001039: case 0x400103A: case 0x400103B: // BG3X
case 0x400103C: case 0x400103D: case 0x400103E: case 0x400103F: // BG3Y
case 0x4001040: case 0x4001041: case 0x4001042: case 0x4001043: // WINH
case 0x4001044: case 0x4001045: case 0x4001046: case 0x4001047: // WINV
case 0x4001048: case 0x4001049: case 0x400104A: case 0x400104B: // WININ/OUT
case 0x400104C: case 0x400104D: // MOSAIC
case 0x4001050: case 0x4001051: // BLDCNT
case 0x4001052: case 0x4001053: // BLDALPHA
case 0x4001054: case 0x4001055: // BLDY
case 0x400106C: case 0x400106D: // MASTER_BRIGHT
return Nds.Ppu.ReadHwio8Arm9(addr);
case 0x40000B0: case 0x40000B1: case 0x40000B2: case 0x40000B3: // DMA0SAD
case 0x40000B4: case 0x40000B5: case 0x40000B6: case 0x40000B7: // DMA0DAD
case 0x40000B8: case 0x40000B9: case 0x40000BA: case 0x40000BB: // DMA0CNT
case 0x40000BC: case 0x40000BD: case 0x40000BE: case 0x40000BF: // DMA1SAD
case 0x40000C0: case 0x40000C1: case 0x40000C2: case 0x40000C3: // DMA1DAD
case 0x40000C4: case 0x40000C5: case 0x40000C6: case 0x40000C7: // DMA1CNT
case 0x40000C8: case 0x40000C9: case 0x40000CA: case 0x40000CB: // DMA2SAD
case 0x40000CC: case 0x40000CD: case 0x40000CE: case 0x40000CF: // DMA2DAD
case 0x40000D0: case 0x40000D1: case 0x40000D2: case 0x40000D3: // DMA2CNT
case 0x40000D4: case 0x40000D5: case 0x40000D6: case 0x40000D7: // DMA3SAD
case 0x40000D8: case 0x40000D9: case 0x40000DA: case 0x40000DB: // DMA3DAD
case 0x40000DC: case 0x40000DD: case 0x40000DE: case 0x40000DF: // DMA3CNT
case 0x40000E0: case 0x40000E1: case 0x40000E2: case 0x40000E3: // DMA0 Fill Data
case 0x40000E4: case 0x40000E5: case 0x40000E6: case 0x40000E7: // DMA1 Fill Data
case 0x40000E8: case 0x40000E9: case 0x40000EA: case 0x40000EB: // DMA2 Fill Data
case 0x40000EC: case 0x40000ED: case 0x40000EE: case 0x40000EF: // DMA3 Fill Data
return Nds.Dma9.ReadHwio8(addr);
case 0x4000100: case 0x4000101: case 0x4000102: case 0x4000103: // Timer 0
case 0x4000104: case 0x4000105: case 0x4000106: case 0x4000107: // Timer 1
case 0x4000108: case 0x4000109: case 0x400010A: case 0x400010B: // Timer 2
case 0x400010C: case 0x400010D: case 0x400010E: case 0x400010F: // Timer 3
return Nds.Timers9.ReadHwio8(addr);
case 0x4000180: case 0x4000181: case 0x4000182: case 0x4000183: // IPCSYNC
case 0x4000184: case 0x4000185: case 0x4000186: case 0x4000187: // IPCFIFOCNT
case 0x4000188: case 0x4000189: case 0x400018A: case 0x400018B: // IPCFIFOSEND
case 0x4100000: case 0x4100001: case 0x4100002: case 0x4100003: // IPCFIFORECV
return Nds.Ipcs[0].ReadHwio8(addr);
case 0x40001A0: case 0x40001A1: // AUXSPICNT
case 0x40001A2: case 0x40001A3: // AUXSPIDATA
case 0x40001A4: case 0x40001A5: case 0x40001A6: case 0x40001A7: // ROMCTRL
case 0x4100010: case 0x4100011: case 0x4100012: case 0x4100013: // Slot 1 Data In
return Nds.Cartridge.ReadHwio8(false, addr);
case 0x4000208: case 0x4000209: case 0x400020A: case 0x400020B: // IME
case 0x4000210: case 0x4000211: case 0x4000212: case 0x4000213: // IE
case 0x4000214: case 0x4000215: case 0x4000216: case 0x4000217: // IF
return Nds.HwControl9.ReadHwio8(addr);
case 0x4000130: case 0x4000131: // KEYINPUT
return Nds.Keypad.ReadHwio8(addr);
case 0x4000204: case 0x4000205: // EXMEMCNT
case 0x4000240: case 0x4000241: case 0x4000242: case 0x4000243: // VRAMCNT
case 0x4000244: case 0x4000245: case 0x4000246: case 0x4000247: // VRAMCNT, WRAMCNT
case 0x4000248: case 0x4000249: // VRAMCNT
return Nds.MemoryControl.ReadHwio8Nds9(addr);
case 0x4000280: case 0x4000281: case 0x4000282: case 0x4000283: // DIVCNT B3
case 0x4000290: case 0x4000291: case 0x4000292: case 0x4000293: // DIV_NUMER
case 0x4000294: case 0x4000295: case 0x4000296: case 0x4000297: // DIV_NUMER
case 0x4000298: case 0x4000299: case 0x400029A: case 0x400029B: // DIV_DENOM
case 0x400029C: case 0x400029D: case 0x400029E: case 0x400029F: // DIV_DENOM
case 0x40002A0: case 0x40002A1: case 0x40002A2: case 0x40002A3: // DIV_RESULT
case 0x40002A4: case 0x40002A5: case 0x40002A6: case 0x40002A7: // DIV_RESULT
case 0x40002A8: case 0x40002A9: case 0x40002AA: case 0x40002AB: // DIVREM_RESULT
case 0x40002AC: case 0x40002AD: case 0x40002AE: case 0x40002AF: // DIVREM_RESULT
case 0x40002B0: case 0x40002B1: // SQRTCNT
case 0x40002B4: case 0x40002B5: case 0x40002B6: case 0x40002B7: // SQRT_RESULT
case 0x40002B8: case 0x40002B9: case 0x40002BA: case 0x40002BB: // SQRT_PARAM
case 0x40002BC: case 0x40002BD: case 0x40002BE: case 0x40002BF: // SQRT_PARAM
return Nds.Math.ReadHwio8(addr);
case 0x4000300:
// Console.WriteLine("NDS9 POSTFLG read");
return Nds.HwControl9.Postflg;
case 0x4000304: case 0x4000305: case 0x4000306: case 0x4000307: // POWCNT1
return Nds.ReadHwio8Arm9(addr);
}
// Console.WriteLine($"NDS9: Unmapped MMIO read addr:{Hex(addr, 8)}");
return 0;
}
public void WriteHwio8(bool debug, uint addr, byte val)
{
if (LogHwioAccesses)
{
lock (HwioWriteLog) {
if ((addr & ~1) != 0 && !debug)
{
uint count;
HwioWriteLog.TryGetValue(addr, out count);
HwioWriteLog[addr] = count + 1;
}
}
}
if (addr >= 0x4000320 && addr < 0x40006A4) // 3D
{
// Console.Error.WriteLine($"8-bit or 16-bit write to 3D addr:{Hex(addr, 8)} val:{Hex(val, 2)}");
return;
}
switch (addr)
{
// Engine A
case 0x4000000: case 0x4000001: case 0x4000002: case 0x4000003: // DISPCNT A
case 0x4000004: case 0x4000005: // DISPSTAT
case 0x4000006: case 0x4000007: // VCOUNT
case 0x4000008: case 0x4000009: // BG0CNT
case 0x400000A: case 0x400000B: // BG1CNT
case 0x400000C: case 0x400000D: // BG2CNT
case 0x400000E: case 0x400000F: // BG3CNT
case 0x4000010: case 0x4000011: case 0x4000012: case 0x4000013: // BG0OFS
case 0x4000014: case 0x4000015: case 0x4000016: case 0x4000017: // BG1OFS
case 0x4000018: case 0x4000019: case 0x400001A: case 0x400001B: // BG2OFS
case 0x400001C: case 0x400001D: case 0x400001E: case 0x400001F: // BG3OFS
case 0x4000020: case 0x4000021: case 0x4000022: case 0x4000023: // BG2PA/PB
case 0x4000024: case 0x4000025: case 0x4000026: case 0x4000027: // BG2PC/PD
case 0x4000028: case 0x4000029: case 0x400002A: case 0x400002B: // BG2X
case 0x400002C: case 0x400002D: case 0x400002E: case 0x400002F: // BG2Y
case 0x4000030: case 0x4000031: case 0x4000032: case 0x4000033: // BG3PA/PB
case 0x4000034: case 0x4000035: case 0x4000036: case 0x4000037: // BG3PC/PD
case 0x4000038: case 0x4000039: case 0x400003A: case 0x400003B: // BG3X
case 0x400003C: case 0x400003D: case 0x400003E: case 0x400003F: // BG3Y
case 0x4000040: case 0x4000041: case 0x4000042: case 0x4000043: // WINH
case 0x4000044: case 0x4000045: case 0x4000046: case 0x4000047: // WINV
case 0x4000048: case 0x4000049: case 0x400004A: case 0x400004B: // WININ/OUT
case 0x400004C: case 0x400004D: // MOSAIC
case 0x4000050: case 0x4000051: // BLDCNT
case 0x4000052: case 0x4000053: // BLDALPHA
case 0x4000054: case 0x4000055: // BLDY
case 0x4000060: case 0x4000061: // DISP3DCNT
case 0x4000064: case 0x4000065: case 0x4000066: case 0x4000067: // DISPCAPCNT
case 0x400006C: case 0x400006D: // MASTER_BRIGHT
// Engine B
case 0x4001000: case 0x4001001: case 0x4001002: case 0x4001003: // DISPCNT A
case 0x4001008: case 0x4001009: // BG0CNT
case 0x400100A: case 0x400100B: // BG1CNT
case 0x400100C: case 0x400100D: // BG2CNT
case 0x400100E: case 0x400100F: // BG3CNT
case 0x4001010: case 0x4001011: case 0x4001012: case 0x4001013: // BG0OFS
case 0x4001014: case 0x4001015: case 0x4001016: case 0x4001017: // BG1OFS
case 0x4001018: case 0x4001019: case 0x400101A: case 0x400101B: // BG2OFS
case 0x400101C: case 0x400101D: case 0x400101E: case 0x400101F: // BG3OFS
case 0x4001020: case 0x4001021: case 0x4001022: case 0x4001023: // BG2PA/PB
case 0x4001024: case 0x4001025: case 0x4001026: case 0x4001027: // BG2PC/PD
case 0x4001028: case 0x4001029: case 0x400102A: case 0x400102B: // BG2X
case 0x400102C: case 0x400102D: case 0x400102E: case 0x400102F: // BG2Y
case 0x4001030: case 0x4001031: case 0x4001032: case 0x4001033: // BG3PA/PB
case 0x4001034: case 0x4001035: case 0x4001036: case 0x4001037: // BG3PC/PD
case 0x4001038: case 0x4001039: case 0x400103A: case 0x400103B: // BG3X
case 0x400103C: case 0x400103D: case 0x400103E: case 0x400103F: // BG3Y
case 0x4001040: case 0x4001041: case 0x4001042: case 0x4001043: // WINH
case 0x4001044: case 0x4001045: case 0x4001046: case 0x4001047: // WINV
case 0x4001048: case 0x4001049: case 0x400104A: case 0x400104B: // WININ/OUT
case 0x400104C: case 0x400104D: // MOSAIC
case 0x4001050: case 0x4001051: // BLDCNT
case 0x4001052: case 0x4001053: // BLDALPHA
case 0x4001054: case 0x4001055: // BLDY
case 0x400106C: case 0x400106D: // MASTER_BRIGHT
Nds.Ppu.WriteHwio8Arm9(addr, val); return;
case 0x40000B0: case 0x40000B1: case 0x40000B2: case 0x40000B3: // DMA0SAD
case 0x40000B4: case 0x40000B5: case 0x40000B6: case 0x40000B7: // DMA0DAD
case 0x40000B8: case 0x40000B9: case 0x40000BA: case 0x40000BB: // DMA0CNT
case 0x40000BC: case 0x40000BD: case 0x40000BE: case 0x40000BF: // DMA1SAD
case 0x40000C0: case 0x40000C1: case 0x40000C2: case 0x40000C3: // DMA1DAD
case 0x40000C4: case 0x40000C5: case 0x40000C6: case 0x40000C7: // DMA1CNT
case 0x40000C8: case 0x40000C9: case 0x40000CA: case 0x40000CB: // DMA2SAD
case 0x40000CC: case 0x40000CD: case 0x40000CE: case 0x40000CF: // DMA2DAD
case 0x40000D0: case 0x40000D1: case 0x40000D2: case 0x40000D3: // DMA2CNT
case 0x40000D4: case 0x40000D5: case 0x40000D6: case 0x40000D7: // DMA3SAD
case 0x40000D8: case 0x40000D9: case 0x40000DA: case 0x40000DB: // DMA3DAD
case 0x40000DC: case 0x40000DD: case 0x40000DE: case 0x40000DF: // DMA3CNT
case 0x40000E0: case 0x40000E1: case 0x40000E2: case 0x40000E3: // DMA0 Fill Data
case 0x40000E4: case 0x40000E5: case 0x40000E6: case 0x40000E7: // DMA1 Fill Data
case 0x40000E8: case 0x40000E9: case 0x40000EA: case 0x40000EB: // DMA2 Fill Data
case 0x40000EC: case 0x40000ED: case 0x40000EE: case 0x40000EF: // DMA3 Fill Data
Nds.Dma9.WriteHwio8(addr, val); return;
case 0x4000100: case 0x4000101: case 0x4000102: case 0x4000103: // Timer 0
case 0x4000104: case 0x4000105: case 0x4000106: case 0x4000107: // Timer 1
case 0x4000108: case 0x4000109: case 0x400010A: case 0x400010B: // Timer 2
case 0x400010C: case 0x400010D: case 0x400010E: case 0x400010F: // Timer 3
Nds.Timers9.WriteHwio8(addr, val); return;
case 0x4000180: case 0x4000181: case 0x4000182: case 0x4000183: // IPCSYNC
case 0x4000184: case 0x4000185: case 0x4000186: case 0x4000187: // IPCFIFOCNT
case 0x4000188: case 0x4000189: case 0x400018A: case 0x400018B: // IPCFIFOSEND
Nds.Ipcs[0].WriteHwio8(addr, val); return;
case 0x40001A0: case 0x40001A1: // AUXSPICNT
case 0x40001A2: case 0x40001A3: // AUXSPIDATA
case 0x40001A4: case 0x40001A5: case 0x40001A6: case 0x40001A7: // ROMCTRL
case 0x40001A8: case 0x40001A9: case 0x40001AA: case 0x40001AB: // Slot 1 Command 0-3
case 0x40001AC: case 0x40001AD: case 0x40001AE: case 0x40001AF: // Slot 1 Command 4-7
Nds.Cartridge.WriteHwio8(false, addr, val); return;
case 0x40001B0: case 0x40001B1: case 0x40001B2: case 0x40001B3: // Slot 1 KEY2 encryption seed
case 0x40001B4: case 0x40001B5: case 0x40001B6: case 0x40001B7:
case 0x40001B8: case 0x40001B9: case 0x40001BA: case 0x40001BB:
return;
case 0x4000208: case 0x4000209: case 0x400020A: case 0x400020B: // IME
case 0x4000210: case 0x4000211: case 0x4000212: case 0x4000213: // IE
case 0x4000214: case 0x4000215: case 0x4000216: case 0x4000217: // IF
Nds.HwControl9.WriteHwio8(addr, val); return;
case 0x4000204: case 0x4000205: // EXMEMCNT
case 0x4000240: case 0x4000241: case 0x4000242: case 0x4000243: // VRAMCNT
case 0x4000244: case 0x4000245: case 0x4000246: case 0x4000247: // VRAMCNT, WRAMCNT
case 0x4000248: case 0x4000249: // VRAMCNT
Nds.MemoryControl.WriteHwio8Nds9(addr, val); return;
case 0x4000280: case 0x4000281: case 0x4000282: case 0x4000283: // DIVCNT B3
case 0x4000290: case 0x4000291: case 0x4000292: case 0x4000293: // DIV_NUMER
case 0x4000294: case 0x4000295: case 0x4000296: case 0x4000297: // DIV_NUMER
case 0x4000298: case 0x4000299: case 0x400029A: case 0x400029B: // DIV_DENOM
case 0x400029C: case 0x400029D: case 0x400029E: case 0x400029F: // DIV_DENOM
case 0x40002A0: case 0x40002A1: case 0x40002A2: case 0x40002A3: // DIV_RESULT
case 0x40002A4: case 0x40002A5: case 0x40002A6: case 0x40002A7: // DIV_RESULT
case 0x40002A8: case 0x40002A9: case 0x40002AA: case 0x40002AB: // DIVREM_RESULT
case 0x40002AC: case 0x40002AD: case 0x40002AE: case 0x40002AF: // DIVREM_RESULT
case 0x40002B0: case 0x40002B1: // SQRTCNT
case 0x40002B4: case 0x40002B5: case 0x40002B6: case 0x40002B7: // SQRT_RESULT
case 0x40002B8: case 0x40002B9: case 0x40002BA: case 0x40002BB: // SQRT_PARAM
case 0x40002BC: case 0x40002BD: case 0x40002BE: case 0x40002BF: // SQRT_PARAM
Nds.Math.WriteHwio8(addr, val); return;
case 0x4000300:
Console.WriteLine("NDS9 POSTFLG write");
Nds.HwControl9.Postflg = (byte)(val & 0b11);
return;
case 0x4000304: case 0x4000305: case 0x4000306: case 0x4000307:// POWCNT1
Nds.WriteHwio8Arm9(addr, val);
return;
}
// Console.WriteLine($"NDS9: Unmapped MMIO write addr:{Hex(addr, 8)} val:{Hex(val, 2)}");
}
}
}