diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index 61eb806bc8..dcae7c5ba1 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -304,6 +304,7 @@
+
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/BANDAI-FCG-1.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/BANDAI-FCG-1.cs
index b004416d65..3cf23de4ad 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/BANDAI-FCG-1.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/BANDAI-FCG-1.cs
@@ -30,26 +30,26 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
//configuration
int prg_bank_mask_16k, chr_bank_mask_1k;
- bool has_eprom = false;
//regenerable state
IntBuffer prg_banks_16k = new IntBuffer(2);
//state
- int prg_reg_16k, eprom;
+ int prg_reg_16k;
ByteBuffer regs = new ByteBuffer(8);
bool irq_enabled;
ushort irq_counter;
+ SEEPROM eprom;
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
ser.Sync("prg_reg_16k", ref prg_reg_16k);
ser.Sync("regs", ref regs);
- ser.Sync("eprom", ref eprom);
ser.Sync("irq_counter", ref irq_counter);
ser.Sync("irq_enabled", ref irq_enabled);
-
+ if (eprom != null)
+ eprom.SyncState(ser);
SyncPRG();
}
@@ -64,23 +64,31 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
switch (Cart.board_type)
{
- case "BANDAI-FCG-1":
- AssertPrg(128, 256, 512); AssertChr(128, 256); AssertWram(0, 8); AssertVram(0);
+ case "BANDAI-FCG-1": // no eprom
+ AssertPrg(128, 256, 512); AssertChr(128, 256); AssertWram(0); AssertVram(0);
break;
- case "BANDAI-FCG-2":
+ case "BANDAI-FCG-2": // no eprom
AssertPrg(128); AssertChr(128); AssertWram(0); AssertVram(0);
break;
- case "BANDAI-LZ93D50+24C01":
+ case "BANDAI-LZ93D50+24C01": // 1kbit eprom
AssertPrg(128, 256); AssertChr(128, 256); AssertWram(0); AssertVram(0);
+ eprom = new SEEPROM(false);
break;
- case "BANDAI-LZ93D50+24C02":
- AssertPrg(128, 256); AssertChr(128, 256); AssertWram(0, 8); AssertVram(0);
+ case "BANDAI-LZ93D50+24C02": // 2kbit eprom
+ AssertPrg(128, 256); AssertChr(128, 256); AssertWram(0); AssertVram(0);
+ eprom = new SEEPROM(true);
break;
+ /* if implementing NES mappers, a way must be found to reliably determine which
+ * eprom variety is in use
+ case "MAPPER016": // TEST TEST
+ Cart.wram_size = 0;
+ Cart.vram_size = 0;
+ eprom = new SEEPROM(false);
+ break;
+ */
default:
return false;
}
- if (Cart.mapper == 159)
- has_eprom = true;
prg_bank_mask_16k = (Cart.prg_size / 16) - 1;
chr_bank_mask_1k = Cart.chr_size - 1;
@@ -142,7 +150,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
irq_counter |= (ushort)(value << 8);
break;
case 0xD:
- eprom = value;
+ if (eprom != null)
+ eprom.WriteByte(value);
break;
}
}
@@ -163,8 +172,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override byte ReadWRAM(int addr)
{
// reading any addr in 6000:7fff returns a single bit from the eeprom
- // in bit 4. zeroing that bit seems sufficient for some games to boot
- return (byte)(NES.DB & 0xef);
+ // in bit 4.
+ byte ret = (byte)(NES.DB & 0xef);
+ if (eprom != null && eprom.ReadBit(NES.DB.Bit(4)))
+ ret |= 0x10;
+ return ret;
}
public override void ClockCPU()
@@ -205,5 +217,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
else return base.ReadPPU(addr);
}
+ public override byte[] SaveRam
+ {
+ get
+ {
+ if (eprom != null)
+ return eprom.GetSaveRAM();
+ else
+ return null;
+ }
+ }
}
}
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SEEPROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SEEPROM.cs
new file mode 100644
index 0000000000..96f96e3892
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SEEPROM.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.Emulation.Consoles.Nintendo
+{
+ ///
+ /// describes a 24C01 or 24C02 as connected to a BANDAI-FCG
+ ///
+
+ // http://pdf1.alldatasheet.com/datasheet-pdf/view/56094/ATMEL/24C01.html 24C01
+ // http://www.atmel.com/Images/doc0180.pdf 24C02 and others
+ public class SEEPROM
+ {
+ ///
+ /// true if 256byte
+ ///
+ bool Big;
+
+ byte[] rom;
+ /// aux circuitry? D7 of data byte
+ bool OutEnable = false;
+ /// asserted by master
+ bool SCK = false;
+ /// asserted by master
+ bool SDA = false;
+ /// true if the SEEPROM is trying to pull down the SDA line
+ bool PullDown = false;
+
+
+ /// number of bits left to send\recv of current byte
+ int BitsLeft;
+ /// current data byte in progress
+ byte Data;
+ /// current chip addr
+ byte Addr;
+
+
+ enum EState
+ {
+ Off, Select, Ignore, Address, Read, Write
+ };
+ EState State;
+
+ ///
+ /// called on the 9th bit of a write
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// called on rising edge of SCK. output bit, if any, can be set by PullDown
+ ///
+ /// input bit
+ 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;
+ }
+
+ ///
+ /// read a bit back from eprom, might be mapped in 6000:7fff
+ ///
+ /// bit from NES.DB
+ ///
+ public bool ReadBit(bool deadbit)
+ {
+ if (!OutEnable)
+ return deadbit;
+ if (!SDA)
+ return false;
+ return !PullDown;
+ }
+
+ public byte[] GetSaveRAM() { return rom; }
+
+ ///
+ ///
+ ///
+ /// 256 byte instead of 128 byte
+ 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();
+ }
+ }
+}