public override void write(ushort address, byte val) { if (address < 0x6000) { // A couple of games have tried to write here. It's undefined behavior as far as I know. return; } // First handle RAM. Everything else uses the shift register if (address < 0x8000) { int offset = address - 0x6000; PRG_RAM[offset] = val; return; } // Writing with bit 7 set, this write just resets the shift register if ((val & 0x80) != 0) { shift_register = 0x00; ControlRegister |= 0x0C; shift_register_write_counter = 0; updateOffsets(); return; } // We're shifting in a bit to the shift register.. shift_register >>= 1; shift_register |= (byte)((val & 1) << 4); shift_register_write_counter++; // Should we do an actual write? if (shift_register_write_counter == 5) { if (address < 0xA000) { ControlRegister = shift_register; NametableMirroringMode oldMode = cartridge.NametableMirroring; switch (ControlRegister & 3) { case 0: cartridge.NametableMirroring = NametableMirroringMode.OneScreenLowBank; break; case 1: cartridge.NametableMirroring = NametableMirroringMode.OneScreenHighBank; break; case 2: cartridge.NametableMirroring = NametableMirroringMode.Vertical; break; case 3: cartridge.NametableMirroring = NametableMirroringMode.Horizontal; break; } if (oldMode != cartridge.NametableMirroring) { Console.WriteLine("Switched to {0}", cartridge.NametableMirroring.ToString()); } } else if (address < 0xC000) { CHR0Select = shift_register; } else if (address < 0xE000) { CHR1Select = shift_register; } else { Console.WriteLine("Switched to PRG {0}", shift_register & 0x0F); PRGSelect = shift_register; } // Update offsets updateOffsets(); shift_register = 0x00; shift_register_write_counter = 0; } }
/// <summary> /// Load a rom file using the iNES file format, the de facto NES rom file format. /// http://wiki.nesdev.com/w/index.php/INES /// </summary> /// <param name="romPath"></param> private void loadRomData(string romPath) { byte[] fullRomData = File.ReadAllBytes(romPath); byte[] header = new ArraySegment <byte>(fullRomData, 0, 16).ToArray(); // First confirm this is actually an iNES rom... uint fileSignature = BitConverter.ToUInt32(header, 0); if (fileSignature != INES_FILE_SIGNATURE) { log.error("Did not find expected iNES Rom File Signature"); throw new InvalidDataException(); } PRGROM_16KBankCount = header[4]; PRGRAM_8KBankCount = header[8]; CHRROM_8KBankCount = header[5]; // iNES has a number of flags all rolled up into 4 flag bytes. int flag6 = header[6]; int flag7 = header[7]; int flag9 = header[9]; int flag10 = header[10]; NametableMirroring = (flag6 & 1) == 1 ? NametableMirroringMode.Vertical : NametableMirroringMode.Horizontal; bool usesTrainer = (flag6 & 4) != 0; if (usesTrainer) { log.error("ROM uses a trainer. This is unsupported."); throw new NotImplementedException(); } isNTSC = (flag10 & 1) == 0; if (!isNTSC) { log.error("ROM is PAL. This is unsupported..."); throw new NotImplementedException(); } if ((flag6 & 2) == 1) { BatteryBackedRAM = true; } MapperNumber = flag6 >> 4 | (flag7 & 0xf0); // Now that we know all the 'metadata' about the file, load the actual data. int prgStart = 16 + (usesTrainer ? 512 : 0); int prgBytes = 0x4000 * PRGROM_16KBankCount; PRGRomData = new ArraySegment <byte>(fullRomData, prgStart, prgBytes).ToArray(); int chrStart = prgStart + prgBytes; int chrBytes = 0x2000 * CHRROM_8KBankCount; CHRRomData = new ArraySegment <byte>(fullRomData, chrStart, chrBytes).ToArray(); log.info("ROM Details --"); log.info(" * Mapper #{0}", MapperNumber); log.info(" * Nametable mirroring mode is {0}.", NametableMirroring.ToString()); log.info(" * {0} Output", isNTSC ? "NTSC" : "PAL"); if (BatteryBackedRAM) { log.info(" * Battery-backed RAM ('Game Saves' supported)"); } log.info(" * ROM Bank Data"); log.info(" - {0}x 16 KB PRG ROM", PRGROM_16KBankCount); log.info(" - {0}x 8 KB PRG RAM", PRGRAM_8KBankCount); log.info(" - {0}x 8 KB CHR ROM", CHRROM_8KBankCount); log.info("Finished loading '{0}'.", romPath); }