178 lines
5.2 KiB
C#
178 lines
5.2 KiB
C#
using System.Runtime.CompilerServices;
|
|
|
|
namespace Iris.GBA
|
|
{
|
|
internal sealed class KeyInput
|
|
{
|
|
internal enum Register
|
|
{
|
|
KEYINPUT,
|
|
KEYCNT
|
|
}
|
|
|
|
private UInt16 _KEYINPUT;
|
|
private UInt16 _KEYCNT;
|
|
|
|
private readonly Common.Scheduler _scheduler;
|
|
private readonly Common.System.PollInput_Delegate _pollInputCallback;
|
|
|
|
private InterruptControl _interruptControl;
|
|
|
|
private const UInt64 CheckInterruptCycleCount = 280_896; // once per frame
|
|
private bool _checkingInterrupt;
|
|
|
|
internal KeyInput(Common.Scheduler scheduler, Common.System.PollInput_Delegate pollInputCallback)
|
|
{
|
|
_scheduler = scheduler;
|
|
_pollInputCallback = pollInputCallback;
|
|
|
|
_scheduler.RegisterTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterrupt);
|
|
}
|
|
|
|
internal void Initialize(InterruptControl interruptControl)
|
|
{
|
|
_interruptControl = interruptControl;
|
|
}
|
|
|
|
internal void ResetState()
|
|
{
|
|
_KEYINPUT = 0x03ff;
|
|
_KEYCNT = 0;
|
|
}
|
|
|
|
internal void LoadState(BinaryReader reader)
|
|
{
|
|
_KEYINPUT = reader.ReadUInt16();
|
|
_KEYCNT = reader.ReadUInt16();
|
|
}
|
|
|
|
internal void SaveState(BinaryWriter writer)
|
|
{
|
|
writer.Write(_KEYINPUT);
|
|
writer.Write(_KEYCNT);
|
|
}
|
|
|
|
internal UInt16 ReadRegister(Register register)
|
|
{
|
|
switch (register)
|
|
{
|
|
case Register.KEYINPUT:
|
|
_pollInputCallback();
|
|
|
|
if ((_KEYCNT & 0x4000) == 0x4000)
|
|
CheckInterrupt();
|
|
|
|
return _KEYINPUT;
|
|
|
|
case Register.KEYCNT:
|
|
return _KEYCNT;
|
|
|
|
// should never happen
|
|
default:
|
|
throw new Exception("Iris.GBA.KeyInput: Register read error");
|
|
}
|
|
}
|
|
|
|
internal void WriteRegister(Register register, UInt16 value, Memory.RegisterWriteMode mode)
|
|
{
|
|
switch (register)
|
|
{
|
|
case Register.KEYCNT:
|
|
Memory.WriteRegisterHelper(ref _KEYCNT, value, mode);
|
|
|
|
if ((_KEYCNT & 0x4000) == 0x4000)
|
|
{
|
|
_pollInputCallback();
|
|
CheckInterrupt();
|
|
|
|
if (!_checkingInterrupt)
|
|
{
|
|
_checkingInterrupt = true;
|
|
_scheduler.ScheduleTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterruptCycleCount);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// should never happen
|
|
default:
|
|
throw new Exception("Iris.GBA.KeyInput: Register write error");
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
internal void SetKeyStatus(Common.System.Key key, Common.System.KeyStatus status)
|
|
{
|
|
int pos;
|
|
|
|
switch (key)
|
|
{
|
|
case Common.System.Key.A:
|
|
pos = 0;
|
|
break;
|
|
case Common.System.Key.B:
|
|
pos = 1;
|
|
break;
|
|
case Common.System.Key.Select:
|
|
pos = 2;
|
|
break;
|
|
case Common.System.Key.Start:
|
|
pos = 3;
|
|
break;
|
|
case Common.System.Key.Right:
|
|
pos = 4;
|
|
break;
|
|
case Common.System.Key.Left:
|
|
pos = 5;
|
|
break;
|
|
case Common.System.Key.Up:
|
|
pos = 6;
|
|
break;
|
|
case Common.System.Key.Down:
|
|
pos = 7;
|
|
break;
|
|
case Common.System.Key.R:
|
|
pos = 8;
|
|
break;
|
|
case Common.System.Key.L:
|
|
pos = 9;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
_KEYINPUT = (UInt16)((_KEYINPUT & ~(1 << pos)) | ((int)status << pos));
|
|
}
|
|
|
|
private void CheckInterrupt(UInt64 cycleCountDelay)
|
|
{
|
|
if ((_KEYCNT & 0x4000) == 0)
|
|
{
|
|
_checkingInterrupt = false;
|
|
return;
|
|
}
|
|
|
|
_pollInputCallback();
|
|
CheckInterrupt();
|
|
|
|
_scheduler.ScheduleTask((int)GBA_System.TaskId.CheckKeyInterrupt, CheckInterruptCycleCount - cycleCountDelay);
|
|
}
|
|
|
|
private void CheckInterrupt()
|
|
{
|
|
UInt16 mask = (UInt16)(_KEYCNT & 0x03ff);
|
|
|
|
if ((_KEYCNT & 0x8000) == 0)
|
|
{
|
|
if ((~_KEYINPUT & mask) != 0)
|
|
_interruptControl.RequestInterrupt(InterruptControl.Interrupt.Key);
|
|
}
|
|
else
|
|
{
|
|
// TODO: figure out what happens when mask == 0
|
|
if ((~_KEYINPUT & mask) == mask)
|
|
_interruptControl.RequestInterrupt(InterruptControl.Interrupt.Key);
|
|
}
|
|
}
|
|
}
|
|
}
|