BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/SEEPROM.cs

264 lines
5.9 KiB
C#

using System;
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Emulation.Cores.Nintendo.NES
{
/// <summary>
/// describes a 24C01 or 24C02 as connected to a BANDAI-FCG
/// </summary>
// http://pdf1.alldatasheet.com/datasheet-pdf/view/56094/ATMEL/24C01.html 24C01
// http://www.atmel.com/Images/doc0180.pdf 24C02 and others
public sealed class SEEPROM
{
/// <summary>
/// true if 256byte
/// </summary>
bool Big;
byte[] rom;
/// <summary>aux circuitry? D7 of data byte</summary>
bool OutEnable = false;
/// <summary>asserted by master</summary>
bool SCK = false;
/// <summary>asserted by master</summary>
bool SDA = false;
/// <summary>true if the SEEPROM is trying to pull down the SDA line</summary>
bool PullDown = false;
/// <summary>number of bits left to send\recv of current byte</summary>
int BitsLeft;
/// <summary>current data byte in progress</summary>
byte Data;
/// <summary>current chip addr</summary>
byte Addr;
enum EState
{
Off, Select, Ignore, Address, Read, Write
};
EState State;
/// <summary>
/// called on the 9th bit of a write
/// </summary>
void ClockByteWrite()
{
if (State == EState.Write)
{
PullDown = true; // ack
// commit
Console.WriteLine("{1:x2} => rom[{0:x2}]", Addr, Data);
rom[Addr] = Data;
Addr++;
Addr &= (byte)(rom.Length - 1);
// next byte
BitsLeft = 8;
}
else if (State == EState.Select)
{
if (Big) // 24C02: select contains a device selector, plus mode
{
Console.WriteLine("256B Select: {0:x2}", Data);
// device selector byte should be 1010 000x
// x = 0: write. x = 1: read
if ((Data & 0xfe) != 0xa0)
{
Console.WriteLine("STATE: IGNORE");
State = EState.Ignore;
}
else
{
if (Data.Bit(0))
{
PullDown = true; // ack
Console.WriteLine("STATE: READ");
State = EState.Read;
BitsLeft = 8;
Data = rom[Addr];
}
else
{
PullDown = true; // ack
Console.WriteLine("STATE: ADDRESS");
State = EState.Address;
BitsLeft = 8;
}
}
}
else // 24C01: select contains a 7 bit address, plus mode
{
Addr = (byte)(Data >> 1);
Console.WriteLine("128B Addr: {0:x2}", Addr);
if (Data.Bit(0))
{
PullDown = true; // ack
Console.WriteLine("STATE: READ");
State = EState.Read;
BitsLeft = 8;
Data = rom[Addr];
}
else
{
PullDown = true; // ack
Console.WriteLine("STATE: WRITE");
State = EState.Write;
BitsLeft = 8;
}
}
}
else if (State == EState.Address) // (Only on 24C02): a byte of address
{
Addr = Data;
Console.WriteLine("256B Addr: {0:x2}", Data);
PullDown = true; // ack
Console.WriteLine("STATE: WRITE"); // to random read, the device will be set to read mode right after this
State = EState.Write;
BitsLeft = 8;
}
}
/// <summary>
/// called on rising edge of SCK. output bit, if any, can be set by PullDown
/// </summary>
/// <param name="bit">input bit</param>
void ClockBit(bool bit)
{
switch (State)
{
case EState.Off:
case EState.Ignore:
break;
case EState.Select:
case EState.Address:
case EState.Write:
if (BitsLeft > 0)
{
BitsLeft--;
if (bit)
Data |= (byte)(1 << BitsLeft);
else
Data &= (byte)~(1 << BitsLeft);
}
else // "9th" bit
ClockByteWrite();
break;
case EState.Read:
if (BitsLeft > 0)
{
BitsLeft--;
PullDown = !Data.Bit(BitsLeft);
}
else // 0 bits left: master acknowledges, and prepare another byte
{
if (bit)
{
// master didn't acknowledge. what to do?
}
Console.WriteLine("{1:x2} <= rom[{0:x2}]", Addr, Data);
Addr++;
Addr &= (byte)(rom.Length - 1);
Data = rom[Addr];
BitsLeft = 8;
}
break;
}
}
void ClockStart()
{
State = EState.Select;
BitsLeft = 8;
Console.WriteLine("STATE: SELECT");
}
void ClockStop()
{
State = EState.Off;
Console.WriteLine("STATE: OFF");
PullDown = false;
}
public void WriteByte(byte val)
{
OutEnable = val.Bit(7);
bool newSDA = val.Bit(6);
bool newSCK = val.Bit(5);
if (!newSCK) // falling or inactive SCK: cancel any active ack / readback
{
PullDown = false;
}
else
{
if (!SCK) // rising edge
{
ClockBit(newSDA);
}
else // clock stays high; look for changes in SDA
{
if (!SDA && newSDA)
{
ClockStop();
}
else if (SDA && !newSDA)
{
ClockStart();
}
}
}
SCK = newSCK;
SDA = newSDA;
}
/// <summary>
/// read a bit back from eprom, might be mapped in 6000:7fff
/// </summary>
/// <param name="deadbit">bit from NES.DB</param>
/// <returns></returns>
public bool ReadBit(bool deadbit)
{
if (!OutEnable)
return deadbit;
if (!SDA)
return false;
return !PullDown;
}
public byte[] GetSaveRAM() { return rom; }
/// <summary>
///
/// </summary>
/// <param name="Big">256 byte instead of 128 byte</param>
public SEEPROM(bool Big)
{
rom = new byte[Big ? 256 : 128];
this.Big = Big;
}
public void SyncState(Serializer ser)
{
ser.BeginSection("SEEPROM");
ser.Sync("rom", ref rom, false);
ser.Sync("OutEnable", ref OutEnable);
ser.Sync("SCK", ref SCK);
ser.Sync("SDA", ref SDA);
ser.Sync("PullDown", ref PullDown);
ser.Sync("BitsLeft", ref BitsLeft);
ser.Sync("Data", ref Data);
ser.Sync("Addr", ref Addr);
int tmp = (int)State;
ser.Sync("State", ref tmp);
State = (EState)tmp;
ser.EndSection();
}
}
}