public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { byte banks = (byte)(size / 0x2000); for (byte bank = 0; bank < banks - 2; bank += 2) { Console.Write("Reading PRG banks #{0} and #{1}... ", bank, bank + 1); dumper.WriteCpu(0x8000, new byte[] { 6, bank }); dumper.WriteCpu(0x8000, new byte[] { 7, (byte)(bank | 1) }); data.AddRange(dumper.ReadCpu(0x8000, 0x4000)); Console.WriteLine("OK"); } Console.Write("Reading last PRG banks #{0} and #{1}... ", banks - 2, banks - 1); data.AddRange(dumper.ReadCpu(0xC000, 0x4000)); Console.WriteLine("OK"); }
public static void PasswordUnlock(FamicomDumperConnection dumper, byte[] password) { try { if (password.Length != 8) { throw new InvalidDataException("Invalid password length"); } Console.Write("Unlocking password... "); // Password Protection Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0x60); // Password unlock dumper.WriteCpu(0x8000, 0x25); dumper.WriteCpu(0x8000, 0x03); for (byte i = 0; i < password.Length; i++) { dumper.WriteCpu((ushort)(0x8000 + i), password[i]); } dumper.WriteCpu(0x8000, 0x29); Console.WriteLine("OK"); } finally { ResetFlash(dumper); } }
public void DumpChr(FamicomDumperConnection dumper, List <byte> data, int size) { byte outbanks = (byte)(size / (256 * 1024)); for (byte outbank = 0; outbank < outbanks; outbank += 1) { dumper.Reset(); dumper.WriteCpu(0xA001, 0x80); // RAM protect dumper.WriteCpu((ushort)(0x6828 | (outbank << 1)), 0x00); dumper.WriteCpu(0xA001, 0); // disable W-RAM int banks = 256; if (banks > 256) { throw new Exception("CHR size is too big"); } for (int bank = 0; bank < banks; bank += 4) { Console.Write("Reading CHR banks #{4}|{0}, #{4}|{1}, #{4}|{2}, #{4}|{3}... ", bank, bank + 1, bank + 2, bank + 3, outbank); dumper.WriteCpu(0x8000, new byte[] { 2, (byte)bank }); dumper.WriteCpu(0x8000, new byte[] { 3, (byte)(bank | 1) }); dumper.WriteCpu(0x8000, new byte[] { 4, (byte)(bank | 2) }); dumper.WriteCpu(0x8000, new byte[] { 5, (byte)(bank | 3) }); data.AddRange(dumper.ReadPpu(0x1000, 0x1000)); Console.WriteLine("OK"); } } }
void WriteMMC1(FamicomDumperConnection dumper, UInt16 address, byte data) { byte[] buffer = new byte[5]; for (int i = 0; i < 5; i++) { buffer[i] = (byte)(data >> i); } dumper.WriteCpu(address, buffer); }
public static CFIInfo GetCFIInfo(FamicomDumperConnection dumper) { try { dumper.WriteCpu(0x8AAA, 0x98); // CFI mode var cfiRaw = dumper.ReadCpu(0x8000, 0x100); if (cfiRaw[0x20] != 0x51 || cfiRaw[0x22] != 0x52 || cfiRaw[0x24] != 0x59) { throw new IOException("Can't enter CFI mode. Invalid flash memory? Broken cartridge? Is it inserted?"); } var cfi = new CFIInfo(cfiRaw, CFIInfo.ParseMode.Every2Bytes); return(cfi); } finally { dumper.WriteCpu(0x8000, 0xF0); } }
public static void PPBLockBitCheckPrint(FamicomDumperConnection dumper) { try { // PPB Lock Command Set Entry dumper.WriteCpu(0x8AAA, 0xAA); dumper.WriteCpu(0x8555, 0x55); dumper.WriteCpu(0x8AAA, 0x50); var ppbLockStatus = dumper.ReadCpu(0x8000); if (ppbLockStatus == 0) { Console.WriteLine("WARNING: PPB Lock Bit is set!"); } } finally { ResetFlash(dumper); } }
public void DumpChr(FamicomDumperConnection dumper, List <byte> data, int size) { int banks = size / 0x400; if (banks > 256) { throw new Exception("CHR size is too big"); } for (int bank = 0; bank < banks; bank += 4) { Console.Write("Reading CHR banks #{0}, #{1}, #{2}, #{3}... ", bank, bank + 1, bank + 2, bank + 3); dumper.WriteCpu(0x8000, new byte[] { 2, (byte)bank }); dumper.WriteCpu(0x8000, new byte[] { 3, (byte)(bank | 1) }); dumper.WriteCpu(0x8000, new byte[] { 4, (byte)(bank | 2) }); dumper.WriteCpu(0x8000, new byte[] { 5, (byte)(bank | 3) }); data.AddRange(dumper.ReadPpu(0x1000, 0x1000)); Console.WriteLine("OK"); } }
public static void TestPrgRam(FamicomDumperConnection dumper, int count = -1) { Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x01); // enable PRG RAM 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 PRG RAM, bank #{bank}/{4}... "); rnd.NextBytes(data[bank]); dumper.WriteCpu(0x5005, bank); dumper.WriteCpu(0x6000, data[bank]); } for (byte bank = 0; bank < 4; bank++) { Console.Write($"Reading PRG RAM, bank #{bank}/{4}... "); 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 {b:X4}: {rdata[b]:X2} != {data[bank][b]:X2}"); ok = false; } } if (!ok) { File.WriteAllBytes("prgramgood.bin", data[bank]); Console.WriteLine("prgramgood.bin writed"); File.WriteAllBytes("prgrambad.bin", rdata); Console.WriteLine("prgrambad.bin writed"); throw new IOException("Test failed"); } Console.WriteLine("OK"); } count--; } }
public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { int prgBanks = size / 0x8000; Console.Write("Reset... "); dumper.Reset(); Console.WriteLine("OK"); dumper.WriteCpu(0x5002, 0xFE); // mask = 8K 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); Console.Write("Reading PRG bank #{0}/{1}... ", bank, prgBanks); data.AddRange(dumper.ReadCpu(0x8000, 0x8000)); Console.WriteLine("OK"); } Console.WriteLine("Done!"); }
public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { dumper.WriteCpu((ushort)(0x5000), (byte)0x00); dumper.WriteCpu((ushort)(0x5001), (byte)0x10); dumper.WriteCpu((ushort)(0x5002), (byte)0x10); dumper.WriteCpu((ushort)(0x5006), (byte)0x07); dumper.WriteCpu((ushort)(0x5007), (byte)0x03); byte banks = (byte)(size / 0x8000); for (int bank = 0; bank < banks; bank++) { Console.Write("Reading PRG bank #{0}... ", bank); // TODO: избежать конфликтов шины // Avoiding bus conflicts /* * for (int i = 0; i < lastBank.Length; i++) * { * if (lastBank[i] == bank) * { * break; * } * } */ dumper.WriteCpu((ushort)(0x8000), (byte)bank); data.AddRange(dumper.ReadCpu(0x8000, 0x8000)); Console.WriteLine("OK"); } }
private static void CheckRAMAdapter(FamicomDumperConnection dumper) { // Just simple test that RAM adapter is connected bool ramAdapterPresent = true; dumper.WriteCpu(0x4023, 0x01); // enable disk registers dumper.WriteCpu(0x4026, 0x00); dumper.WriteCpu(0x4025, 0b00100110); // reset dumper.WriteCpu(0x0000, 0xFF); // to prevent open bus read var ext = dumper.ReadCpu(0x4033); if (ext != 0x00) { ramAdapterPresent = false; } dumper.WriteCpu(0x4026, 0xFF); dumper.WriteCpu(0x0000, 0x00); // to prevent open bus read ext = dumper.ReadCpu(0x4033); if ((ext & 0x7F) != 0x7F) { ramAdapterPresent = false; } if (!ramAdapterPresent) { throw new IOException("RAM adapter IO error, is it connected?"); } }
public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { dumper.Reset(); int outbanks = size / (128 * 1024); int outbankSize = 512; for (int outbank = 0; outbank < outbanks; outbank += outbankSize / 128) { 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 banks = outbankSize * 1024 / 0x2000; for (int bank = 0; bank < banks - 2; bank += 2) { Console.Write("Reading PRG banks #{2}|{0} and #{2}|{1}... ", bank, bank + 1, outbank); dumper.WriteCpu(0x8000, new byte[] { 6, (byte)(bank) }); dumper.WriteCpu(0x8000, new byte[] { 7, (byte)(bank | 1) }); data.AddRange(dumper.ReadCpu(0x8000, 0x4000)); Console.WriteLine("OK"); } Console.Write("Reading last PRG banks #{2}|{0} and #{2}|{1}... ", banks - 2, banks - 1, outbank); data.AddRange(dumper.ReadCpu(0xC000, 0x4000)); Console.WriteLine("OK"); } }
public static void ReadCrc(FamicomDumperConnection dumper) { Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K var cfi = FlashHelper.GetCFIInfo(dumper); Console.WriteLine($"Device size: {cfi.DeviceSize / 1024 / 1024} MByte / {cfi.DeviceSize / 1024 / 1024 * 8} Mbit"); uint banks = cfi.DeviceSize / BANK_SIZE; var readStartTime = DateTime.Now; var lastSectorTime = DateTime.Now; var timeEstimated = new TimeSpan(); ushort crc = 0; for (int bank = 0; bank < banks; bank++) { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); int pos = bank * BANK_SIZE; if (pos % (128 * 1024) == 0) { timeEstimated = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 4); timeEstimated = timeEstimated.Add(DateTime.Now - readStartTime); lastSectorTime = DateTime.Now; } var timePassed = DateTime.Now - readStartTime; Console.Write($"Reading CRC of bank #{bank}/{banks} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... "); var crcr = dumper.ReadCpuCrc(0x8000, BANK_SIZE); Console.WriteLine($"CRC = {crcr:X4}"); crc ^= crcr; } Console.WriteLine($"Total CRC = {crc:X4}"); }
public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { byte outbanks = (byte)(size / (256 * 1024)); for (byte outbank = 0; outbank < outbanks; outbank += 1) { dumper.Reset(); dumper.WriteCpu(0xA001, 0x80); // RAM protect dumper.WriteCpu((ushort)(0x6828 | (outbank << 1)), 0x00); dumper.WriteCpu(0xA001, 0); // disable W-RAM byte banks = 32; //(byte)(size / 0x2000); for (byte bank = 0; bank < banks - 2; bank += 2) { Console.Write("Reading PRG banks #{2}|{0} and #{2}|{1}... ", bank, bank + 1, outbank); dumper.WriteCpu(0x8000, new byte[] { 6, bank }); dumper.WriteCpu(0x8000, new byte[] { 7, (byte)(bank | 1) }); data.AddRange(dumper.ReadCpu(0x8000, 0x4000)); Console.WriteLine("OK"); } Console.Write("Reading last PRG banks #{2}|{0} and #{2}|{1}... ", banks - 2, banks - 1, outbank); data.AddRange(dumper.ReadCpu(0xC000, 0x4000)); Console.WriteLine("OK"); } }
public void DumpChr(FamicomDumperConnection dumper, List <byte> data, int size) { dumper.WriteCpu(0x8000, 0x80); WriteMMC1(dumper, 0x8000, 0x0C); byte banks = (byte)(size / 0x1000); for (int bank = 0; bank < banks; bank += 2) { Console.Write("Reading CHR banks #{0} and #{1}... ", bank, bank + 1); WriteMMC1(dumper, 0xA000, (byte)bank); data.AddRange(dumper.ReadPpu(0x0000, 0x2000)); Console.WriteLine("OK"); } }
static void TestBattery(FamicomDumperConnection dumper, string mapperName) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine($"Using mapper: #{mapper.Number} ({mapper.Name})"); } else { Console.WriteLine($"Using mapper: {mapper.Name}"); } mapper.EnablePrgRam(dumper); var rnd = new Random(); var data = new byte[0x2000]; rnd.NextBytes(data); Console.Write("Writing PRG RAM... "); dumper.WriteCpu(0x6000, data); Reset(dumper); Console.WriteLine("Replug cartridge and press any key"); Console.ReadKey(); Console.WriteLine(); mapper.EnablePrgRam(dumper); Console.Write("Reading PRG RAM... "); 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 {b:X4}: {rdata[b]:X2} != {data[b]:X2}"); ok = false; } } if (ok) { Console.WriteLine("OK!"); } else { throw new VerificationException("Failed!"); } }
public static void PPBClear(FamicomDumperConnection dumper, ushort coolboyReg) { // 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(coolboyReg, r0, r1, r2, r3); FlashHelper.PPBClear(dumper); }
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.Count(), prg.Length - s); Array.Copy(nesFile.PRG.ToArray(), s % nesFile.PRG.Count(), prg, s, n); s += n; } var chr = new byte[0x2000]; s = 0; while (s < chr.Length) { var n = Math.Min(nesFile.CHR.Count(), chr.Length - s); Array.Copy(nesFile.CHR.ToArray(), s % nesFile.CHR.Count(), 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: #{mapper.Number} ({mapper.Name})"); } else { Console.WriteLine($"Using mapper: {mapper.Name}"); } mapper.EnablePrgRam(dumper); var rnd = new Random(); while (count != 0) { var data = new byte[0x2000]; rnd.NextBytes(data); Console.Write("Writing PRG RAM... "); dumper.WriteCpu(0x6000, data); Console.Write("Reading PRG RAM... "); 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 {b:X4}: {rdata[b]:X2} != {data[b]:X2}"); ok = false; } } if (!ok) { File.WriteAllBytes("prgramgood.bin", data); Console.WriteLine("prgramgood.bin writed"); File.WriteAllBytes("prgrambad.bin", rdata); Console.WriteLine("prgrambad.bin writed"); throw new VerificationException("Failed!"); } Console.WriteLine("OK!"); count--; } }
static void WritePrgRam(FamicomDumperConnection dumper, string fileName, string mapperName) { var mapper = GetMapper(mapperName); if (mapper.Number >= 0) { Console.WriteLine($"Using mapper: #{mapper.Number} ({mapper.Name})"); } else { Console.WriteLine($"Using mapper: {mapper.Name}"); } mapper.EnablePrgRam(dumper); Console.Write("Writing PRG RAM... "); var prgram = File.ReadAllBytes(fileName); dumper.WriteCpu(0x6000, prgram); Console.WriteLine("OK"); dumper.ReadCpu(0x0, 1); // to avoid corruption Reset(dumper); }
public void DumpPrg(FamicomDumperConnection dumper, List <byte> data, int size) { dumper.WriteCpu(0x8000, 0x80); WriteMMC1(dumper, 0x8000, 0x0C); byte banks = (byte)(size / 0x4000); for (byte bank = 0; bank < banks - 1; bank++) { Console.Write("Reading PRG bank #{0}... ", bank); WriteMMC1(dumper, 0xE000, bank); data.AddRange(dumper.ReadCpu(0x8000, 0x4000)); Console.WriteLine("OK"); } if (banks > 0) { Console.Write("Reading last PRG bank #{0}... ", banks - 1); data.AddRange(dumper.ReadCpu(0xC000, 0x4000)); Console.WriteLine("OK"); } }
public static byte DetectVersion(FamicomDumperConnection dumper) { byte version; Console.Write("Detecting COOLBOY version... "); // 0th CHR bank using both methods dumper.WriteCpu(0x5000, 0, 0, 0, 0x10); dumper.WriteCpu(0x6000, 0, 0, 0, 0x10); // Writing 0 dumper.WritePpu(0x0000, 0); // First CHR bank using both methods dumper.WriteCpu(0x5000, 0, 0, 1, 0x10); dumper.WriteCpu(0x6000, 0, 0, 1, 0x10); // Writing 1 dumper.WritePpu(0x0000, 1); // 0th bank using first method dumper.WriteCpu(0x6000, 0, 0, 0, 0x10); byte v6000 = dumper.ReadPpu(0x0000); // return dumper.WriteCpu(0x6000, 0, 0, 1, 0x10); // 0th bank using second method dumper.WriteCpu(0x5000, 0, 0, 0, 0x10); byte v5000 = dumper.ReadPpu(0x0000); if (v6000 == 0 && v5000 == 1) { version = 1; } else if (v6000 == 1 && v5000 == 0) { version = 2; } else { throw new IOException("Can't detect COOLBOY version"); } Console.WriteLine($"Version: {version}"); return(version); }
public static void PrintFlashInfo(FamicomDumperConnection dumper) { Program.Reset(dumper); 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, r0, r1, r2, r3); var cfi = FlashHelper.GetCFIInfo(dumper); FlashHelper.PrintCFIInfo(cfi); FlashHelper.LockBitsCheckPrint(dumper); FlashHelper.PPBLockBitCheckPrint(dumper); }
public void DumpChr(FamicomDumperConnection dumper, List <byte> data, int size) { if (prg == null) { prg = dumper.ReadCpu(0x8000, DefaultPrgSize); } byte banks = (byte)(size / 0x2000); for (int bank = 0; bank < banks; bank++) { Console.Write("Dumping CHR bank #{0}... ", bank); for (int i = 0; i < prg.Length; i++) { if (prg[i] == bank) { dumper.WriteCpu((ushort)(0x8000 + i), (byte)bank); break; } } byte[] d = dumper.ReadPpu(0x0000, 0x2000); data.AddRange(d); Console.WriteLine("OK"); } }
public static void FindBads(FamicomDumperConnection dumper, bool silent) { Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K try { PPBClear(dumper); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}. Lets try anyway."); } dumper.WriteCpu(0x5000, 0); dumper.WriteCpu(0x5001, 0); var cfi = FlashHelper.GetCFIInfo(dumper); Console.WriteLine($"Device size: {cfi.DeviceSize / 1024 / 1024} MByte / {cfi.DeviceSize / 1024 / 1024 * 8} Mbit"); uint banks = cfi.DeviceSize / BANK_SIZE; FlashHelper.LockBitsCheckPrint(dumper); Console.Write("Erasing sector #0... "); dumper.EraseCpuFlashSector(); Console.WriteLine("OK"); var data = new byte[BANK_SIZE]; new Random().NextBytes(data); Console.Write("Writing sector #0 for test... "); dumper.WriteCpuFlash(0x0000, data); Console.WriteLine("OK"); Console.Write("Reading sector #0 for test... "); var datar = dumper.ReadCpu(0x8000, BANK_SIZE); for (int i = 0; i < data.Length; i++) { if (data[i] != datar[i]) { throw new VerificationException("Check failed"); } } Console.WriteLine("OK"); var writeStartTime = DateTime.Now; var lastSectorTime = DateTime.Now; var timeEstimated = new TimeSpan(); var badSectors = new List <int>(); for (int bank = 0; bank < banks; bank += 4) { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); timeEstimated = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 4); timeEstimated = timeEstimated.Add(DateTime.Now - writeStartTime); lastSectorTime = DateTime.Now; var timePassed = DateTime.Now - writeStartTime; Console.Write($"Erasing sector #{bank / 4}/{banks / 4} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... "); try { dumper.EraseCpuFlashSector(); Console.WriteLine("OK"); } catch { Console.WriteLine("ERROR!"); if (!silent) { Program.PlayErrorSound(); } Console.Write("Trying again... "); Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); try { dumper.EraseCpuFlashSector(); Console.WriteLine("OK"); } catch { Console.WriteLine($"ERROR! Sector #{bank / 4} is bad."); if (!silent) { Program.PlayErrorSound(); } badSectors.Add(bank / 4); } } } if (badSectors.Count > 0) { foreach (var bad in badSectors) { Console.WriteLine($"Bad sector: {bad}"); } throw new IOException("Bad sectors found"); } else { Console.WriteLine("There is no bad sectors"); } }
public static void Write(FamicomDumperConnection dumper, string fileName, IEnumerable <int> badSectors, bool silent, bool needCheck = false, bool writePBBs = false, bool ignoreBadSectors = false) { byte[] PRG; if (Path.GetExtension(fileName).ToLower() == ".bin") { PRG = File.ReadAllBytes(fileName); } else { try { var nesFile = new NesFile(fileName); PRG = nesFile.PRG.ToArray(); } catch { var nesFile = new UnifFile(fileName); PRG = nesFile.Fields["PRG0"]; } } int banks = PRG.Length / BANK_SIZE; Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K dumper.WriteCpu(0x5000, 0); dumper.WriteCpu(0x5001, 0); FlashHelper.ResetFlash(dumper); var cfi = FlashHelper.GetCFIInfo(dumper); Console.WriteLine($"Device size: {cfi.DeviceSize / 1024 / 1024} MByte / {cfi.DeviceSize / 1024 / 1024 * 8} Mbit"); Console.WriteLine($"Maximum number of bytes in multi-byte program: {cfi.MaximumNumberOfBytesInMultiProgram}"); if (dumper.ProtocolVersion >= 3) { dumper.SetMaximumNumberOfBytesInMultiProgram(cfi.MaximumNumberOfBytesInMultiProgram); } FlashHelper.LockBitsCheckPrint(dumper); if (PRG.Length > cfi.DeviceSize) { throw new ArgumentOutOfRangeException("PRG.Length", "This ROM is too big for this cartridge"); } try { PPBClear(dumper); } catch (Exception ex) { if (!silent) { Program.PlayErrorSound(); } Console.WriteLine($"ERROR! {ex.Message}. Lets continue anyway."); } var writeStartTime = DateTime.Now; var lastSectorTime = DateTime.Now; var timeEstimated = new TimeSpan(); int totalErrorCount = 0; int currentErrorCount = 0; var newBadSectorsList = new List <int>(); for (int bank = 0; bank < banks; bank++) { while (badSectors.Contains(bank / 4) || newBadSectorsList.Contains(bank / 4)) { bank += 4; // bad sector :( } try { byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); var data = new byte[BANK_SIZE]; int pos = bank * BANK_SIZE; if (pos % (128 * 1024) == 0) { timeEstimated = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 4); timeEstimated = timeEstimated.Add(DateTime.Now - writeStartTime); lastSectorTime = DateTime.Now; Console.Write($"Erasing sector #{bank / 4}... "); dumper.EraseCpuFlashSector(); Console.WriteLine("OK"); } Array.Copy(PRG, pos, data, 0, data.Length); var timePassed = DateTime.Now - writeStartTime; Console.Write($"Writing bank #{bank}/{banks} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... "); dumper.WriteCpuFlash(0x0000, data); Console.WriteLine("OK"); if ((bank % 4 == 3) || (bank == banks - 1)) // After last bank in sector { if (writePBBs) { FlashHelper.PPBSet(dumper); } currentErrorCount = 0; } } catch (Exception ex) { totalErrorCount++; currentErrorCount++; Console.WriteLine($"ERROR {ex.GetType()}: {ex.Message}"); if (!silent) { Program.PlayErrorSound(); } if (currentErrorCount >= 5) { if (!ignoreBadSectors) { throw ex; } else { newBadSectorsList.Add(bank / 4); currentErrorCount = 0; Console.WriteLine($"Lets skip sector #{bank / 4}"); } } else { Console.WriteLine("Lets try again"); } bank = (bank & ~3) - 1; Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x04); // enable PRG write dumper.WriteCpu(0x5002, 0xFE); // mask = 32K FlashHelper.ResetFlash(dumper); continue; } } if (totalErrorCount > 0) { Console.WriteLine($"Write error count: {totalErrorCount}"); } if (newBadSectorsList.Any()) { Console.WriteLine($"Can't write sectors: {string.Join(", ", newBadSectorsList.OrderBy(s => s))}"); } var wrongCrcSectorsList = new List <int>(); if (needCheck) { Console.WriteLine("Starting verification process"); Program.Reset(dumper); var readStartTime = DateTime.Now; lastSectorTime = DateTime.Now; timeEstimated = new TimeSpan(); dumper.WriteCpu(0x5002, 0xFE); // mask = 32K for (int bank = 0; bank < banks; bank++) { while (badSectors.Contains(bank / 4)) { bank += 4; // bad sector :( } byte r0 = (byte)(bank >> 7); byte r1 = (byte)(bank << 1); dumper.WriteCpu(0x5000, r0); dumper.WriteCpu(0x5001, r1); var data = new byte[BANK_SIZE]; int pos = bank * BANK_SIZE; if (pos % (128 * 1024) == 0) { timeEstimated = new TimeSpan((DateTime.Now - lastSectorTime).Ticks * (banks - bank) / 4); timeEstimated = timeEstimated.Add(DateTime.Now - readStartTime); lastSectorTime = DateTime.Now; } Array.Copy(PRG, pos, data, 0, data.Length); ushort crc = 0; foreach (var a in data) { crc ^= a; for (int i = 0; i < 8; ++i) { if ((crc & 1) != 0) { crc = (ushort)((crc >> 1) ^ 0xA001); } else { crc = (ushort)(crc >> 1); } } } var timePassed = DateTime.Now - readStartTime; Console.Write($"Reading CRC of bank #{bank}/{banks} ({100 * bank / banks}%, {timePassed.Hours:D2}:{timePassed.Minutes:D2}:{timePassed.Seconds:D2}/{timeEstimated.Hours:D2}:{timeEstimated.Minutes:D2}:{timeEstimated.Seconds:D2})... "); var crcr = dumper.ReadCpuCrc(0x8000, BANK_SIZE); if (crcr != crc) { Console.WriteLine($"Verification failed: {crcr:X4} != {crc:X4}"); if (!silent) { Program.PlayErrorSound(); } wrongCrcSectorsList.Add(bank / 4); } else { Console.WriteLine($"OK (CRC = {crcr:X4})"); } } if (totalErrorCount > 0) { Console.WriteLine($"Write error count: {totalErrorCount}"); } if (newBadSectorsList.Any()) { Console.WriteLine($"Can't write sectors: {string.Join(", ", newBadSectorsList.OrderBy(s => s))}"); } if (wrongCrcSectorsList.Any()) { Console.WriteLine($"Sectors with wrong CRC: {string.Join(", ", wrongCrcSectorsList.Distinct().OrderBy(s => s))}"); } } if (newBadSectorsList.Any() || wrongCrcSectorsList.Any()) { throw new IOException("Cartridge is not writed correctly"); } }
public void EnablePrgRam(FamicomDumperConnection dumper) { dumper.WriteCpu(0xA001, 0x80); }
public void EnablePrgRam(FamicomDumperConnection dumper) { dumper.Reset(); dumper.WriteCpu(0x5007, 0x01); // enable SRAM dumper.WriteCpu(0x5005, 0x02); // select bank }
public static void TestChrRam(FamicomDumperConnection dumper, int count = -1, int chrSize = -1) { if (chrSize < 0) { chrSize = 256 * 1024; } Console.WriteLine($"Testing CHR RAM, size: {chrSize / 1024}KB"); Program.Reset(dumper); dumper.WriteCpu(0x5007, 0x2); // enable CHR writing var rnd = new Random(); var data = new byte[0x2000]; rnd.NextBytes(data); Console.WriteLine("Single bank 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 {b:X4}: {rdata[b]:X2} != {data[b]:X2}"); ok = false; } } if (!ok) { File.WriteAllBytes("chrramgood.bin", data); Console.WriteLine("chrramgood.bin writed"); File.WriteAllBytes("chrrambad.bin", rdata); Console.WriteLine("chrrambad.bin writed"); throw new IOException("Test failed"); } Console.WriteLine("OK"); Console.WriteLine("Multibank test."); data = new byte[chrSize]; for (; count != 0; count--) { dumper.WriteCpu(0x5007, 0x2); // enable CHR writing rnd.NextBytes(data); for (byte bank = 0; bank < data.Length / 0x2000; bank++) { Console.WriteLine($"Writing CHR RAM bank #{bank}/{data.Length / 0x2000}..."); dumper.WriteCpu(0x5003, (byte)(bank & 0b00011111)); // select bank, low 5 bits dumper.WriteCpu(0x5005, (byte)((bank & 0b00100000) << 2)); // select bank, 6th bit var d = new byte[0x2000]; Array.Copy(data, bank * 0x2000, d, 0, 0x2000); dumper.WritePpu(0x0000, d); } for (byte bank = 0; bank < data.Length / 0x2000; bank++) { Console.Write($"Reading CHR RAM bank #{bank}/{data.Length / 0x2000}... "); dumper.WriteCpu(0x5003, (byte)(bank & 0b00011111)); // select bank, low 5 bits dumper.WriteCpu(0x5005, (byte)((bank & 0b00100000) << 2)); // select bank, 6th bit 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 {b:X4}: {rdata[b]:X2} != {data[b + bank * 0x2000]:X2}"); ok = false; } } if (!ok) { File.WriteAllBytes("chrramgoodf.bin", data); Console.WriteLine("chrramgoodf.bin writed"); File.WriteAllBytes("chrrambad.bin", rdata); Console.WriteLine("chrrambad.bin writed"); throw new IOException("Test failed"); } Console.WriteLine("OK"); } } }
public LuaMapper() { script = new Script(); script.Globals["ReadPrg"] = script.Globals["ReadCpu"] = (Func <UInt16, int, List <byte> >) delegate(UInt16 address, int length) { if (Verbose) { Console.WriteLine("Reading {0} bytes from CPU:${1:X4}", length, address); } var result = new List <byte>(); result.AddRange(dumper.ReadCpu(address, length)); return(result); }; script.Globals["WritePrg"] = script.Globals["WriteCpu"] = (Action <UInt16, List <byte> >) delegate(UInt16 address, List <byte> data) { if (Verbose) { var a = address; foreach (var v in data) { Console.WriteLine("CPU write ${0:X2} => ${1:X4}", v, a); a++; } } dumper.WriteCpu(address, data.ToArray()); }; script.Globals["AddPrg"] = script.Globals["AddPrgResult"] = (Action <List <byte> >) delegate(List <byte> r) { resultPrg.AddRange(r); }; script.Globals["ReadAddPrg"] = script.Globals["ReadAddCpu"] = (Action <UInt16, int>) delegate(UInt16 address, int length) { if (Verbose) { Console.WriteLine("Reading {0} bytes from CPU:${1:X4}", length, address); } resultPrg.AddRange(dumper.ReadCpu(address, length)); }; script.Globals["ReadChr"] = script.Globals["ReadPpu"] = (Func <UInt16, int, List <byte> >) delegate(UInt16 address, int length) { if (Verbose) { Console.WriteLine("Reading {0} bytes from PPU:${1:X4}", length, address); } var result = new List <byte>(); result.AddRange(dumper.ReadPpu(address, length)); return(result); }; script.Globals["WriteChr"] = script.Globals["WritePpu"] = (Action <UInt16, List <byte> >) delegate(UInt16 address, List <byte> data) { if (Verbose) { var a = address; foreach (var v in data) { Console.WriteLine("PPU write ${0:X2} => ${1:X4}", v, a); a++; } } dumper.WritePpu(address, data.ToArray()); }; script.Globals["ReadAddChr"] = script.Globals["ReadAddPpu"] = (Action <UInt16, int>) delegate(UInt16 address, int length) { if (Verbose) { Console.WriteLine("Reading {0} bytes from PPU:${1:$X4}", length, address); } resultChr.AddRange(dumper.ReadPpu(address, length)); }; script.Globals["AddChr"] = script.Globals["AddChrResult"] = (Action <List <byte> >) delegate(List <byte> r) { resultChr.AddRange(r); }; script.Globals["Reset"] = (Action) delegate { if (Verbose) { Console.Write("Reset... "); } dumper.Reset(); if (Verbose) { Console.WriteLine("OK"); } }; script.Globals["WriteFile"] = (Action <string, List <byte> >) delegate(string filename, List <byte> data) { if (Verbose) { Console.Write("Writing data to \"{0}\"... ", Path.GetFileName(filename)); } File.WriteAllBytes(filename, data.ToArray()); if (Verbose) { Console.WriteLine("OK"); } }; script.Globals["WriteNes"] = (WriteNesDelegate) delegate(string filename, List <byte> prgData, List <byte> chrData, byte mapper, bool vertical) { if (Verbose) { Console.Write("Writing data to NES file \"{0}\" (mapper={1}, mirroring={2})... ", Path.GetFileName(filename), mapper, vertical ? "vertical" : "horizontal"); } var nesFile = new NesFile(); nesFile.PRG = prgData.ToArray(); nesFile.CHR = chrData.ToArray(); nesFile.Mapper = 0; nesFile.Mirroring = vertical ? NesFile.MirroringType.Vertical : NesFile.MirroringType.Horizontal; nesFile.Save(filename); if (Verbose) { Console.WriteLine("OK"); } }; script.Globals["Error"] = (Action <string>) delegate(string message) { throw new Exception(message); }; }