GBA.Unity/Assets/emulator/saving/Eeprom.cs
2024-08-16 14:51:15 +08:00

197 lines
6.0 KiB
C#

using System;
using static OptimeGBA.Bits;
namespace OptimeGBA
{
public enum EepromState
{
Ready,
StartRequest,
ReceiveRequestType,
ReceiveAddrForRead,
ReceiveAddrForWrite,
ReceiveDataForWrite,
ReceiveTerminatingZero
}
public enum EepromSize
{
Eeprom4k,
Eeprom64k
}
public sealed class Eeprom : SaveProvider
{
EepromState State = EepromState.Ready;
EepromSize Size;
public byte[] EEPROM = new byte[0x2000];
public uint Addr = 0;
public uint ReadAddr = 0;
public uint BitsRemaining = 0;
public uint ReadBitsRemaining = 0;
public Gba Gba;
public Eeprom(Gba gba, EepromSize size)
{
Gba = gba;
Size = size;
}
public byte ReadBitEEPROM()
{
byte bitIndex = (byte)(ReadAddr & 7);
uint index = ReadAddr >> 3;
return (byte)(BitTest(EEPROM[index], bitIndex) ? 1 : 0);
}
public void WriteBitEEPROM(bool bit)
{
byte bitIndex = (byte)(Addr & 7);
uint index = Addr >> 3;
if (bit)
{
EEPROM[index] = BitSet(EEPROM[index], bitIndex);
}
else
{
EEPROM[index] = BitClear(EEPROM[index], bitIndex);
}
}
public override byte Read8(uint addr)
{
if (Gba.Dma.DmaLock)
{
// Debug.Log("[EEPROM] Read from DMA");
}
byte val = 0;
if (ReadBitsRemaining > 0)
{
if (ReadBitsRemaining <= 64)
{
val = ReadBitEEPROM();
// Debug.Log($"[EEPROM] Read (addr: {Util.Hex(ReadAddr, 4)}) {val}, bits remaining: " + ReadBitsRemaining);
ReadAddr++;
}
else
{
val = 1;
}
ReadBitsRemaining--;
}
else
{
ReadBitsRemaining = 68;
}
return val;
}
public override void Write8(uint addr, byte val)
{
if (Gba.Dma.DmaLock)
{
// Debug.Log("[EEPROM] Write from DMA");
}
bool bit = BitTest(val, 0);
switch (State)
{
case EepromState.Ready:
if (bit)
{
// Debug.Log("[EEPROM] Request started");
State = EepromState.StartRequest;
}
break;
case EepromState.StartRequest:
BitsRemaining = Size == EepromSize.Eeprom64k ? 14U : 6U;
if (bit)
{
// Debug.Log("[EEPROM] Receiving read address");
State = EepromState.ReceiveAddrForRead;
ReadAddr = 0;
}
else
{
// Debug.Log("[EEPROM] Receiving write address");
State = EepromState.ReceiveAddrForWrite;
Addr = 0;
}
break;
case EepromState.ReceiveAddrForRead:
if (BitsRemaining > 0)
{
ReadAddr |= bit ? 1u : 0u;
ReadAddr <<= 1;
ReadAddr &= 0x3FF;
BitsRemaining--;
// Debug.Log($"[EEPROM] Setting read address ({bit}), bits remaining: {BitsRemaining}");
if (BitsRemaining == 0)
{
// Debug.Log("[EEPROM] Read address written: " + Util.Hex(ReadAddr, 4));
State = EepromState.ReceiveTerminatingZero;
BitsRemaining = 68;
ReadBitsRemaining = 68;
}
}
break;
case EepromState.ReceiveAddrForWrite:
if (BitsRemaining > 0)
{
Addr |= bit ? 1u : 0u;
Addr <<= 1;
Addr &= 0x3FF;
BitsRemaining--;
// Debug.Log($"[EEPROM] Setting write address ({bit}), bits remaining: {BitsRemaining}");
if (BitsRemaining == 0)
{
BitsRemaining = 64;
State = EepromState.ReceiveDataForWrite;
// Debug.Log("[EEPROM] Write address set: " + Util.Hex(Addr, 4));
}
}
break;
case EepromState.ReceiveDataForWrite:
if (BitsRemaining > 0)
{
WriteBitEEPROM(bit);
// Debug.Log($"[EEPROM] Write (addr: {Util.Hex(Addr, 4)}) {Convert.ToByte(bit)}, bits remaining: " + BitsRemaining);
Addr++;
BitsRemaining--;
if (BitsRemaining == 0)
{
// Debug.Log($"[EEPROM] Write finished");
State = EepromState.Ready;
}
}
break;
case EepromState.ReceiveTerminatingZero:
State = EepromState.Ready;
// Debug.Log($"[EEPROM] Received terminating zero");
break;
}
}
public override byte[] GetSave()
{
return EEPROM;
}
public override void LoadSave(byte[] save)
{
for (uint i = 0; i < save.Length && i < EEPROM.Length; i++)
{
EEPROM[i] = save[i];
}
}
}
}