Cores/Nds/NDS.IO.cs
namespace Emulotl.Nds;

public sealed partial class NDS
{
	private static bool IsGpu2DA( uint addr ) => addr >= 0x04000000 && addr < 0x04000060;
	private static bool IsGpu2DB( uint addr ) => addr >= 0x04001000 && addr < 0x04001060;
	private static bool IsGpu3D( uint addr ) => (addr >= 0x04000060 && addr < 0x04000064) || (addr >= 0x04000320 && addr < 0x040006A4);

	public byte ARM9IORead8( uint addr )
	{
		switch ( addr )
		{
			case 0x04000130: return (byte)KeyInput;
			case 0x04000131: return (byte)(KeyInput >> 8);
			case 0x04000240: return GPU.VRAMCNT[0];
			case 0x04000241: return GPU.VRAMCNT[1];
			case 0x04000242: return GPU.VRAMCNT[2];
			case 0x04000243: return GPU.VRAMCNT[3];
			case 0x04000244: return GPU.VRAMCNT[4];
			case 0x04000245: return GPU.VRAMCNT[5];
			case 0x04000246: return GPU.VRAMCNT[6];
			case 0x04000247: return WRAMCnt;
			case 0x04000248: return GPU.VRAMCNT[7];
			case 0x04000249: return GPU.VRAMCNT[8];
			case 0x04000300: return PostFlag9;
		}
		if ( IsGpu2DA( addr ) ) return GPU.GPU2D_A.Read8( addr );
		if ( IsGpu2DB( addr ) ) return GPU.GPU2D_B.Read8( addr );
		if ( IsGpu3D( addr ) ) return GPU3D.Read8( addr );
		return (byte)(ARM9IORead32( addr & ~3u ) >> (int)((addr & 3) * 8));
	}

	public ushort ARM9IORead16( uint addr )
	{
		switch ( addr )
		{
			case 0x04000004: return GPU.DispStat[0];
			case 0x04000006: return GPU.VCount;
			case 0x040000B8: return (ushort)(DMAs[0].Cnt & 0xFFFF);
			case 0x040000BA: return (ushort)(DMAs[0].Cnt >> 16);
			case 0x040000C4: return (ushort)(DMAs[1].Cnt & 0xFFFF);
			case 0x040000C6: return (ushort)(DMAs[1].Cnt >> 16);
			case 0x040000D0: return (ushort)(DMAs[2].Cnt & 0xFFFF);
			case 0x040000D2: return (ushort)(DMAs[2].Cnt >> 16);
			case 0x040000DC: return (ushort)(DMAs[3].Cnt & 0xFFFF);
			case 0x040000DE: return (ushort)(DMAs[3].Cnt >> 16);
			case 0x04000100: return TimerGetCounter( 0 );
			case 0x04000102: return Timers[0].Cnt;
			case 0x0400006C: return GPU.MasterBrightnessA;
			case 0x0400106C: return GPU.MasterBrightnessB;
			case 0x04000064: return (ushort)GPU.CaptureCnt;
			case 0x04000066: return (ushort)(GPU.CaptureCnt >> 16);
			case 0x040001A0: return AuxSpiCnt[0];
			case 0x040001A2: return ReadAuxSpiData( 0 );
			case 0x04000280: return DivCnt;
			case 0x040002B0: return SqrtCnt;
			case 0x04000180: return IPCSync9;
			case 0x04000184:
				{
					ushort val = IPCFIFOCnt9;
					if ( IPCFIFO9.IsEmpty() ) val |= 0x0001;
					else if ( IPCFIFO9.IsFull() ) val |= 0x0002;
					if ( IPCFIFO7.IsEmpty() ) val |= 0x0100;
					else if ( IPCFIFO7.IsFull() ) val |= 0x0200;
					return val;
				}
			case 0x04000104: return TimerGetCounter( 1 );
			case 0x04000106: return Timers[1].Cnt;
			case 0x04000108: return TimerGetCounter( 2 );
			case 0x0400010A: return Timers[2].Cnt;
			case 0x0400010C: return TimerGetCounter( 3 );
			case 0x0400010E: return Timers[3].Cnt;
			case 0x04000130: return KeyInput;
			case 0x04000134: return RCnt;
			case 0x04000208: return (ushort)IME[0];
			case 0x04000300: return PostFlag9;
			case 0x04000304: return (ushort)PowerControl9;
		}
		if ( IsGpu2DA( addr ) ) return GPU.GPU2D_A.Read16( addr );
		if ( IsGpu2DB( addr ) ) return GPU.GPU2D_B.Read16( addr );
		if ( IsGpu3D( addr ) ) return GPU3D.Read16( addr );
		return (ushort)(ARM9IORead32( addr & ~3u ) >> (int)((addr & 2) * 8));
	}

	public uint ARM9IORead32( uint addr )
	{
		switch ( addr )
		{
			case 0x04000004: return (uint)(GPU.DispStat[0] | (GPU.VCount << 16));
			case 0x04000064: return GPU.CaptureCnt;
			case 0x040000B0: return DMAs[0].SrcAddr;
			case 0x040000B4: return DMAs[0].DstAddr;
			case 0x040000B8: return DMAs[0].Cnt;
			case 0x040000BC: return DMAs[1].SrcAddr;
			case 0x040000C0: return DMAs[1].DstAddr;
			case 0x040000C4: return DMAs[1].Cnt;
			case 0x040000C8: return DMAs[2].SrcAddr;
			case 0x040000CC: return DMAs[2].DstAddr;
			case 0x040000D0: return DMAs[2].Cnt;
			case 0x040000D4: return DMAs[3].SrcAddr;
			case 0x040000D8: return DMAs[3].DstAddr;
			case 0x040000DC: return DMAs[3].Cnt;
			case 0x040000E0: return DMA9Fill[0];
			case 0x040000E4: return DMA9Fill[1];
			case 0x040000E8: return DMA9Fill[2];
			case 0x040000EC: return DMA9Fill[3];
			case 0x04000100: return (uint)(TimerGetCounter( 0 ) | (Timers[0].Cnt << 16));
			case 0x04000104: return (uint)(TimerGetCounter( 1 ) | (Timers[1].Cnt << 16));
			case 0x04000108: return (uint)(TimerGetCounter( 2 ) | (Timers[2].Cnt << 16));
			case 0x0400010C: return (uint)(TimerGetCounter( 3 ) | (Timers[3].Cnt << 16));
			case 0x04000180: return IPCSync9;
			case 0x04000184: return ARM9IORead16( addr );
			case 0x04000240: return (uint)(GPU.VRAMCNT[0] | (GPU.VRAMCNT[1] << 8) | (GPU.VRAMCNT[2] << 16) | (GPU.VRAMCNT[3] << 24));
			case 0x04000244: return (uint)(GPU.VRAMCNT[4] | (GPU.VRAMCNT[5] << 8) | (GPU.VRAMCNT[6] << 16) | (WRAMCnt << 24));
			case 0x04000248: return (uint)(GPU.VRAMCNT[7] | (GPU.VRAMCNT[8] << 8));
			case 0x04000208: return IME[0];
			case 0x04000210: return IE[0];
			case 0x04000214: return IF[0];
			case 0x040001A4: return ROMCnt;
			case 0x04100010: return ReadROMData();
			case 0x04000280: return DivCnt;
			case 0x04000290: return DivNumerator[0];
			case 0x04000294: return DivNumerator[1];
			case 0x04000298: return DivDenominator[0];
			case 0x0400029C: return DivDenominator[1];
			case 0x040002A0: return DivQuotient[0];
			case 0x040002A4: return DivQuotient[1];
			case 0x040002A8: return DivRemainder[0];
			case 0x040002AC: return DivRemainder[1];
			case 0x040002B0: return SqrtCnt;
			case 0x040002B4: return SqrtRes;
			case 0x040002B8: return SqrtVal[0];
			case 0x040002BC: return SqrtVal[1];
			case 0x04000300: return PostFlag9;
			case 0x04000304: return PowerControl9;
			case 0x04100000:
				if ( (IPCFIFOCnt9 & 0x8000) != 0 )
				{
					uint ret;
					if ( IPCFIFO7.IsEmpty() )
					{
						IPCFIFOCnt9 |= 0x4000;
						ret = IPCFIFO7.Peek();
					}
					else
					{
						ret = IPCFIFO7.Read();

						if ( IPCFIFO7.IsEmpty() && (IPCFIFOCnt7 & 0x0004) != 0 )
							SetIRQ( 1, IRQ.IPCSendDone );
					}
					return ret;
				}
				return IPCFIFO7.Peek();
		}
		if ( IsGpu2DA( addr ) ) return GPU.GPU2D_A.Read32( addr );
		if ( IsGpu2DB( addr ) ) return GPU.GPU2D_B.Read32( addr );
		if ( IsGpu3D( addr ) ) return GPU3D.Read32( addr );
		return 0;
	}

	public void ARM9IOWrite8( uint addr, byte val )
	{
		switch ( addr )
		{
			case 0x04000240: GPU.MapVRAM_AB( 0, val ); return;
			case 0x04000241: GPU.MapVRAM_AB( 1, val ); return;
			case 0x04000242: GPU.MapVRAM_CD( 2, val ); return;
			case 0x04000243: GPU.MapVRAM_CD( 3, val ); return;
			case 0x04000244: GPU.MapVRAM_E( 4, val ); return;
			case 0x04000245: GPU.MapVRAM_FG( 5, val ); return;
			case 0x04000246: GPU.MapVRAM_FG( 6, val ); return;
			case 0x04000247: MapSharedWRAM( val ); return;
			case 0x04000248: GPU.MapVRAM_H( 7, val ); return;
			case 0x04000249: GPU.MapVRAM_I( 8, val ); return;
			case 0x04000181:
				IPCSync7 &= 0xFFF0;
				IPCSync7 |= (ushort)(val & 0x0F);
				IPCSync9 &= 0xB0FF;
				IPCSync9 |= (ushort)((val & 0x4F) << 8);
				if ( (val & 0x20) != 0 && (IPCSync7 & 0x4000) != 0 )
					SetIRQ( 1, IRQ.IPCSync );
				return;
			case 0x040001A0: WriteAuxSpiCnt( 0, val, 0x00FF ); return;
			case 0x040001A1: WriteAuxSpiCnt( 0, (ushort)(val << 8), 0xFF00 ); return;
			case 0x040001A2: WriteAuxSpiData( 0, val ); return;
			case 0x04000300: PostFlag9 = (byte)((PostFlag9 & 0x1) | (val & 0x3)); return;
		}
		if ( addr >= 0x040001A8 && addr <= 0x040001AF ) { WriteROMCommand( (int)(addr - 0x040001A8), val ); return; }
		if ( IsGpu2DA( addr ) ) GPU.GPU2D_A.Write8( addr, val );
		else if ( IsGpu2DB( addr ) ) GPU.GPU2D_B.Write8( addr, val );
		else if ( IsGpu3D( addr ) ) GPU3D.Write8( addr, val );
	}

	public void ARM9IOWrite16( uint addr, ushort val )
	{
		switch ( addr )
		{
			case 0x04000004: GPU.SetDispStat( 0, val, 0xFFFF ); return;
			case 0x04000006: GPU.SetVCount( val, 0xFFFF ); return;
			case 0x04000134: RCnt = val; return;
			case 0x0400006C: GPU.MasterBrightnessA = (ushort)(val & 0xC01F); return;
			case 0x0400106C: GPU.MasterBrightnessB = (ushort)(val & 0xC01F); return;
			case 0x04000064: GPU.CaptureCnt = (GPU.CaptureCnt & 0xFFFF0000) | (uint)(val & 0x1F1F); return;
			case 0x04000066: GPU.CaptureCnt = (GPU.CaptureCnt & 0xFFFF) | ((uint)(val & 0xEF3F) << 16); return;
			case 0x040001A0: WriteAuxSpiCnt( 0, val, 0xFFFF ); return;
			case 0x040001A2: WriteAuxSpiData( 0, (byte)val ); return;
			case 0x040001A4: WriteROMCnt( 0, val, 0x0000FFFF ); return;
			case 0x040001A6: WriteROMCnt( 0, (uint)val << 16, 0xFFFF0000 ); return;
			case 0x040001A8: WriteROMCommand( 0, (byte)val ); WriteROMCommand( 1, (byte)(val >> 8) ); return;
			case 0x040001AA: WriteROMCommand( 2, (byte)val ); WriteROMCommand( 3, (byte)(val >> 8) ); return;
			case 0x040001AC: WriteROMCommand( 4, (byte)val ); WriteROMCommand( 5, (byte)(val >> 8) ); return;
			case 0x040001AE: WriteROMCommand( 6, (byte)val ); WriteROMCommand( 7, (byte)(val >> 8) ); return;
			case 0x040000B8: DMAs[0].WriteCnt( (DMAs[0].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000BA: DMAs[0].WriteCnt( (DMAs[0].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000C4: DMAs[1].WriteCnt( (DMAs[1].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000C6: DMAs[1].WriteCnt( (DMAs[1].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000D0: DMAs[2].WriteCnt( (DMAs[2].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000D2: DMAs[2].WriteCnt( (DMAs[2].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000DC: DMAs[3].WriteCnt( (DMAs[3].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000DE: DMAs[3].WriteCnt( (DMAs[3].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x04000180:
				IPCSync7 &= 0xFFF0;
				IPCSync7 |= (ushort)((val & 0x0F00) >> 8);
				IPCSync9 &= 0xB0FF;
				IPCSync9 |= (ushort)(val & 0x4F00);
				if ( (val & 0x2000) != 0 && (IPCSync7 & 0x4000) != 0 )
					SetIRQ( 1, IRQ.IPCSync );
				return;
			case 0x04000184:
				if ( (val & 0x0008) != 0 )
					IPCFIFO9.Clear();
				if ( (val & 0x0004) != 0 && (IPCFIFOCnt9 & 0x0004) == 0 && IPCFIFO9.IsEmpty() )
					SetIRQ( 0, IRQ.IPCSendDone );
				if ( (val & 0x0400) != 0 && (IPCFIFOCnt9 & 0x0400) == 0 && !IPCFIFO7.IsEmpty() )
					SetIRQ( 0, IRQ.IPCRecv );
				if ( (val & 0x4000) != 0 )
					IPCFIFOCnt9 &= 0xBFFF;
				IPCFIFOCnt9 = (ushort)((val & 0x8404) | (IPCFIFOCnt9 & 0x4000));
				return;
			case 0x04000188:
				ARM9IOWrite32( addr, (uint)(val | (val << 16)) );
				return;
			case 0x04000240:
				GPU.MapVRAM_AB( 0, (byte)val );
				GPU.MapVRAM_AB( 1, (byte)(val >> 8) );
				return;
			case 0x04000242:
				GPU.MapVRAM_CD( 2, (byte)val );
				GPU.MapVRAM_CD( 3, (byte)(val >> 8) );
				return;
			case 0x04000244:
				GPU.MapVRAM_E( 4, (byte)val );
				GPU.MapVRAM_FG( 5, (byte)(val >> 8) );
				return;
			case 0x04000246:
				GPU.MapVRAM_FG( 6, (byte)val );
				MapSharedWRAM( (byte)(val >> 8) );
				return;
			case 0x04000248:
				GPU.MapVRAM_H( 7, (byte)val );
				GPU.MapVRAM_I( 8, (byte)(val >> 8) );
				return;
			case 0x04000100: Timers[0].Reload = val; return;
			case 0x04000102: TimerStart( 0, val ); return;
			case 0x04000104: Timers[1].Reload = val; return;
			case 0x04000106: TimerStart( 1, val ); return;
			case 0x04000108: Timers[2].Reload = val; return;
			case 0x0400010A: TimerStart( 2, val ); return;
			case 0x0400010C: Timers[3].Reload = val; return;
			case 0x0400010E: TimerStart( 3, val ); return;
			case 0x04000208: IME[0] = (uint)(val & 0x1); UpdateIRQ( 0 ); return;
			case 0x04000280: DivCnt = val; StartDiv(); return;
			case 0x040002B0: SqrtCnt = val; StartSqrt(); return;
			case 0x04000304: PowerControl9 = (uint)(val & 0x820F); GPU.SetPowerCnt( PowerControl9 ); return;
		}
		if ( IsGpu2DA( addr ) ) GPU.GPU2D_A.Write16( addr, val );
		else if ( IsGpu2DB( addr ) ) GPU.GPU2D_B.Write16( addr, val );
		else if ( IsGpu3D( addr ) ) GPU3D.Write16( addr, val );
	}

	public void ARM9IOWrite32( uint addr, uint val )
	{
		switch ( addr )
		{
			case 0x04000004:
				GPU.SetDispStat( 0, (ushort)(val & 0xFFFF), 0xFFFF );
				GPU.SetVCount( (ushort)(val >> 16), 0xFFFF );
				return;
			case 0x040000B0: DMAs[0].SrcAddr = val; return;
			case 0x040000B4: DMAs[0].DstAddr = val; return;
			case 0x040000B8: DMAs[0].WriteCnt( val ); return;
			case 0x040000BC: DMAs[1].SrcAddr = val; return;
			case 0x040000C0: DMAs[1].DstAddr = val; return;
			case 0x040000C4: DMAs[1].WriteCnt( val ); return;
			case 0x040000C8: DMAs[2].SrcAddr = val; return;
			case 0x040000CC: DMAs[2].DstAddr = val; return;
			case 0x040000D0: DMAs[2].WriteCnt( val ); return;
			case 0x040000D4: DMAs[3].SrcAddr = val; return;
			case 0x040000D8: DMAs[3].DstAddr = val; return;
			case 0x040000DC: DMAs[3].WriteCnt( val ); return;
			case 0x040000E0: DMA9Fill[0] = val; return;
			case 0x040000E4: DMA9Fill[1] = val; return;
			case 0x040000E8: DMA9Fill[2] = val; return;
			case 0x040000EC: DMA9Fill[3] = val; return;
			case 0x0400006C: GPU.MasterBrightnessA = (ushort)(val & 0xC01F); return;
			case 0x0400106C: GPU.MasterBrightnessB = (ushort)(val & 0xC01F); return;
			case 0x04000240:
				GPU.MapVRAM_AB( 0, (byte)val );
				GPU.MapVRAM_AB( 1, (byte)(val >> 8) );
				GPU.MapVRAM_CD( 2, (byte)(val >> 16) );
				GPU.MapVRAM_CD( 3, (byte)(val >> 24) );
				return;
			case 0x04000244:
				GPU.MapVRAM_E( 4, (byte)val );
				GPU.MapVRAM_FG( 5, (byte)(val >> 8) );
				GPU.MapVRAM_FG( 6, (byte)(val >> 16) );
				MapSharedWRAM( (byte)(val >> 24) );
				return;
			case 0x04000248:
				GPU.MapVRAM_H( 7, (byte)val );
				GPU.MapVRAM_I( 8, (byte)(val >> 8) );
				return;
			case 0x04000180:
			case 0x04000184:
				ARM9IOWrite16( addr, (ushort)val );
				return;
			case 0x04000188:
				if ( (IPCFIFOCnt9 & 0x8000) != 0 )
				{
					if ( IPCFIFO9.IsFull() )
						IPCFIFOCnt9 |= 0x4000;
					else
					{
						bool wasempty = IPCFIFO9.IsEmpty();
						IPCFIFO9.Write( val );
						if ( (IPCFIFOCnt7 & 0x0400) != 0 && wasempty )
							SetIRQ( 1, IRQ.IPCRecv );
					}
				}
				return;
			case 0x04000100:
				Timers[0].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 0, (ushort)(val >> 16) );
				return;
			case 0x04000104:
				Timers[1].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 1, (ushort)(val >> 16) );
				return;
			case 0x04000108:
				Timers[2].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 2, (ushort)(val >> 16) );
				return;
			case 0x0400010C:
				Timers[3].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 3, (ushort)(val >> 16) );
				return;
			case 0x04000064: GPU.CaptureCnt = val & 0xEF3F1F1F; return;
			case 0x04000208: IME[0] = val & 0x1; UpdateIRQ( 0 ); return;
			case 0x04000210: IE[0] = val; UpdateIRQ( 0 ); return;
			case 0x04000214: IF[0] &= ~val; GPU3D.CheckFIFOIRQ(); UpdateIRQ( 0 ); return;
			case 0x040001A0:
				WriteAuxSpiCnt( 0, (ushort)val, 0xFFFF );
				return;
			case 0x040001A4: WriteROMCnt( 0, val, 0xFFFFFFFF ); return;
			case 0x040001A8:
				WriteROMCommand( 0, (byte)val );
				WriteROMCommand( 1, (byte)(val >> 8) );
				WriteROMCommand( 2, (byte)(val >> 16) );
				WriteROMCommand( 3, (byte)(val >> 24) );
				return;
			case 0x040001AC:
				WriteROMCommand( 4, (byte)val );
				WriteROMCommand( 5, (byte)(val >> 8) );
				WriteROMCommand( 6, (byte)(val >> 16) );
				WriteROMCommand( 7, (byte)(val >> 24) );
				return;
			case 0x04100010: WriteROMData( val, 0xFFFFFFFF ); return;
			case 0x04000280: DivCnt = (ushort)val; StartDiv(); return;
			case 0x04000290: DivNumerator[0] = val; StartDiv(); return;
			case 0x04000294: DivNumerator[1] = val; StartDiv(); return;
			case 0x04000298: DivDenominator[0] = val; StartDiv(); return;
			case 0x0400029C: DivDenominator[1] = val; StartDiv(); return;
			case 0x040002B0: SqrtCnt = (ushort)val; StartSqrt(); return;
			case 0x040002B8: SqrtVal[0] = val; StartSqrt(); return;
			case 0x040002BC: SqrtVal[1] = val; StartSqrt(); return;
			case 0x04000304: PowerControl9 = val & 0x820F; GPU.SetPowerCnt( PowerControl9 ); return;
		}
		if ( IsGpu2DA( addr ) ) GPU.GPU2D_A.Write32( addr, val );
		else if ( IsGpu2DB( addr ) ) GPU.GPU2D_B.Write32( addr, val );
		else if ( IsGpu3D( addr ) ) GPU3D.Write32( addr, val );
	}

	public byte ARM7IORead8( uint addr )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
			return SPU.Read8( addr );
		switch ( addr )
		{
			case 0x04000130: return (byte)KeyInput;
			case 0x04000131: return (byte)(KeyInput >> 8);
			case 0x04000138: return (byte)RtcRead();
			case 0x040001C2: return ReadSpiData();
			case 0x04000240: return GPU.VRAMSTAT;
			case 0x04000241: return WRAMCnt;
			case 0x04000300: return PostFlag7;
		}
		return (byte)(ARM7IORead32( addr & ~3u ) >> (int)((addr & 3) * 8));
	}

	public ushort ARM7IORead16( uint addr )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
			return SPU.Read16( addr );
		switch ( addr )
		{
			case 0x04000004: return GPU.DispStat[1];
			case 0x04000006: return GPU.VCount;
			case 0x040000B8: return (ushort)(DMAs[4].Cnt & 0xFFFF);
			case 0x040000BA: return (ushort)(DMAs[4].Cnt >> 16);
			case 0x040000C4: return (ushort)(DMAs[5].Cnt & 0xFFFF);
			case 0x040000C6: return (ushort)(DMAs[5].Cnt >> 16);
			case 0x040000D0: return (ushort)(DMAs[6].Cnt & 0xFFFF);
			case 0x040000D2: return (ushort)(DMAs[6].Cnt >> 16);
			case 0x040000DC: return (ushort)(DMAs[7].Cnt & 0xFFFF);
			case 0x040000DE: return (ushort)(DMAs[7].Cnt >> 16);
			case 0x04000180: return IPCSync7;
			case 0x04000184:
				{
					ushort val = IPCFIFOCnt7;
					if ( IPCFIFO7.IsEmpty() ) val |= 0x0001;
					else if ( IPCFIFO7.IsFull() ) val |= 0x0002;
					if ( IPCFIFO9.IsEmpty() ) val |= 0x0100;
					else if ( IPCFIFO9.IsFull() ) val |= 0x0200;
					return val;
				}
			case 0x04000100: return TimerGetCounter( 4 );
			case 0x04000102: return Timers[4].Cnt;
			case 0x04000104: return TimerGetCounter( 5 );
			case 0x04000106: return Timers[5].Cnt;
			case 0x04000108: return TimerGetCounter( 6 );
			case 0x0400010A: return Timers[6].Cnt;
			case 0x0400010C: return TimerGetCounter( 7 );
			case 0x0400010E: return Timers[7].Cnt;
			case 0x04000130: return KeyInput;
			case 0x04000134: return RCnt;
			case 0x04000136: return ExtKeyIn;
			case 0x04000138: return RtcRead();
			case 0x040001A0: return AuxSpiCnt[1];
			case 0x040001A2: return ReadAuxSpiData( 1 );
			case 0x040001C0: return SpiCnt;
			case 0x040001C2: return ReadSpiData();
			case 0x04000208: return (ushort)IME[1];
			case 0x04000300: return PostFlag7;
			case 0x04000304: return (ushort)PowerControl7;
		}
		return (ushort)(ARM7IORead32( addr & ~3u ) >> (int)((addr & 2) * 8));
	}

	public uint ARM7IORead32( uint addr )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
			return SPU.Read32( addr );
		switch ( addr )
		{
			case 0x04000004: return (uint)(GPU.DispStat[1] | (GPU.VCount << 16));
			case 0x040000B0: return DMAs[4].SrcAddr;
			case 0x040000B4: return DMAs[4].DstAddr;
			case 0x040000B8: return DMAs[4].Cnt;
			case 0x040000BC: return DMAs[5].SrcAddr;
			case 0x040000C0: return DMAs[5].DstAddr;
			case 0x040000C4: return DMAs[5].Cnt;
			case 0x040000C8: return DMAs[6].SrcAddr;
			case 0x040000CC: return DMAs[6].DstAddr;
			case 0x040000D0: return DMAs[6].Cnt;
			case 0x040000D4: return DMAs[7].SrcAddr;
			case 0x040000D8: return DMAs[7].DstAddr;
			case 0x040000DC: return DMAs[7].Cnt;
			case 0x04000100: return (uint)(TimerGetCounter( 4 ) | (Timers[4].Cnt << 16));
			case 0x04000104: return (uint)(TimerGetCounter( 5 ) | (Timers[5].Cnt << 16));
			case 0x04000108: return (uint)(TimerGetCounter( 6 ) | (Timers[6].Cnt << 16));
			case 0x0400010C: return (uint)(TimerGetCounter( 7 ) | (Timers[7].Cnt << 16));
			case 0x040001A4: return ROMCnt;
			case 0x04100010: return ReadROMData();
			case 0x04000180: return IPCSync7;
			case 0x04000184: return ARM7IORead16( addr );
			case 0x04000208: return IME[1];
			case 0x04000210: return IE[1];
			case 0x04000214: return IF[1];
			case 0x04100000:
				if ( (IPCFIFOCnt7 & 0x8000) != 0 )
				{
					uint ret;
					if ( IPCFIFO9.IsEmpty() )
					{
						IPCFIFOCnt7 |= 0x4000;
						ret = IPCFIFO9.Peek();
					}
					else
					{
						ret = IPCFIFO9.Read();

						if ( IPCFIFO9.IsEmpty() && (IPCFIFOCnt9 & 0x0004) != 0 )
							SetIRQ( 0, IRQ.IPCSendDone );
					}
					return ret;
				}
				return IPCFIFO9.Peek();
		}
		return 0;
	}

	public void ARM7IOWrite8( uint addr, byte val )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
		{
			SPU.Write8( addr, val );
			return;
		}
		switch ( addr )
		{
			case 0x04000181:
				IPCSync9 &= 0xFFF0;
				IPCSync9 |= (ushort)(val & 0x0F);
				IPCSync7 &= 0xB0FF;
				IPCSync7 |= (ushort)((val & 0x4F) << 8);
				if ( (val & 0x20) != 0 && (IPCSync9 & 0x4000) != 0 )
					SetIRQ( 0, IRQ.IPCSync );
				return;
			case 0x040001C2: WriteSpiData( val ); return;
			case 0x040001A0: WriteAuxSpiCnt( 1, val, 0x00FF ); return;
			case 0x040001A1: WriteAuxSpiCnt( 1, (ushort)(val << 8), 0xFF00 ); return;
			case 0x040001A2: WriteAuxSpiData( 1, val ); return;
			case 0x04000138: RtcWrite( val, true ); return;
			default:
				if ( addr >= 0x040001A8 && addr <= 0x040001AF ) WriteROMCommand( (int)(addr - 0x040001A8), val );
				return;
			case 0x04000301:
				if ( (val & 0xC0) == 0x80 ) ARM7.Halt( 1 );
				return;
			case 0x04000300:
				if ( ARM7.R[15] >= 0x4000 ) return;
				if ( (PostFlag7 & 0x01) == 0 ) PostFlag7 = (byte)(val & 0x01);
				return;
		}
	}

	public void ARM7IOWrite16( uint addr, ushort val )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
		{
			SPU.Write16( addr, val );
			return;
		}
		switch ( addr )
		{
			case 0x04000004: GPU.SetDispStat( 1, val, 0xFFFF ); return;
			case 0x04000006: GPU.SetVCount( val, 0xFFFF ); return;
			case 0x04000134: RCnt = val; return;
			case 0x04000138: RtcWrite( val, false ); return;
			case 0x040001A0: WriteAuxSpiCnt( 1, val, 0xFFFF ); return;
			case 0x040001A2: WriteAuxSpiData( 1, (byte)val ); return;
			case 0x040001A4: WriteROMCnt( 1, val, 0x0000FFFF ); return;
			case 0x040001A6: WriteROMCnt( 1, (uint)val << 16, 0xFFFF0000 ); return;
			case 0x040001A8: WriteROMCommand( 0, (byte)val ); WriteROMCommand( 1, (byte)(val >> 8) ); return;
			case 0x040001AA: WriteROMCommand( 2, (byte)val ); WriteROMCommand( 3, (byte)(val >> 8) ); return;
			case 0x040001AC: WriteROMCommand( 4, (byte)val ); WriteROMCommand( 5, (byte)(val >> 8) ); return;
			case 0x040001AE: WriteROMCommand( 6, (byte)val ); WriteROMCommand( 7, (byte)(val >> 8) ); return;
			case 0x040001C0: WriteSpiCnt( val ); return;
			case 0x040001C2: WriteSpiData( (byte)val ); return;
			case 0x040000B8: DMAs[4].WriteCnt( (DMAs[4].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000BA: DMAs[4].WriteCnt( (DMAs[4].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000C4: DMAs[5].WriteCnt( (DMAs[5].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000C6: DMAs[5].WriteCnt( (DMAs[5].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000D0: DMAs[6].WriteCnt( (DMAs[6].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000D2: DMAs[6].WriteCnt( (DMAs[6].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x040000DC: DMAs[7].WriteCnt( (DMAs[7].Cnt & 0xFFFF0000) | val ); return;
			case 0x040000DE: DMAs[7].WriteCnt( (DMAs[7].Cnt & 0x0000FFFF) | ((uint)val << 16) ); return;
			case 0x04000180:
				IPCSync9 &= 0xFFF0;
				IPCSync9 |= (ushort)((val & 0x0F00) >> 8);
				IPCSync7 &= 0xB0FF;
				IPCSync7 |= (ushort)(val & 0x4F00);
				if ( (val & 0x2000) != 0 && (IPCSync9 & 0x4000) != 0 )
					SetIRQ( 0, IRQ.IPCSync );
				return;
			case 0x04000184:
				if ( (val & 0x0008) != 0 )
					IPCFIFO7.Clear();
				if ( (val & 0x0004) != 0 && (IPCFIFOCnt7 & 0x0004) == 0 && IPCFIFO7.IsEmpty() )
					SetIRQ( 1, IRQ.IPCSendDone );
				if ( (val & 0x0400) != 0 && (IPCFIFOCnt7 & 0x0400) == 0 && !IPCFIFO9.IsEmpty() )
					SetIRQ( 1, IRQ.IPCRecv );
				if ( (val & 0x4000) != 0 )
					IPCFIFOCnt7 &= 0xBFFF;
				IPCFIFOCnt7 = (ushort)((val & 0x8404) | (IPCFIFOCnt7 & 0x4000));
				return;
			case 0x04000188:
				ARM7IOWrite32( addr, (uint)(val | (val << 16)) );
				return;
			case 0x04000100: Timers[4].Reload = val; return;
			case 0x04000102: TimerStart( 4, val ); return;
			case 0x04000104: Timers[5].Reload = val; return;
			case 0x04000106: TimerStart( 5, val ); return;
			case 0x04000108: Timers[6].Reload = val; return;
			case 0x0400010A: TimerStart( 6, val ); return;
			case 0x0400010C: Timers[7].Reload = val; return;
			case 0x0400010E: TimerStart( 7, val ); return;
			case 0x04000208: IME[1] = (uint)(val & 0x1); UpdateIRQ( 1 ); return;
			case 0x04000304: PowerControl7 = (uint)(val & 0x0003); SPU.SetPowerCnt( PowerControl7 & 0x0001 ); Wifi.SetPowerCnt( PowerControl7 & 0x0002 ); return;
			case 0x04000308: if ( ARM7BIOSProt == 0 ) ARM7BIOSProt = (uint)(val & 0xFFFE); return;
		}
	}

	public void ARM7IOWrite32( uint addr, uint val )
	{
		if ( addr >= 0x04000400 && addr < 0x04000520 )
		{
			SPU.Write32( addr, val );
			return;
		}
		switch ( addr )
		{
			case 0x040001A0: WriteAuxSpiCnt( 1, (ushort)val, 0xFFFF ); return;
			case 0x040001A4: WriteROMCnt( 1, val, 0xFFFFFFFF ); return;
			case 0x040001A8:
				WriteROMCommand( 0, (byte)val );
				WriteROMCommand( 1, (byte)(val >> 8) );
				WriteROMCommand( 2, (byte)(val >> 16) );
				WriteROMCommand( 3, (byte)(val >> 24) );
				return;
			case 0x040001AC:
				WriteROMCommand( 4, (byte)val );
				WriteROMCommand( 5, (byte)(val >> 8) );
				WriteROMCommand( 6, (byte)(val >> 16) );
				WriteROMCommand( 7, (byte)(val >> 24) );
				return;
			case 0x04100010: WriteROMData( val, 0xFFFFFFFF ); return;
			case 0x04000004:
				GPU.SetDispStat( 1, (ushort)(val & 0xFFFF), 0xFFFF );
				GPU.SetVCount( (ushort)(val >> 16), 0xFFFF );
				return;
			case 0x04000100:
				Timers[4].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 4, (ushort)(val >> 16) );
				return;
			case 0x04000104:
				Timers[5].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 5, (ushort)(val >> 16) );
				return;
			case 0x04000108:
				Timers[6].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 6, (ushort)(val >> 16) );
				return;
			case 0x0400010C:
				Timers[7].Reload = (ushort)(val & 0xFFFF);
				TimerStart( 7, (ushort)(val >> 16) );
				return;
			case 0x04000180:
			case 0x04000184:
				ARM7IOWrite16( addr, (ushort)val );
				return;
			case 0x04000188:
				if ( (IPCFIFOCnt7 & 0x8000) != 0 )
				{
					if ( IPCFIFO7.IsFull() )
						IPCFIFOCnt7 |= 0x4000;
					else
					{
						bool wasempty = IPCFIFO7.IsEmpty();
						IPCFIFO7.Write( val );
						if ( (IPCFIFOCnt9 & 0x0400) != 0 && wasempty )
							SetIRQ( 0, IRQ.IPCRecv );
					}
				}
				return;
			case 0x04000208: IME[1] = val & 0x1; UpdateIRQ( 1 ); return;
			case 0x04000210: IE[1] = val; UpdateIRQ( 1 ); return;
			case 0x04000214: IF[1] &= ~val; UpdateIRQ( 1 ); return;
			case 0x04000304: PowerControl7 = val & 0x0003; SPU.SetPowerCnt( PowerControl7 & 0x0001 ); Wifi.SetPowerCnt( PowerControl7 & 0x0002 ); return;
		}
	}
}