public static void PPBErase(FamicomDumperConnection dumper) { Console.Write($"Erasing all PBBs... "); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K // Sector 0 dumper.WriteCpu(0x5000, 0); dumper.WriteCpu(0x5001, 0); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // All PPB Erase dumper.WriteCpu(0x8000, 0x80); dumper.WriteCpu(0x8000, 0x30); // Check while (true) { var b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); var b1 = dumper.ReadCpu(0x8000, 1)[0]; var tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { Thread.Sleep(1); break; } else// DQ6 = toggle { if ((b0 & (1 << 5)) != 0) // DQ5 = 1 { b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); b1 = dumper.ReadCpu(0x8000, 1)[0]; tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { break; } else { throw new Exception("PPB erase failed"); } } } } var r = dumper.ReadCpu(0x8000, 1)[0]; if ((r & 1) != 1) // DQ0 = 0 { throw new Exception("PPB erase failed"); } // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); Console.WriteLine("OK"); }
public static byte PPBRead(FamicomDumperConnection dumper, uint sector) { // Select sector int bank = (int)(sector * 8); byte r0 = (byte)(((bank >> 3) & 0x07) // 5, 4, 3 bits | (((bank >> 9) & 0x03) << 4) // 10, 9 bits | (1 << 6)); // resets 4th mask bit byte r1 = (byte)((((bank >> 7) & 0x03) << 2) // 8, 7 | (((bank >> 6) & 1) << 4) // 6 | (1 << 7)); // resets 5th mask bit byte r2 = 0; byte r3 = (byte)((1 << 4) // NROM mode | ((bank & 7) << 1)); // 2, 1, 0 bits dumper.WriteCpu(0x6000, new byte[] { r0 }); dumper.WriteCpu(0x6001, new byte[] { r1 }); dumper.WriteCpu(0x6002, new byte[] { r2 }); dumper.WriteCpu(0x6003, new byte[] { r3 }); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // PPB Status Read var result = dumper.ReadCpu(0x8000, 1)[0]; // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); return(result); }
static void ReadPrgRam(FamicomDumperConnection dumper, string fileName, string mapperName) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine("Using mapper: #{0} ({1})", mapper.Number, mapper.Name); } else { Console.WriteLine("Using mapper: {0}", mapper.Name); } mapper.EnablePrgRam(dumper); Console.Write("Reading PRG-RAM... "); var sram = dumper.ReadCpu(0x6000, 0x2000); File.WriteAllBytes(fileName, sram); dumper.ReadCpu(0x0, 1); // to avoid corruption dumper.Reset(); }
static void TestBattery(FamicomDumperConnection dumper, string mapperName) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine("Using mapper: #{0} ({1})", mapper.Number, mapper.Name); } else { Console.WriteLine("Using mapper: {0}", mapper.Name); } mapper.EnablePrgRam(dumper); var rnd = new Random(); var data = new byte[0x2000]; rnd.NextBytes(data); Console.Write("Writing SRAM... "); dumper.WriteCpu(0x6000, data); dumper.Reset(); Console.WriteLine("Replug cartridge and press enter"); Console.ReadLine(); mapper.EnablePrgRam(dumper); Console.Write("Reading SRAM... "); var rdata = dumper.ReadCpu(0x6000, 0x2000); bool ok = true; for (int b = 0; b < 0x2000; b++) { if (data[b] != rdata[b]) { Console.WriteLine("Mismatch at {0:X4}: {1:X2} != {2:X2}", b, rdata[b], data[b]); ok = false; } } if (ok) { Console.WriteLine("OK!"); } else { Console.WriteLine("Failed!"); } }
static void TestPrgRamCoolgirl(FamicomDumperConnection dumper, int count = -1) { TestPrgRam(dumper, "coolgirl", 1); dumper.Reset(); dumper.WriteCpu(0x5007, 0x01); // enable SRAM var rnd = new Random(); while (count != 0) { var data = new byte[][] { new byte[0x2000], new byte[0x2000], new byte[0x2000], new byte[0x2000] }; for (byte bank = 0; bank < 4; bank++) { Console.WriteLine("Writing SRAM, bank #{0}... ", bank); rnd.NextBytes(data[bank]); dumper.WriteCpu(0x5005, bank); dumper.WriteCpu(0x6000, data[bank]); } for (byte bank = 0; bank < 4; bank++) { Console.Write("Reading SRAM, bank #{0}... ", bank); dumper.WriteCpu(0x5005, bank); var rdata = dumper.ReadCpu(0x6000, 0x2000); bool ok = true; for (int b = 0; b < 0x2000; b++) { if (data[bank][b] != rdata[b]) { Console.WriteLine("Mismatch at {0:X4}: {1:X2} != {2:X2}", b, rdata[b], data[bank][b]); ok = false; } } if (!ok) { File.WriteAllBytes("sramgood.bin", data[bank]); Console.WriteLine("sramgood.bin writed"); File.WriteAllBytes("srambad.bin", rdata); Console.WriteLine("srambad.bin writed"); throw new Exception("Test failed"); } Console.WriteLine("OK!"); } count--; } }
static void TestPrgRam(FamicomDumperConnection dumper, string mapperName, int count = -1) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine("Using mapper: #{0} ({1})", mapper.Number, mapper.Name); } else { Console.WriteLine("Using mapper: {0}", mapper.Name); } mapper.EnablePrgRam(dumper); var rnd = new Random(); while (count != 0) { var data = new byte[0x2000]; rnd.NextBytes(data); Console.Write("Writing SRAM... "); dumper.WriteCpu(0x6000, data); Console.Write("Reading SRAM... "); var rdata = dumper.ReadCpu(0x6000, 0x2000); bool ok = true; for (int b = 0; b < 0x2000; b++) { if (data[b] != rdata[b]) { Console.WriteLine("Mismatch at {0:X4}: {1:X2} != {2:X2}", b, rdata[b], data[b]); ok = false; } } if (!ok) { File.WriteAllBytes("sramgood.bin", data); Console.WriteLine("sramgood.bin writed"); File.WriteAllBytes("srambad.bin", rdata); Console.WriteLine("srambad.bin writed"); throw new Exception("Test failed"); } Console.WriteLine("OK!"); count--; } }
public static int GetFlashSize(FamicomDumperConnection dumper) { dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0x90); var autoselect = dumper.ReadCpu(0x8000, 0x100); byte manufacturer = autoselect[0]; var device = new byte[] { autoselect[2], autoselect[0x1C], autoselect[0x1E] }; dumper.WriteCpu(0x8000, 0xF0); // Reset Console.WriteLine("Chip manufacturer ID: {0:X2}", manufacturer); Console.WriteLine("Chip device ID: {0:X2} {1:X2} {2:X2}", device[0], device[1], device[2]); string deviceName; int size; switch ((UInt32)((device[0] << 16) | (device[1] << 8) | (device[2]))) { case 0x7E2801: deviceName = "S29GL01GP"; size = 128 * 1024 * 1024; break; case 0x7E2301: deviceName = "S29GL512GP"; size = 64 * 1024 * 1024; break; case 0x7E2201: deviceName = "S29GL256GP"; size = 32 * 1024 * 1024; break; case 0x7E2101: deviceName = "S29GL128GP"; size = 16 * 1024 * 1024; break; default: throw new Exception("Unknown device ID"); } Console.WriteLine("Device name: {0}", deviceName); Console.WriteLine("Device size: {0} MBytes / {1} Mbit", size / 1024 / 1024, size / 1024 / 1024 * 8); return(size); }
public static byte PPBRead(FamicomDumperConnection dumper, uint sector) { dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K // Select sector byte r0 = (byte)((sector * 4) >> 7); byte r1 = (byte)((sector * 4) << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // PPB Status Read var result = dumper.ReadCpu(0x8000, 1)[0]; // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); return(result); }
public static int GetFlashSizePrintInfo(FamicomDumperConnection dumper) { dumper.WriteCpu(0x8AAA, 0x98); // CFI mode var cfi = dumper.ReadCpu(0x8000, 0x200); dumper.WriteCpu(0x8000, 0xF0); // Reset if (cfi[0x20] != 0x51 || cfi[0x22] != 0x52 || cfi[0x24] != 0x59) { throw new Exception("Can't enter CFI mode. Invalid flash memory? Broken cartridge? Is it inserted?"); } int size = 1 << cfi[0x27 * 2]; FlashDeviceInterface flashDeviceInterface = (FlashDeviceInterface)(cfi[0x28 * 2] | (cfi[0x29 * 2] << 8)); Console.WriteLine("Primary Algorithm Command Set and Control Interface ID Code: {0:X2}{1:X2}h", cfi[0x13 * 2], cfi[0x14 * 2]); Console.WriteLine("Vcc Logic Supply Minimum Program / Erase voltage: {0}v", (cfi[0x1B * 2] >> 4) + 0.1 * (cfi[0x1B * 2] & 0x0F)); Console.WriteLine("Vcc Logic Supply Maximum Program / Erase voltage: {0}v", (cfi[0x1C * 2] >> 4) + 0.1 * (cfi[0x1C * 2] & 0x0F)); Console.WriteLine("Vpp [Programming] Supply Minimum Program / Erase voltage: {0}v", (cfi[0x1D * 2] >> 4) + 0.1 * (cfi[0x1D * 2] & 0x0F)); Console.WriteLine("Vpp [Programming] Supply Maximum Program / Erase voltage: {0}v", (cfi[0x1E * 2] >> 4) + 0.1 * (cfi[0x1E * 2] & 0x0F)); Console.WriteLine("Maximum number of bytes in multi-byte program: {0}", 1 << (cfi[0x2A * 2] | (cfi[0x2B * 2] << 8))); Console.WriteLine("Device size: {0} MByte / {1} Mbit", size / 1024 / 1024, size / 1024 / 1024 * 8); Console.WriteLine("Flash device interface: {0}", flashDeviceInterface.ToString().Replace("_", " ")); return(size); }
public static void PPBErase(FamicomDumperConnection dumper) { Console.Write($"Erasing all PBBs... "); // Sector 0 int bank = 0; byte r0 = (byte)(((bank >> 3) & 0x07) // 5, 4, 3 bits | (((bank >> 9) & 0x03) << 4) // 10, 9 bits | (1 << 6)); // resets 4th mask bit byte r1 = (byte)((((bank >> 7) & 0x03) << 2) // 8, 7 | (((bank >> 6) & 1) << 4) // 6 | (1 << 7)); // resets 5th mask bit byte r2 = 0; byte r3 = (byte)((1 << 4) // NROM mode | ((bank & 7) << 1)); // 2, 1, 0 bits dumper.WriteCpu(0x6000, new byte[] { r0 }); dumper.WriteCpu(0x6001, new byte[] { r1 }); dumper.WriteCpu(0x6002, new byte[] { r2 }); dumper.WriteCpu(0x6003, new byte[] { r3 }); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // All PPB Erase dumper.WriteCpu(0x8000, 0x80); dumper.WriteCpu(0x8000, 0x30); // Check while (true) { var b0 = dumper.ReadCpu(0x8000, 1)[0]; var b1 = dumper.ReadCpu(0x8000, 1)[0]; var tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { Thread.Sleep(1); break; } else// DQ6 = toggle { if ((b0 & (1 << 5)) != 0) // DQ5 = 1 { b0 = dumper.ReadCpu(0x8000, 1)[0]; b1 = dumper.ReadCpu(0x8000, 1)[0]; tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { break; } else { throw new Exception("PPB erase failed"); } } } } var r = dumper.ReadCpu(0x8000, 1)[0]; if ((r & 1) != 1) // DQ0 = 0 { throw new Exception("PPB erase failed"); } // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); Console.WriteLine("OK"); }
public static void FindBads(FamicomDumperConnection dumper, bool silent) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K PPBErase(dumper); dumper.WriteCpu(0x5000, 0); dumper.WriteCpu(0x5001, 0); var flashSize = CommonHelper.GetFlashSizePrintInfo(dumper); int prgBanks = flashSize / 0x8000; Console.Write("Erasing sector #0... "); dumper.ErasePrgFlash(FamicomDumperConnection.FlashAccessType.Direct); Console.WriteLine("OK"); var data = new byte[0x8000]; new Random().NextBytes(data); Console.Write("Writing sector #0 for test... "); dumper.WritePrgFlash(0x0000, data, FamicomDumperConnection.FlashAccessType.Direct, false); Console.WriteLine("OK"); Console.Write("Reading sector #0 for test... "); var datar = dumper.ReadCpu(0x8000, 0x8000); for (int i = 0; i < data.Length; i++) { if (data[i] != datar[i]) { throw new Exception("Check failed"); } } Console.WriteLine("OK"); var writeStartTime = DateTime.Now; var lastSectorTime = DateTime.Now; var timeTotal = new TimeSpan(); var badSectors = new List <int>(); for (int bank = 0; bank < prgBanks; bank += 4) { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); timeTotal = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (prgBanks - bank) / 4); timeTotal = timeTotal.Add(DateTime.Now - writeStartTime); lastSectorTime = DateTime.Now; var timePassed = DateTime.Now - writeStartTime; Console.Write("Erasing sector {0}/{1} ({2}%, {3:D2}:{4:D2}:{5:D2}/{6:D2}:{7:D2}:{8:D2})... ", bank / 4 + 1, prgBanks / 4, (int)(100 * bank / prgBanks), timePassed.Hours, timePassed.Minutes, timePassed.Seconds, timeTotal.Hours, timeTotal.Minutes, timeTotal.Seconds); try { dumper.ErasePrgFlash(FamicomDumperConnection.FlashAccessType.Direct); Console.WriteLine("OK"); } catch { Console.WriteLine("ERROR!"); if (!silent) { Program.errorSound.PlaySync(); } Console.Write("Trying again... "); dumper.Reset(); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); try { dumper.ErasePrgFlash(FamicomDumperConnection.FlashAccessType.Direct); Console.WriteLine("OK"); } catch { Console.WriteLine("ERROR! Sector #{0} is bad.", bank / 4); if (!silent) { Program.errorSound.PlaySync(); } badSectors.Add(bank / 4); } } } if (badSectors.Count > 0) { foreach (var bad in badSectors) { Console.WriteLine("Bad sector: {0}", bad); } throw new Exception("Bad sectors found"); } else { Console.WriteLine("There is no bad sectors"); } }
public static void PPBWrite(FamicomDumperConnection dumper, uint sector) { Console.Write($"Writing PPB for sector #{sector}... "); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K // Select sector byte r0 = (byte)((sector * 4) >> 7); byte r1 = (byte)((sector * 4) << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // PPB Program dumper.WriteCpu(0x8000, 0xA0); dumper.WriteCpu(0x8000, 0x00); // Sector 0 dumper.WriteCpu(0x5000, 0); dumper.WriteCpu(0x5001, 0); // Check while (true) { var b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); var b1 = dumper.ReadCpu(0x8000, 1)[0]; var tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { Thread.Sleep(1); break; } else// DQ6 = toggle { if ((b0 & (1 << 5)) != 0) // DQ5 = 1 { b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); b1 = dumper.ReadCpu(0x8000, 1)[0]; tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { break; } else { throw new Exception("PPB write failed"); } } } } var r = dumper.ReadCpu(0x8000, 1)[0]; if ((r & 1) != 0) // DQ0 = 1 { throw new Exception("PPB write failed"); } // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); Console.WriteLine("OK"); }
public static void PPBWrite(FamicomDumperConnection dumper, ushort coolboyReg, uint sector) { Console.Write($"Writing PPB for sector #{sector}... "); // Select sector int bank = (int)(sector * 8); byte r0 = (byte)(((bank >> 3) & 0x07) // 5, 4, 3 bits | (((bank >> 9) & 0x03) << 4) // 10, 9 bits | (1 << 6)); // resets 4th mask bit byte r1 = (byte)((((bank >> 7) & 0x03) << 2) // 8, 7 | (((bank >> 6) & 1) << 4) // 6 | (1 << 7)); // resets 5th mask bit byte r2 = 0; byte r3 = (byte)((1 << 4) // NROM mode | ((bank & 7) << 1)); // 2, 1, 0 bits dumper.WriteCpu(coolboyReg, new byte[] { r0, r1, r2, r3 }); // PPB Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0xC0); // PPB Program dumper.WriteCpu(0x8000, 0xA0); dumper.WriteCpu(0x8000, 0x00); // Check while (true) { var b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); var b1 = dumper.ReadCpu(0x8000, 1)[0]; var tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { Thread.Sleep(1); break; } else// DQ6 = toggle { if ((b0 & (1 << 5)) != 0) // DQ5 = 1 { b0 = dumper.ReadCpu(0x8000, 1)[0]; //dumper.ReadCpu(0x0000, 1); b1 = dumper.ReadCpu(0x8000, 1)[0]; tg = b0 ^ b1; if ((tg & (1 << 6)) == 0) // DQ6 = not toggle { break; } else { throw new Exception("PPB write failed"); } } } } var r = dumper.ReadCpu(0x8000, 1)[0]; if ((r & 1) != 0) // DQ0 = 1 { throw new Exception("PPB write failed"); } // PPB Command Set Exit dumper.WriteCpu(0x8000, 0x90); dumper.WriteCpu(0x8000, 0x00); Console.WriteLine("OK"); }