feat(timing): add DIV/TIMA timer with overflow reload delay
This commit is contained in:
1 parent
35f236ee90
commit
485e61c8ef
1 file changed
+126
@@ -0,0 +1,126 @@
|
|||||||
|
using System.IO;
|
||||||
|
using GameBoy.Cpu;
|
||||||
|
|
||||||
|
namespace GameBoy.Timing
|
||||||
|
{
|
||||||
|
public sealed class Timer
|
||||||
|
{
|
||||||
|
private readonly InterruptController interrupts;
|
||||||
|
|
||||||
|
private ushort counter;
|
||||||
|
private byte tima;
|
||||||
|
private byte tma;
|
||||||
|
private byte tac;
|
||||||
|
private bool previousEdge;
|
||||||
|
private int reloadCountdown;
|
||||||
|
|
||||||
|
public Timer(InterruptController interrupts)
|
||||||
|
{
|
||||||
|
this.interrupts = interrupts;
|
||||||
|
counter = 0xABCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Step()
|
||||||
|
{
|
||||||
|
if (reloadCountdown > 0)
|
||||||
|
{
|
||||||
|
reloadCountdown--;
|
||||||
|
if (reloadCountdown == 0)
|
||||||
|
{
|
||||||
|
tima = tma;
|
||||||
|
interrupts.Request(Interrupt.Timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counter = (ushort)(counter + 1);
|
||||||
|
UpdateEdge();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEdge()
|
||||||
|
{
|
||||||
|
bool edge = ((counter >> SelectedBit()) & 1) != 0 && (tac & 0x04) != 0;
|
||||||
|
if (previousEdge && !edge)
|
||||||
|
IncrementTima();
|
||||||
|
previousEdge = edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IncrementTima()
|
||||||
|
{
|
||||||
|
if (tima == 0xFF)
|
||||||
|
{
|
||||||
|
tima = 0;
|
||||||
|
reloadCountdown = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tima++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int SelectedBit()
|
||||||
|
{
|
||||||
|
return (tac & 0x03) switch
|
||||||
|
{
|
||||||
|
0 => 9,
|
||||||
|
1 => 3,
|
||||||
|
2 => 5,
|
||||||
|
_ => 7
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte Read(ushort address)
|
||||||
|
{
|
||||||
|
return (address & 0xFF) switch
|
||||||
|
{
|
||||||
|
0x04 => (byte)(counter >> 8),
|
||||||
|
0x05 => tima,
|
||||||
|
0x06 => tma,
|
||||||
|
0x07 => (byte)(tac | 0xF8),
|
||||||
|
_ => 0xFF
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(ushort address, byte value)
|
||||||
|
{
|
||||||
|
switch (address & 0xFF)
|
||||||
|
{
|
||||||
|
case 0x04:
|
||||||
|
counter = 0;
|
||||||
|
UpdateEdge();
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
if (reloadCountdown > 0)
|
||||||
|
reloadCountdown = 0;
|
||||||
|
tima = value;
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
tma = value;
|
||||||
|
break;
|
||||||
|
case 0x07:
|
||||||
|
tac = (byte)(value & 0x07);
|
||||||
|
UpdateEdge();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(BinaryWriter writer)
|
||||||
|
{
|
||||||
|
writer.Write(counter);
|
||||||
|
writer.Write(tima);
|
||||||
|
writer.Write(tma);
|
||||||
|
writer.Write(tac);
|
||||||
|
writer.Write(previousEdge);
|
||||||
|
writer.Write(reloadCountdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(BinaryReader reader)
|
||||||
|
{
|
||||||
|
counter = reader.ReadUInt16();
|
||||||
|
tima = reader.ReadByte();
|
||||||
|
tma = reader.ReadByte();
|
||||||
|
tac = reader.ReadByte();
|
||||||
|
previousEdge = reader.ReadBoolean();
|
||||||
|
reloadCountdown = reader.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user