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 TestChrRam(FamicomDumperConnection dumper) { var rnd = new Random(); while (true) { var data = new byte[0x2000]; rnd.NextBytes(data); Console.Write("Writing CHR RAM... "); dumper.WritePpu(0x0000, data); Console.Write("Reading CHR RAM... "); var rdata = dumper.ReadPpu(0x0000, 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("chrgood.bin", data); Console.WriteLine("chrgood.bin writed"); File.WriteAllBytes("chrbad.bin", rdata); Console.WriteLine("chrbad.bin writed"); throw new Exception("Test failed"); } Console.WriteLine("OK!"); } }
static void TestCoolgirlFull(FamicomDumperConnection dumper) { while (true) { TestChrRamCoolgirl(dumper, 2); TestPrgRamCoolgirl(dumper, 5); } }
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 void GetInfo(FamicomDumperConnection dumper) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K CommonHelper.GetFlashSizePrintInfo(dumper); }
public static void FullTest(FamicomDumperConnection dumper, int count = -1) { while (count != 0) { TestChrRam(dumper, 1); TestPrgRam(dumper, 1); if (count > 0) { count--; } } }
static void WriteJtag(FamicomDumperConnection dumper, string fileName) { Console.Write("JTAG setup... "); dumper.JtagSetup(); Console.WriteLine("OK"); Console.Write("JTAG programming... "); dumper.WriteJtag(File.ReadAllBytes(fileName)); Console.WriteLine("Done!"); Console.Write("JTAG shutdown... "); dumper.JtagShutdown(); Console.WriteLine("OK"); }
static void WriteCoolgirl(FamicomDumperConnection dumper, string fileName) { byte[] PRG; try { var nesFile = new NesFile(fileName); PRG = nesFile.PRG; } catch { var nesFile = new UnifFile(fileName); PRG = nesFile.Fields["PRG0"]; } int prgBanks = PRG.Length / 0x8000; Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0x5007, 0x04); // enable PRG write DateTime lastSectorTime = DateTime.Now; TimeSpan timeTotal = new TimeSpan(); for (int bank = 0; bank < prgBanks; bank++) { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); dumper.WriteCpu(0x5002, 0xFE); var data = new byte[0x8000]; int pos = bank * 0x8000; if (pos % (128 * 1024) == 0) { timeTotal = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (prgBanks - bank) / 4); timeTotal = timeTotal.Add(DateTime.Now - startTime); lastSectorTime = DateTime.Now; Console.Write("Erasing sector... "); dumper.ErasePrgFlash(FamicomDumperConnection.FlashType.Coolgirl); Console.WriteLine("OK"); } Array.Copy(PRG, pos, data, 0, data.Length); var timePassed = DateTime.Now - startTime; //var timeTotal = new TimeSpan((DateTime.Now - startTime).Ticks * prgBanks / (bank + 1)); Console.Write("Writing {0}/{1} ({2}%, {3:D2}:{4:D2}:{5:D2}/{6:D2}:{7:D2}:{8:D2})... ", bank + 1, prgBanks, (int)(100 * bank / prgBanks), timePassed.Hours, timePassed.Minutes, timePassed.Seconds, timeTotal.Hours, timeTotal.Minutes, timeTotal.Seconds); dumper.WritePrgFlash(0x0000, data, FamicomDumperConnection.FlashType.Coolgirl, true); Console.WriteLine("OK"); } }
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--; } }
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); }
static void WriteEeprom(FamicomDumperConnection dumper, string fileName) { var nesFile = new NesFile(fileName); var prg = new byte[0x8000]; int s = 0; while (s < prg.Length) { var n = Math.Min(nesFile.PRG.Length, prg.Length - s); Array.Copy(nesFile.PRG, s % nesFile.PRG.Length, prg, s, n); s += n; } var chr = new byte[0x2000]; s = 0; while (s < chr.Length) { var n = Math.Min(nesFile.CHR.Length, chr.Length - s); Array.Copy(nesFile.CHR, s % nesFile.CHR.Length, chr, s, n); s += n; } dumper.Timeout = 1000; var buff = new byte[64]; Console.Write("Writing PRG EEPROM"); for (UInt16 a = 0; a < prg.Length; a += 64) { Array.Copy(prg, a, buff, 0, buff.Length); dumper.WriteCpu((UInt16)(0x8000 + a), buff); Thread.Sleep(3); Console.Write("."); } Console.WriteLine(" OK"); Console.Write("Writing CHR EEPROM"); for (UInt16 a = 0; a < chr.Length; a += 64) { Array.Copy(chr, a, buff, 0, buff.Length); dumper.WritePpu(a, buff); Thread.Sleep(3); Console.Write("."); } Console.WriteLine(" OK"); }
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--; } }
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 WritePrgRam(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("Writing PRG-RAM... "); var sram = File.ReadAllBytes(fileName); dumper.WriteCpu(0x6000, sram); dumper.ReadCpu(0x0, 1); // to avoid corruption dumper.Reset(); }
public static void GetInfo(FamicomDumperConnection dumper) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); var version = DetectVersion(dumper); var CoolboyReg = (UInt16)(version == 2 ? 0x5000 : 0x6000); 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(CoolboyReg, new byte[] { r0, r1, r2, r3 }); CommonHelper.GetFlashSizePrintInfo(dumper); }
public static byte DetectVersion(FamicomDumperConnection dumper) { byte version; Console.Write("Detecting COOLBOY version... "); // 0th CHR bank using both methods dumper.WriteCpu(0x5000, new byte[] { 0, 0, 0, 0x10 }); dumper.WriteCpu(0x6000, new byte[] { 0, 0, 0, 0x10 }); // Writing 0 dumper.WritePpu(0x0000, new byte[] { 0 }); // First CHR bank using both methods dumper.WriteCpu(0x5000, new byte[] { 0, 0, 1, 0x10 }); dumper.WriteCpu(0x6000, new byte[] { 0, 0, 1, 0x10 }); // Writing 1 dumper.WritePpu(0x0000, new byte[] { 1 }); // 0th bank using first method dumper.WriteCpu(0x6000, new byte[] { 0, 0, 0, 0x10 }); byte v6000 = dumper.ReadPpu(0x0000, 1)[0]; // return dumper.WriteCpu(0x6000, new byte[] { 0, 0, 1, 0x10 }); // 0th bank using second method dumper.WriteCpu(0x5000, new byte[] { 0, 0, 0, 0x10 }); byte v5000 = dumper.ReadPpu(0x0000, 1)[0]; if (v6000 == 0 && v5000 == 1) { version = 1; } else if (v6000 == 1 && v5000 == 0) { version = 2; } else { throw new Exception("Can't detect COOLBOY version"); } Console.WriteLine("Version: {0}", version); return(version); }
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); }
static void LuaConsole(FamicomDumperConnection dumper, LuaMapper luaMapper) { luaMapper.Verbose = true; Console.WriteLine("Starting interactive Lua console, type \"exit\" to exit."); while (true) { Console.Write("> "); var line = Console.ReadLine(); if (line.ToLower().Trim() == "exit") { break; } try { luaMapper.Execute(dumper, line, false); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } } }
public static void ReadCrc(FamicomDumperConnection dumper) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K var flashSize = CommonHelper.GetFlashSizePrintInfo(dumper); int prgBanks = flashSize / 0x8000; var readStartTime = DateTime.Now; var lastSectorTime = DateTime.Now; var timeTotal = new TimeSpan(); UInt16 crc = 0; for (int bank = 0; bank < /*16*/ prgBanks; bank++) { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); var data = new byte[0x8000]; int pos = bank * 0x8000; if (pos % (128 * 1024) == 0) { timeTotal = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (prgBanks - bank) / 4); timeTotal = timeTotal.Add(DateTime.Now - readStartTime); lastSectorTime = DateTime.Now; } var timePassed = DateTime.Now - readStartTime; Console.Write("Reading CRC {0}/{1} ({2}%, {3:D2}:{4:D2}:{5:D2}/{6:D2}:{7:D2}:{8:D2})... ", bank + 1, prgBanks, (int)(100 * bank / prgBanks), timePassed.Hours, timePassed.Minutes, timePassed.Seconds, timeTotal.Hours, timeTotal.Minutes, timeTotal.Seconds); var crcr = dumper.ReadCpuCrc(0x8000, 0x8000); Console.WriteLine("CRC = {0:X4}", crcr); crc ^= crcr; } Console.WriteLine("Total CRC = {0:X4}", crc); }
public static void GetInfo(FamicomDumperConnection dumper) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); 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 }); CommonHelper.GetFlashSize(dumper); }
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); }
static void DumpTiles(FamicomDumperConnection dumper, string fileName, string mapperName, int chrSize, int tilesPerLine = 16) { 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); } Console.WriteLine("Dumping..."); List <byte> chr = new List <byte>(); chrSize = chrSize >= 0 ? chrSize : mapper.DefaultChrSize; Console.WriteLine("CHR memory size: {0}K", chrSize / 1024); mapper.DumpChr(dumper, chr, chrSize); var tiles = new TilesExtractor(chr.ToArray()); var allTiles = tiles.GetAllTiles(); Console.WriteLine("Saving to {0}...", fileName); allTiles.Save(fileName, ImageFormat.Png); }
static void Reset(FamicomDumperConnection dumper) { Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); }
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"); }
static void WriteCoolboy(FamicomDumperConnection dumper, string fileName) { byte[] PRG; try { var nesFile = new NesFile(fileName); PRG = nesFile.PRG; } catch { var nesFile = new UnifFile(fileName); PRG = nesFile.Fields["PRG0"]; } while (PRG.Length < 512 * 1024) { var PRGbig = new byte[PRG.Length * 2]; Array.Copy(PRG, 0, PRGbig, 0, PRG.Length); Array.Copy(PRG, 0, PRGbig, PRG.Length, PRG.Length); PRG = PRGbig; } int prgBanks = PRG.Length / 0x2000; Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0xA001, 0x00); // RAM protect DateTime lastSectorTime = DateTime.Now; TimeSpan timeTotal = new TimeSpan(); for (int bank = 0; bank < prgBanks; bank += 2) { int outbank = bank / 16; byte r0 = (byte)((outbank & 0x07) | ((outbank & 0xc0) >> 2)); byte r1 = (byte)(((outbank & 0x30) >> 2) | ((outbank << 1) & 0x10)); byte r2 = 0; byte r3 = 0; dumper.WriteCpu(0x6000, new byte[] { r0 }); dumper.WriteCpu(0x6001, new byte[] { r1 }); dumper.WriteCpu(0x6002, new byte[] { r2 }); dumper.WriteCpu(0x6003, new byte[] { r3 }); int inbank = bank % 64; dumper.WriteCpu(0x8000, new byte[] { 6, (byte)(inbank) }); dumper.WriteCpu(0x8000, new byte[] { 7, (byte)(inbank | 1) }); var data = new byte[0x4000]; int pos = bank * 0x2000; if (pos % (128 * 1024) == 0) { timeTotal = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (prgBanks - bank) / 16); timeTotal = timeTotal.Add(DateTime.Now - startTime); lastSectorTime = DateTime.Now; Console.Write("Erasing sector... "); dumper.ErasePrgFlash(FamicomDumperConnection.FlashType.Coolboy); Console.WriteLine("OK"); } Array.Copy(PRG, pos, data, 0, data.Length); var timePassed = DateTime.Now - startTime; Console.Write("Writing {0}/{1} ({2}%, {3:D2}:{4:D2}:{5:D2}/{6:D2}:{7:D2}:{8:D2})... ", bank / 2 + 1, prgBanks / 2, (int)(100 * bank / prgBanks), timePassed.Hours, timePassed.Minutes, timePassed.Seconds, timeTotal.Hours, timeTotal.Minutes, timeTotal.Seconds); dumper.WritePrgFlash(0x0000, data, FamicomDumperConnection.FlashType.Coolboy, true); Console.WriteLine("OK"); } }
static void WriteFlash(FamicomDumperConnection dumper, string fileName, WriteFlashRotMode rotMode = WriteFlashRotMode.None) { var nesFile = new NesFile(fileName); int prgBanks = nesFile.PRG.Length / 0x8000; if (prgBanks * 0x8000 < nesFile.PRG.Length) { prgBanks++; } int chrBanks = nesFile.CHR.Length / 0x2000; if (chrBanks * 0x2000 < nesFile.CHR.Length) { chrBanks++; } Console.Write("Erasing PRG FLASH... "); dumper.Timeout = 3000000; dumper.ErasePrgFlash(); Console.WriteLine("Done!"); Console.Write("Writing PRG FLASH... "); dumper.Timeout = 10000; int writed = 0; for (byte bank = 0; bank < 16; bank++) { int pos; if (bank < prgBanks) { pos = bank * 0x8000; } else if (rotMode == WriteFlashRotMode.LastBanksOnly) { if (bank >= prgBanks && bank < 15) { continue; } pos = (prgBanks - 1) * 0x8000; // last bank } else if (rotMode == WriteFlashRotMode.Full) { pos = (bank % prgBanks) * 0x8000; } else { continue; } var data = new byte[0x8000]; Array.Copy(nesFile.PRG, pos, data, 0, Math.Min(0x8000, nesFile.PRG.Length - pos)); if (bank == prgBanks - 1 && nesFile.PRG.Length - pos < 0x8000) { Array.Copy(nesFile.PRG, pos, data, 0x8000 - (nesFile.PRG.Length - pos), nesFile.PRG.Length - pos); } dumper.WriteCpu(0x0000, (byte)(bank)); dumper.WritePrgFlash(0x0000, data); writed++; switch (rotMode) { case WriteFlashRotMode.None: Console.Write("{0}% ", 100 * writed / prgBanks); break; case WriteFlashRotMode.LastBanksOnly: Console.Write("{0}% ", 100 * writed / (prgBanks + 1)); break; case WriteFlashRotMode.Full: Console.Write("{0}% ", 100 * writed / 16); break; } } Console.WriteLine("Done! {0} banks writed.", writed); Console.Write("Erasing CHR FLASH... "); dumper.EraseChrFlash(); Console.WriteLine("Done!"); Console.Write("Writing CHR FLASH... "); for (byte bank = 0; bank < chrBanks; bank++) { int pos = bank * 0x2000; var data = new byte[0x2000]; Array.Copy(nesFile.CHR, pos, data, 0, Math.Min(0x2000, nesFile.CHR.Length - pos)); dumper.WriteCpu(0x4000, bank); dumper.WriteChrFlash(0x0000, data); if (chrBanks > 1) { Console.Write("{0}% ", 100 * (bank + 1) / chrBanks); } } Console.WriteLine("Done! {0} banks writed.", chrBanks); }
static void Bootloader(FamicomDumperConnection dumper) { Console.WriteLine("Rebooting to bootloader..."); dumper.Bootloader(); }
static void TestChrRamCoolgirl(FamicomDumperConnection dumper, int count = -1) { dumper.Reset(); dumper.WriteCpu(0x5007, 0x2); // enable CHR writing var rnd = new Random(); var data = new byte[0x2000]; rnd.NextBytes(data); Console.WriteLine("Basic test."); Console.Write("Writing CHR RAM... "); dumper.WritePpu(0x0000, data); Console.Write("Reading CHR RAM... "); var rdata = dumper.ReadPpu(0x0000, 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("chrgood.bin", data); Console.WriteLine("chrgood.bin writed"); File.WriteAllBytes("chrbad.bin", rdata); Console.WriteLine("chrbad.bin writed"); throw new Exception("Test failed"); } Console.WriteLine("OK!"); Console.WriteLine("Global test."); data = new byte[256 * 1024]; while (count != 0) { dumper.Reset(); dumper.WriteCpu(0x5007, 0x2); // enable CHR writing rnd.NextBytes(data); for (byte bank = 0; bank < 32; bank++) { Console.WriteLine("Writing CHR RAM bank #{0}...", bank); dumper.WriteCpu(0x5003, bank); // select bank var d = new byte[0x2000]; Array.Copy(data, bank * 0x2000, d, 0, 0x2000); dumper.WritePpu(0x0000, d); } for (byte bank = 0; bank < 32; bank++) { Console.Write("Reading CHR RAM bank #{0}... ", bank); dumper.WriteCpu(0x5003, bank); // select bank rdata = dumper.ReadPpu(0x0000, 0x2000); ok = true; for (int b = 0; b < 0x2000; b++) { if (data[b + bank * 0x2000] != rdata[b]) { Console.WriteLine("Mismatch at {0:X4}: {1:X2} != {2:X2}", b, rdata[b], data[b + bank * 0x2000]); ok = false; } } if (ok) { Console.WriteLine("OK!"); } else { throw new Exception("Test failed"); } } count--; } }
static void Dump(FamicomDumperConnection dumper, string fileName, string mapperName, int prgSize, int chrSize, string unifName, string unifAuthor) { 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); } Console.WriteLine("Dumping..."); List <byte> prg = new List <byte>(); List <byte> chr = new List <byte>(); prgSize = prgSize >= 0 ? prgSize : mapper.DefaultPrgSize; chrSize = chrSize >= 0 ? chrSize : mapper.DefaultChrSize; Console.WriteLine("PRG memory size: {0}K", prgSize / 1024); mapper.DumpPrg(dumper, prg, prgSize); while (prg.Count % 0x4000 != 0) { prg.Add(0); } Console.WriteLine("CHR memory size: {0}K", chrSize / 1024); mapper.DumpChr(dumper, chr, chrSize); while (chr.Count % 0x2000 != 0) { chr.Add(0); } byte[] mirroringRaw = dumper.GetMirroring(); NesFile.MirroringType mirroring = NesFile.MirroringType.Unknown_none; if (mirroringRaw.Length == 1) { mirroring = (NesFile.MirroringType)mirroringRaw[0]; Console.WriteLine("Mirroring: " + mirroring); } else if (mirroringRaw.Length == 4) { switch (string.Format("{0:X2}{1:X2}{2:X2}{3:X2}", mirroringRaw[0], mirroringRaw[1], mirroringRaw[2], mirroringRaw[3])) { case "00000101": mirroring = NesFile.MirroringType.Horizontal; // Horizontal break; case "00010001": mirroring = NesFile.MirroringType.Vertical; // Vertical break; case "00000000": mirroring = NesFile.MirroringType.OneScreenA; // One-screen A break; case "01010101": mirroring = NesFile.MirroringType.OneScreenB; // One-screen B break; default: mirroring = NesFile.MirroringType.Unknown_none; // Unknown break; } Console.WriteLine("Mirroring: {0} ({1:X2} {2:X2} {3:X2} {4:X2})", mirroring, mirroringRaw[0], mirroringRaw[1], mirroringRaw[2], mirroringRaw[3]); } Console.WriteLine("Saving to {0}...", fileName); if (mapper.Number >= 0) { var nesFile = new NesFile(); nesFile.Mapper = (byte)mapper.Number; nesFile.Mirroring = mirroring; nesFile.TvSystem = NesFile.TvSystemType.Ntsc; nesFile.PRG = prg.ToArray(); nesFile.CHR = chr.ToArray(); nesFile.Save(fileName); } else { var unifFile = new UnifFile(); unifFile.Version = 4; unifFile.Mapper = mapper.UnifName; if (unifName != null) { unifFile.Fields["NAME"] = UnifFile.StringToUTF8N(unifName); } unifFile.Fields["PRG0"] = prg.ToArray(); if (chr.Count > 0) { unifFile.Fields["CHR0"] = chr.ToArray(); } unifFile.Fields["MIRR"] = new byte[] { 5 }; // mapper controlled unifFile.Save(fileName, unifAuthor); } }
static int Main(string[] args) { startTime = DateTime.Now; string port = "auto"; string mapper = "0"; string psize = null; string csize = null; string filename = null; string luaCode = null; string luaFile = null; string unifName = null; string unifAuthor = null; bool reset = false; bool silent = true; try { if (args.Length == 0) { PrintHelp(); return(0); } string command = args[0]; for (int i = 1; i < args.Length; i++) { string param = args[i]; while (param.StartsWith("-")) { param = param.Substring(1); } string value = i < args.Length - 1 ? args[i + 1] : ""; switch (param.ToLower()) { case "p": case "port": port = value; i++; break; case "m": case "mapper": mapper = value; i++; break; case "f": case "file": filename = value; i++; break; case "lua": case "script": luaCode = value; i++; break; case "luafile": case "scriptfile": luaFile = value; i++; break; case "psize": psize = value; i++; break; case "csize": csize = value; i++; break; case "unifname": unifName = value; i++; break; case "unifauthor": unifAuthor = value; i++; break; case "reset": reset = true; break; case "silent": silent = true; break; case "sound": silent = false; break; default: Console.WriteLine("Unknown parameter: " + param); PrintHelp(); return(2); } } using (var dumper = new FamicomDumperConnection(port)) { dumper.Open(); Console.Write("PRG reader initialization... "); bool prgInit = dumper.PrgReaderInit(); if (!prgInit) { throw new Exception("can't init PRG reader"); } Console.WriteLine("OK"); Console.Write("CHR reader initialization... "); bool chrInit = dumper.ChrReaderInit(); if (!chrInit) { throw new Exception("can't init CHR reader"); } Console.WriteLine("OK"); if (reset) { Reset(dumper); } LuaMapper luaMapper = null; if (!string.IsNullOrEmpty(luaFile) || !string.IsNullOrEmpty(luaCode) || command.ToLower() == "console") { luaMapper = new LuaMapper(); } if (!string.IsNullOrEmpty(luaFile)) { Console.WriteLine("Executing Lua script \"{0}\"...", Path.GetFileName(luaFile)); luaMapper.Verbose = true; luaMapper.Execute(dumper, luaFile, true); luaMapper.Verbose = false; } if (!string.IsNullOrEmpty(luaCode)) { Console.WriteLine("Executing Lua code: \"{0}\"", luaCode); luaMapper.Verbose = true; luaMapper.Execute(dumper, luaCode, false); luaMapper.Verbose = false; } switch (command.ToLower()) { case "reset": if (!reset) { Reset(dumper); } break; case "list-mappers": ListMappers(); break; case "dump": Dump(dumper, filename ?? "output.nes", mapper, parseSize(psize), parseSize(csize), unifName, unifAuthor); break; case "read-prg-ram": case "dump-prg-ram": case "dump-sram": ReadPrgRam(dumper, filename ?? "savegame.sav", mapper); break; case "write-prg-ram": case "write-sram": WritePrgRam(dumper, filename ?? "savegame.sav", mapper); break; case "test-prg-ram": case "test-sram": TestPrgRam(dumper, mapper); break; case "test-prg-ram-coolgirl": case "test-sram-coolgirl": TestPrgRamCoolgirl(dumper); break; case "test-battery": TestBattery(dumper, mapper); break; case "test-chr-ram": TestChrRam(dumper); break; case "test-chr-coolgirl": TestChrRamCoolgirl(dumper); break; case "test-coolgirl": TestCoolgirlFull(dumper); break; case "dump-tiles": DumpTiles(dumper, filename ?? "output.png", mapper, parseSize(csize)); break; case "write-flash": WriteFlash(dumper, filename ?? "game.nes"); break; case "write-coolboy": WriteCoolboy(dumper, filename ?? "game.nes"); break; case "write-coolgirl": WriteCoolgirl(dumper, filename ?? "game.nes"); break; case "jtag": WriteJtag(dumper, filename ?? "mapper.fmp"); break; case "bootloader": Bootloader(dumper); break; case "console": LuaConsole(dumper, luaMapper); break; case "nop": case "none": case "-": break; default: Console.WriteLine("Unknown command: " + command); PrintHelp(); return(2); } Console.WriteLine("Done in {0} seconds", (int)(DateTime.Now - startTime).TotalSeconds); if (!silent) { doneSound.PlaySync(); } return(0); } } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); if (!silent) { Console.Beep(); } return(1); } }