Emux/Emux.GameBoy/Timer/GameBoyTimer.cs

100 lines
2.7 KiB
C#

using System;
using Emux.GameBoy.Cpu;
namespace Emux.GameBoy.Timer
{
public class GameBoyTimer
{
private readonly GameBoy _device;
public const int DivFrequency = 16384;
private int _divClock;
private int _timerClock;
public byte Div;
public byte Tima;
public byte Tma;
public TimerControlFlags Tac;
public GameBoyTimer(GameBoy device)
{
if (device == null)
throw new ArgumentNullException(nameof(device));
_device = device;
}
public int GetTimaFrequency()
{
switch (Tac & TimerControlFlags.ClockMask)
{
case TimerControlFlags.Clock4096Hz:
return 4096;
case TimerControlFlags.Clock16384Hz:
return 16384;
case TimerControlFlags.Clock65536Hz:
return 65536;
case TimerControlFlags.Clock262144Hz:
return 262144;
}
return 0;
}
public void TimerStep(int cycles)
{
_divClock += cycles;
if (_divClock >= DivFrequency)
{
_divClock -= DivFrequency;
Div = (byte) ((Div + 1) % 0xFF);
}
if ((Tac & TimerControlFlags.EnableTimer) == TimerControlFlags.EnableTimer)
{
_timerClock += cycles;
int timaSpeed = GetTimaFrequency();
if (_timerClock > timaSpeed)
{
_timerClock -= timaSpeed;
Tima = Tma;
_device.Cpu.Registers.IF |= InterruptFlags.Timer;
}
}
}
public byte ReadRegister(ushort address)
{
switch (address)
{
case 0x04:
return Div;
case 0x05:
return Tima;
case 0x06:
return Tma;
case 0x07:
return (byte) Tac;
}
throw new ArgumentOutOfRangeException(nameof(address));
}
public void WriteRegister(ushort address, byte value)
{
switch (address)
{
case 0x04:
Div = 0;
break;
case 0x05:
Tima = value;
break;
case 0x06:
Tma = value;
break;
case 0x07:
Tac = (TimerControlFlags) (value & 0b111);
break;
}
}
}
}