feat(cartridge): add MBC3 real-time clock support
This commit is contained in:
1 parent
ab8f98cc3d
commit
3c947b637e
1 file changed
+140
@@ -0,0 +1,140 @@
|
||||
using System.IO;
|
||||
|
||||
namespace GameBoy.Cartridge
|
||||
{
|
||||
public sealed class Mbc3Rtc
|
||||
{
|
||||
private const long CyclesPerSecond = 4194304;
|
||||
|
||||
private int seconds;
|
||||
private int minutes;
|
||||
private int hours;
|
||||
private int days;
|
||||
private bool halt;
|
||||
private bool carry;
|
||||
private long cycleAccumulator;
|
||||
|
||||
private int latchedSeconds;
|
||||
private int latchedMinutes;
|
||||
private int latchedHours;
|
||||
private int latchedDays;
|
||||
private bool latchedHalt;
|
||||
private bool latchedCarry;
|
||||
|
||||
private byte previousLatch = 0xFF;
|
||||
|
||||
public void Step(int cycles)
|
||||
{
|
||||
if (halt)
|
||||
return;
|
||||
cycleAccumulator += cycles;
|
||||
while (cycleAccumulator >= CyclesPerSecond)
|
||||
{
|
||||
cycleAccumulator -= CyclesPerSecond;
|
||||
AddSecond();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSeconds(long amount)
|
||||
{
|
||||
if (halt)
|
||||
return;
|
||||
for (long i = 0; i < amount; i++)
|
||||
AddSecond();
|
||||
}
|
||||
|
||||
private void AddSecond()
|
||||
{
|
||||
seconds++;
|
||||
if (seconds < 60)
|
||||
return;
|
||||
seconds = 0;
|
||||
minutes++;
|
||||
if (minutes < 60)
|
||||
return;
|
||||
minutes = 0;
|
||||
hours++;
|
||||
if (hours < 24)
|
||||
return;
|
||||
hours = 0;
|
||||
days++;
|
||||
if (days < 512)
|
||||
return;
|
||||
days = 0;
|
||||
carry = true;
|
||||
}
|
||||
|
||||
public void WriteLatch(byte value)
|
||||
{
|
||||
if (previousLatch == 0x00 && value == 0x01)
|
||||
Latch();
|
||||
previousLatch = value;
|
||||
}
|
||||
|
||||
private void Latch()
|
||||
{
|
||||
latchedSeconds = seconds;
|
||||
latchedMinutes = minutes;
|
||||
latchedHours = hours;
|
||||
latchedDays = days;
|
||||
latchedHalt = halt;
|
||||
latchedCarry = carry;
|
||||
}
|
||||
|
||||
public byte ReadRegister(int register)
|
||||
{
|
||||
return register switch
|
||||
{
|
||||
0x08 => (byte)latchedSeconds,
|
||||
0x09 => (byte)latchedMinutes,
|
||||
0x0A => (byte)latchedHours,
|
||||
0x0B => (byte)(latchedDays & 0xFF),
|
||||
0x0C => (byte)(((latchedDays >> 8) & 0x01) | (latchedHalt ? 0x40 : 0x00) | (latchedCarry ? 0x80 : 0x00)),
|
||||
_ => 0xFF
|
||||
};
|
||||
}
|
||||
|
||||
public void WriteRegister(int register, byte value)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case 0x08:
|
||||
seconds = value;
|
||||
cycleAccumulator = 0;
|
||||
break;
|
||||
case 0x09:
|
||||
minutes = value;
|
||||
break;
|
||||
case 0x0A:
|
||||
hours = value;
|
||||
break;
|
||||
case 0x0B:
|
||||
days = (days & 0x100) | value;
|
||||
break;
|
||||
case 0x0C:
|
||||
days = (days & 0xFF) | ((value & 0x01) << 8);
|
||||
halt = (value & 0x40) != 0;
|
||||
carry = (value & 0x80) != 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter w)
|
||||
{
|
||||
w.Write(seconds); w.Write(minutes); w.Write(hours); w.Write(days);
|
||||
w.Write(halt); w.Write(carry); w.Write(cycleAccumulator);
|
||||
w.Write(latchedSeconds); w.Write(latchedMinutes); w.Write(latchedHours); w.Write(latchedDays);
|
||||
w.Write(latchedHalt); w.Write(latchedCarry);
|
||||
w.Write(previousLatch);
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader r)
|
||||
{
|
||||
seconds = r.ReadInt32(); minutes = r.ReadInt32(); hours = r.ReadInt32(); days = r.ReadInt32();
|
||||
halt = r.ReadBoolean(); carry = r.ReadBoolean(); cycleAccumulator = r.ReadInt64();
|
||||
latchedSeconds = r.ReadInt32(); latchedMinutes = r.ReadInt32(); latchedHours = r.ReadInt32(); latchedDays = r.ReadInt32();
|
||||
latchedHalt = r.ReadBoolean(); latchedCarry = r.ReadBoolean();
|
||||
previousLatch = r.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user