From 0aa5e2a51244cf52b75cff785b45539d4aefac20 Mon Sep 17 00:00:00 2001 From: goyuken Date: Mon, 17 Dec 2012 19:54:45 +0000 Subject: [PATCH] NES: try implementing SEEPROM for BANDAI-FGC boards. Seems to work reading, but I can't get far enough into any of the games for writing. Like the rest of the mapper, only works with crc id because I don't know how to positively identify any of this from ines headers. Affected games: 24C01 http://bootgod.dyndns.org:7777/search.php?keywords=BANDAI-LZ93D50%2B24C01&kwtype=pcb 24C02 http://bootgod.dyndns.org:7777/search.php?keywords=BANDAI-LZ93D50%2B24C02&kwtype=pcb --- BizHawk.Emulation/BizHawk.Emulation.csproj | 1 + .../Nintendo/NES/Boards/BANDAI-FCG-1.cs | 52 +++- .../Consoles/Nintendo/NES/Boards/SEEPROM.cs | 263 ++++++++++++++++++ 3 files changed, 301 insertions(+), 15 deletions(-) create mode 100644 BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SEEPROM.cs 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(); + } + } +}