GBA.Unity/Assets/Iris/Iris.GBA/InterruptControl.cs
2024-08-15 13:19:43 +08:00

128 lines
3.4 KiB
C#

using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace Iris.GBA
{
internal sealed class InterruptControl
{
internal enum Register
{
IE,
IF,
IME
}
internal enum Interrupt
{
VBlank = 1 << 0,
HBlank = 1 << 1,
VCountMatch = 1 << 2,
Timer0 = 1 << 3,
Timer1 = 1 << 4,
Timer2 = 1 << 5,
Timer3 = 1 << 6,
SIO = 1 << 7,
DMA0 = 1 << 8,
DMA1 = 1 << 9,
DMA2 = 1 << 10,
DMA3 = 1 << 11,
Key = 1 << 12,
//GamePak = 1 << 13
}
private UInt16 _IE;
private UInt16 _IF;
private UInt16 _IME;
private CPU.CPU_Core _cpu;
internal void Initialize(CPU.CPU_Core cpu)
{
_cpu = cpu;
}
internal void ResetState()
{
_IE = 0;
_IF = 0;
_IME = 0;
}
internal void LoadState(BinaryReader reader)
{
_IE = reader.ReadUInt16();
_IF = reader.ReadUInt16();
_IME = reader.ReadUInt16();
}
internal void SaveState(BinaryWriter writer)
{
writer.Write(_IE);
writer.Write(_IF);
writer.Write(_IME);
}
internal UInt16 ReadRegister(Register register)
{
return register switch
{
Register.IE => _IE,
Register.IF => _IF,
Register.IME => _IME,
// should never happen
_ => throw new Exception("Iris.GBA.InterruptControl: Register read error"),
};
}
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
{
switch (register)
{
case Register.IE:
Memory.WriteRegisterHelper(ref _IE, value, mode);
break;
case Register.IF:
switch (mode)
{
case Memory.RegisterWriteMode.LowByte:
_IF &= (UInt16)~value;
break;
case Memory.RegisterWriteMode.HighByte:
_IF &= (UInt16)~(value << 8);
break;
case Memory.RegisterWriteMode.HalfWord:
_IF &= (UInt16)~value;
break;
}
break;
case Register.IME:
Memory.WriteRegisterHelper(ref _IME, value, mode);
break;
// should never happen
default:
throw new Exception("Iris.GBA.InterruptControl: Register write error");
}
CheckInterrupts();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RequestInterrupt(Interrupt interrupt)
{
_IF |= (UInt16)interrupt;
CheckInterrupts();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckInterrupts()
{
_cpu.NIRQ = ((_IME == 0) || ((_IE & _IF) == 0)) ? CPU.CPU_Core.Signal.High : CPU.CPU_Core.Signal.Low;
}
}
}