internal void ResetControllerDefinition(bool subframe) { ControllerDefinition = null; ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); ControllerDefinition = ControllerDeck.ControllerDef; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (Board is FDS b) { ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) { ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } if (_isVS) { ControllerDefinition.BoolButtons.Add("Insert Coin P1"); ControllerDefinition.BoolButtons.Add("Insert Coin P2"); ControllerDefinition.BoolButtons.Add("Service Switch"); } // Add in the reset timing axis for subneshawk if (subframe) { ControllerDefinition.AddAxis("Reset Cycle", 0.RangeTo(500000), 0); } ControllerDefinition.MakeImmutable(); }
public static IControllerDeck AddSystemToControllerDef(this IControllerDeck deck) { var def = deck.ControllerDef; //TODO FDS def.BoolButtons.Add("Reset"); def.BoolButtons.Add("Power"); def.MakeImmutable(); return(deck); }
public void HardReset() { cpu = new MOS6502X(); cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory); cpu.BCD_Enabled = false; cpu.OnExecFetch = ExecFetch; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; // wire controllers // todo: allow changing this ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); // set controller definition first time only if (ControllerDefinition == null) { ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition()); ControllerDefinition.Name = "NES Controller"; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (Board is FDS) { var b = Board as FDS; ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) { ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } if (_isVS) { ControllerDefinition.BoolButtons.Add("Insert Coin P1"); ControllerDefinition.BoolButtons.Add("Insert Coin P2"); ControllerDefinition.BoolButtons.Add("Service Switch"); } } // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); // set up region switch (_display_type) { case Common.DisplayType.PAL: apu = new APU(this, apu, true); ppu.region = PPU.Region.PAL; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1662607; cpu_sequence = cpu_sequence_PAL; _display_type = DisplayType.PAL; break; case Common.DisplayType.NTSC: apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; CoreComm.VsyncNum = 39375000; CoreComm.VsyncDen = 655171; cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; break; // this is in bootgod, but not used at all case Common.DisplayType.DENDY: apu = new APU(this, apu, false); ppu.region = PPU.Region.Dendy; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1773448; cpu_sequence = cpu_sequence_NTSC; _display_type = DisplayType.DENDY; break; default: throw new Exception("Unknown displaytype!"); } if (magicSoundProvider == null) { magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate); } BoardSystemHardReset(); // apu has some specific power up bahaviour that we will emulate here apu.NESHardReset(); if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any()) { for (int i = 0; i < 0x800; i++) { ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count]; } } else { // check fceux's PowerNES and FCEU_MemoryRand function for more information: // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack for (int i = 0; i < 0x800; i++) { if ((i & 4) != 0) { ram[i] = 0xFF; } else { ram[i] = 0x00; } } } SetupMemoryDomains(); // some boards cannot have specific values in RAM upon initialization // Let's hard code those cases here // these will be defined through the gameDB exclusively for now. if (cart.DB_GameInfo != null) { if (cart.DB_GameInfo.Hash == "60FC5FA5B5ACCAF3AEFEBA73FC8BFFD3C4DAE558" || // Camerica Golden 5 cart.DB_GameInfo.Hash == "BAD382331C30B22A908DA4BFF2759C25113CC26A" || // Camerica Golden 5 cart.DB_GameInfo.Hash == "40409FEC8249EFDB772E6FFB2DCD41860C6CCA23" // Camerica Pegasus 4-in-1 ) { ram[0x701] = 0xFF; } } }
protected override void RunImport() { var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName; Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName; using var r = new BinaryReader(SourceFile.Open(FileMode.Open, FileAccess.Read)); var signature = new string(r.ReadChars(4)); if (signature != "FCM\x1A") { Result.Errors.Add("This is not a valid .FCM file."); return; } Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NES"; var syncSettings = new NES.NESSyncSettings(); var controllerSettings = new NESControlSettings { NesLeftPort = nameof(ControllerNES), NesRightPort = nameof(ControllerNES) }; _deck = controllerSettings.Instantiate((x, y) => true); AddDeckControlButtons(); // 004 4-byte little-endian unsigned int: version number, must be 2 uint version = r.ReadUInt32(); if (version != 2) { Result.Errors.Add(".FCM movie version must always be 2."); return; } Result.Movie.Comments.Add($"{MovieOrigin} .FCM version {version}"); // 008 1-byte flags byte flags = r.ReadByte(); /* * bit 0: reserved, set to 0 * bit 1: * if "0", movie begins from an embedded "quicksave" snapshot * if "1", movie begins from reset or power-on[1] */ if (((flags >> 1) & 0x1) == 0) { Result.Errors.Add("Movies that begin with a savestate are not supported."); return; } /* * bit 2: * if "0", NTSC timing * if "1", "PAL" timing * Starting with version 0.98.12 released on September 19, 2004, a "PAL" flag was added to the header but * unfortunately it is not reliable - the emulator does not take the "PAL" setting from the ROM, but from a user * preference. This means that this site cannot calculate movie lengths reliably. */ bool pal = ((flags >> 2) & 0x1) != 0; Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString(); // other: reserved, set to 0 bool syncHack = ((flags >> 4) & 0x1) != 0; Result.Movie.Comments.Add($"SyncHack {syncHack}"); // 009 1-byte flags: reserved, set to 0 r.ReadByte(); // 00A 1-byte flags: reserved, set to 0 r.ReadByte(); // 00B 1-byte flags: reserved, set to 0 r.ReadByte(); // 00C 4-byte little-endian unsigned int: number of frames uint frameCount = r.ReadUInt32(); // 010 4-byte little-endian unsigned int: rerecord count uint rerecordCount = r.ReadUInt32(); Result.Movie.Rerecords = rerecordCount; /* * 018 4-byte little-endian unsigned int: offset to the savestate inside file * The savestate offset is <header_size + length_of_metadata_in_bytes + padding>. The savestate offset should be * 4-byte aligned. At the savestate offset there is a savestate file. The savestate exists even if the movie is * reset-based. */ r.ReadUInt32(); // 01C 4-byte little-endian unsigned int: offset to the controller data inside file uint firstFrameOffset = r.ReadUInt32(); // 020 16-byte md5sum of the ROM used byte[] md5 = r.ReadBytes(16); Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower(); // 030 4-byte little-endian unsigned int: version of the emulator used uint emuVersion = r.ReadUInt32(); Result.Movie.Comments.Add($"{EmulationOrigin} FCEU {emuVersion}"); // 034 name of the ROM used - UTF8 encoded nul-terminated string. var gameBytes = new List <byte>(); while (r.PeekChar() != 0) { gameBytes.Add(r.ReadByte()); } // Advance past null byte. r.ReadByte(); string gameName = Encoding.UTF8.GetString(gameBytes.ToArray()); Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName; /* * After the header comes "metadata", which is UTF8-coded movie title string. The metadata begins after the ROM * name and ends at the savestate offset. This string is displayed as "Author Info" in the Windows version of the * emulator. */ var authorBytes = new List <byte>(); while (r.PeekChar() != 0) { authorBytes.Add(r.ReadByte()); } // Advance past null byte. r.ReadByte(); string author = Encoding.UTF8.GetString(authorBytes.ToArray()); Result.Movie.HeaderEntries[HeaderKeys.Author] = author; // Advance to first byte of input data. r.BaseStream.Position = firstFrameOffset; var controllers = new SimpleController { Definition = _deck.GetDefinition() }; string[] buttons = { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right" }; bool fds = false; int frame = 1; while (frame <= frameCount) { byte update = r.ReadByte(); // aa: Number of delta bytes to follow int delta = (update >> 5) & 0x3; int frames = 0; /* * The delta byte(s) indicate the number of emulator frames between this update and the next update. It is * encoded in little-endian format and its size depends on the magnitude of the delta: * Delta of: Number of bytes: * 0 0 * 1-255 1 * 256-65535 2 * 65536-(2^24-1) 3 */ for (int b = 0; b < delta; b++) { frames += r.ReadByte() * (int)Math.Pow(2, b * 8); } frame += frames; while (frames > 0) { Result.Movie.AppendFrame(controllers); if (controllers["Reset"]) { controllers["Reset"] = false; } frames--; } if (((update >> 7) & 0x1) != 0) { // Control update: 0x1aabbbbb bool reset = false; int command = update & 0x1F; // 0xbbbbb: controllers["Reset"] = command == 1; switch (command) { case 0: // Do nothing break; case 1: // Reset reset = true; break; case 2: // Power cycle reset = true; if (frame != 1) { controllers["Power"] = true; } break; case 7: // VS System Insert Coin Result.Warnings.Add($"Unsupported command: VS System Insert Coin at frame {frame}"); break; case 8: // VS System Dipswitch 0 Toggle Result.Warnings.Add($"Unsupported command: VS System Dipswitch 0 Toggle at frame {frame}"); break; case 24: // FDS Insert fds = true; Result.Warnings.Add($"Unsupported command: FDS Insert at frame {frame}"); break; case 25: // FDS Eject fds = true; Result.Warnings.Add($"Unsupported command: FDS Eject at frame {frame}"); break; case 26: // FDS Select Side fds = true; Result.Warnings.Add($"Unsupported command: FDS Select Side at frame {frame}"); break; default: Result.Warnings.Add($"Unknown command: {command} detected at frame {frame}"); break; } /* * 1 Even if the header says "movie begins from reset", the file still contains a quicksave, and the * quicksave is actually loaded. This flag can't therefore be trusted. To check if the movie actually * begins from reset, one must analyze the controller data and see if the first non-idle command in the * file is a Reset or Power Cycle type control command. */ if (!reset && frame == 1) { Result.Errors.Add("Movies that begin with a savestate are not supported."); return; } } else { /* * Controller update: 0aabbccc * bb: Gamepad number minus one (?) */ int player = ((update >> 3) & 0x3) + 1; if (player > 2) { Result.Errors.Add("Four score not yet supported."); return; } /* * ccc: * 0 A * 1 B * 2 Select * 3 Start * 4 Up * 5 Down * 6 Left * 7 Right */ int button = update & 0x7; /* * The controller update toggles the affected input. Controller update data is emitted to the movie file * only when the state of the controller changes. */ controllers[$"P{player} {buttons[button]}"] = !controllers[$"P{player} {buttons[button]}"]; } Result.Movie.AppendFrame(controllers); } if (fds) { Result.Movie.HeaderEntries[HeaderKeys.BoardName] = "FDS"; } syncSettings.Controls = controllerSettings; Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); }
public void HardReset() { cpu = new MOS6502X <CpuLink>(new CpuLink(this)) { BCD_Enabled = false }; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; // wire controllers // todo: allow changing this ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); // set controller definition first time only if (ControllerDefinition == null) { ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition()); ControllerDefinition.Name = "NES Controller"; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (Board is FDS) { var b = Board as FDS; ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) { ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } if (_isVS) { ControllerDefinition.BoolButtons.Add("Insert Coin P1"); ControllerDefinition.BoolButtons.Add("Insert Coin P2"); ControllerDefinition.BoolButtons.Add("Service Switch"); } } // Add in the reset timing float control for subneshawk if (using_reset_timing && (ControllerDefinition.FloatControls.Count() == 0)) { ControllerDefinition.FloatControls.Add("Reset Cycle"); ControllerDefinition.FloatRanges.Add(new ControllerDefinition.FloatRange(0, 0, 500000)); } // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); // set up region switch (_display_type) { case Common.DisplayType.PAL: apu = new APU(this, apu, true); ppu.region = PPU.Region.PAL; VsyncNum = 50; VsyncDen = 1; cpuclockrate = 1662607; cpu_sequence = cpu_sequence_PAL; _display_type = DisplayType.PAL; ClockRate = 5320342.5; break; case Common.DisplayType.NTSC: apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; VsyncNum = 39375000; VsyncDen = 655171; cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; ClockRate = 5369318.1818181818181818181818182; break; // this is in bootgod, but not used at all case Common.DisplayType.Dendy: apu = new APU(this, apu, false); ppu.region = PPU.Region.Dendy; VsyncNum = 50; VsyncDen = 1; cpuclockrate = 1773448; cpu_sequence = cpu_sequence_NTSC; _display_type = DisplayType.Dendy; ClockRate = 5320342.5; break; default: throw new Exception("Unknown displaytype!"); } blip.SetRates((uint)cpuclockrate, 44100); BoardSystemHardReset(); // apu has some specific power up bahaviour that we will emulate here apu.NESHardReset(); if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any()) { for (int i = 0; i < 0x800; i++) { ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count]; } } else { // check fceux's PowerNES and FCEU_MemoryRand function for more information: // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack for (int i = 0; i < 0x800; i++) { if ((i & 4) != 0) { ram[i] = 0xFF; } else { ram[i] = 0x00; } } } SetupMemoryDomains(); // some boards cannot have specific values in RAM upon initialization // Let's hard code those cases here // these will be defined through the gameDB exclusively for now. if (cart.DB_GameInfo != null) { if (cart.DB_GameInfo.Hash == "60FC5FA5B5ACCAF3AEFEBA73FC8BFFD3C4DAE558" || // Camerica Golden 5 cart.DB_GameInfo.Hash == "BAD382331C30B22A908DA4BFF2759C25113CC26A" || // Camerica Golden 5 cart.DB_GameInfo.Hash == "40409FEC8249EFDB772E6FFB2DCD41860C6CCA23" // Camerica Pegasus 4-in-1 ) { ram[0x701] = 0xFF; } if (cart.DB_GameInfo.Hash == "68ABE1E49C9E9CCEA978A48232432C252E5912C0") // Dancing Blocks { ram[0xEC] = 0; ram[0xED] = 0; } if (cart.DB_GameInfo.Hash == "00C50062A2DECE99580063777590F26A253AAB6B") // Silva Saga { for (int i = 0; i < Board.WRAM.Length; i++) { Board.WRAM[i] = 0xFF; } } } }
public void HardReset() { cpu = new MOS6502X(); cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory); cpu.BCD_Enabled = false; cpu.OnExecFetch = ExecFetch; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; // wire controllers // todo: allow changing this ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); // set controller definition first time only if (ControllerDefinition == null) { ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition()); ControllerDefinition.Name = "NES Controller"; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (Board is FDS) { var b = Board as FDS; ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); // set up region switch (_display_type) { case Common.DisplayType.PAL: apu = new APU(this, apu, true); ppu.region = PPU.Region.PAL; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1662607; cpu_sequence = cpu_sequence_PAL; _display_type = DisplayType.PAL; break; case Common.DisplayType.NTSC: apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; CoreComm.VsyncNum = 39375000; CoreComm.VsyncDen = 655171; cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; break; // this is in bootgod, but not used at all case Common.DisplayType.DENDY: apu = new APU(this, apu, false); ppu.region = PPU.Region.Dendy; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1773448; cpu_sequence = cpu_sequence_NTSC; _display_type = DisplayType.DENDY; break; default: throw new Exception("Unknown displaytype!"); } if (magicSoundProvider == null) magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate); BoardSystemHardReset(); //check fceux's PowerNES and FCEU_MemoryRand function for more information: //relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack for(int i=0;i<0x800;i++) if((i&4)!=0) ram[i] = 0xFF; else ram[i] = 0x00; SetupMemoryDomains(); //in this emulator, reset takes place instantaneously cpu.PC = (ushort)(ReadMemory(0xFFFC) | (ReadMemory(0xFFFD) << 8)); cpu.P = 0x34; cpu.S = 0xFD; }
protected override void RunImport() { using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read); using var r = new BinaryReader(fs); // 000 4-byte signature: 46 4D 56 1A "FMV\x1A" var signature = new string(r.ReadChars(4)); if (signature != "FMV\x1A") { Result.Errors.Add("This is not a valid .FMV file."); return; } // 004 1-byte flags: byte flags = r.ReadByte(); // bit 7: 0=reset-based, 1=savestate-based if (((flags >> 2) & 0x1) != 0) { Result.Errors.Add("Movies that begin with a savestate are not supported."); return; } Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "NES"; var syncSettings = new NES.NESSyncSettings(); // other bits: unknown, set to 0 // 005 1-byte flags: flags = r.ReadByte(); // bit 5: is a FDS recording bool fds; if (((flags >> 5) & 0x1) != 0) { fds = true; Result.Movie.HeaderEntries[HeaderKeys.BOARDNAME] = "FDS"; } else { fds = false; } // bit 6: uses controller 2 bool controller2 = ((flags >> 6) & 0x1) != 0; // bit 7: uses controller 1 bool controller1 = ((flags >> 7) & 0x1) != 0; // other bits: unknown, set to 0 // 006 4-byte little-endian unsigned int: unknown, set to 00000000 r.ReadInt32(); // 00A 4-byte little-endian unsigned int: rerecord count minus 1 uint rerecordCount = r.ReadUInt32(); /* * The rerecord count stored in the file is the number of times a savestate was loaded. If a savestate was never * loaded, the number is 0. Famtasia however displays "1" in such case. It always adds 1 to the number found in * the file. */ Result.Movie.Rerecords = rerecordCount + 1; // 00E 2-byte little-endian unsigned int: unknown, set to 0000 r.ReadInt16(); // 010 64-byte zero-terminated emulator identifier string string emuVersion = NullTerminated(new string(r.ReadChars(64))); Result.Movie.Comments.Add($"{EmulationOrigin} Famtasia version {emuVersion}"); Result.Movie.Comments.Add($"{MovieOrigin} .FMV"); // 050 64-byte zero-terminated movie title string string description = NullTerminated(new string(r.ReadChars(64))); Result.Movie.Comments.Add(description); if (!controller1 && !controller2 && !fds) { Result.Warnings.Add("No input recorded."); } var controllerSettings = new NESControlSettings { NesLeftPort = controller1 ? nameof(ControllerNES) : nameof(UnpluggedNES), NesRightPort = controller2 ? nameof(ControllerNES) : nameof(UnpluggedNES) }; _deck = controllerSettings.Instantiate((x, y) => true); syncSettings.Controls.NesLeftPort = controllerSettings.NesLeftPort; syncSettings.Controls.NesRightPort = controllerSettings.NesRightPort; AddDeckControlButtons(); var controllers = new SimpleController { Definition = _deck.GetDefinition() }; /* * 01 Right * 02 Left * 04 Up * 08 Down * 10 B * 20 A * 40 Select * 80 Start */ string[] buttons = { "Right", "Left", "Up", "Down", "B", "A", "Select", "Start" }; bool[] masks = { controller1, controller2, fds }; /* * The file has no terminator byte or frame count. The number of frames is the <filesize minus 144> divided by * <number of bytes per frame>. */ int bytesPerFrame = 0; for (int player = 1; player <= masks.Length; player++) { if (masks[player - 1]) { bytesPerFrame++; } } long frameCount = (fs.Length - 144) / bytesPerFrame; for (long frame = 1; frame <= frameCount; frame++) { /* * Each frame consists of 1 or more bytes. Controller 1 takes 1 byte, controller 2 takes 1 byte, and the FDS * data takes 1 byte. If all three exist, the frame is 3 bytes. For example, if the movie is a regular NES game * with only controller 1 data, a frame is 1 byte. */ for (int player = 1; player <= masks.Length; player++) { if (!masks[player - 1]) { continue; } byte controllerState = r.ReadByte(); if (player != 3) { for (int button = 0; button < buttons.Length; button++) { controllers[$"P{player} {buttons[button]}"] = ((controllerState >> button) & 0x1) != 0; } } else { Result.Warnings.Add("FDS commands are not properly supported."); } } Result.Movie.AppendFrame(controllers); } Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); }
public void HardReset() { cpu = new MOS6502X(); cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory); cpu.BCD_Enabled = false; cpu.OnExecFetch = ExecFetch; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; // wire controllers // todo: allow changing this ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); // set controller definition first time only if (ControllerDefinition == null) { ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition()); ControllerDefinition.Name = "NES Controller"; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (board is FDS) { var b = board as FDS; ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) { ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } } // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); // set up region switch (_display_type) { case Common.DisplayType.PAL: apu = new APU(this, apu, true); ppu.region = PPU.Region.PAL; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1662607; cpu_sequence = cpu_sequence_PAL; _display_type = DisplayType.PAL; break; case Common.DisplayType.NTSC: apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; CoreComm.VsyncNum = 39375000; CoreComm.VsyncDen = 655171; cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; break; // this is in bootgod, but not used at all case Common.DisplayType.DENDY: apu = new APU(this, apu, false); ppu.region = PPU.Region.Dendy; CoreComm.VsyncNum = 50; CoreComm.VsyncDen = 1; cpuclockrate = 1773448; cpu_sequence = cpu_sequence_NTSC; _display_type = DisplayType.DENDY; break; default: throw new Exception("Unknown displaytype!"); } if (magicSoundProvider == null) { magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate); } BoardSystemHardReset(); //check fceux's PowerNES and FCEU_MemoryRand function for more information: //relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack for (int i = 0; i < 0x800; i++) { if ((i & 4) != 0) { ram[i] = 0xFF; } else { ram[i] = 0x00; } } SetupMemoryDomains(); //in this emulator, reset takes place instantaneously cpu.PC = (ushort)(ReadMemory(0xFFFC) | (ReadMemory(0xFFFD) << 8)); cpu.P = 0x34; cpu.S = 0xFD; }
public void HardReset() { cpu = new MOS6502X <CpuLink>(new CpuLink(this)) { BCD_Enabled = false }; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; // wire controllers // todo: allow changing this ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback); // set controller definition first time only if (ControllerDefinition == null) { ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition()) { Name = "NES Controller" }; // controls other than the deck ControllerDefinition.BoolButtons.Add("Power"); ControllerDefinition.BoolButtons.Add("Reset"); if (Board is FDS b) { ControllerDefinition.BoolButtons.Add("FDS Eject"); for (int i = 0; i < b.NumSides; i++) { ControllerDefinition.BoolButtons.Add("FDS Insert " + i); } } if (_isVS) { ControllerDefinition.BoolButtons.Add("Insert Coin P1"); ControllerDefinition.BoolButtons.Add("Insert Coin P2"); ControllerDefinition.BoolButtons.Add("Service Switch"); } } // Add in the reset timing axis for subneshawk if (using_reset_timing && ControllerDefinition.Axes.Count == 0) { ControllerDefinition.AddAxis("Reset Cycle", 0.RangeTo(500000), 0); } // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); // set up region switch (_display_type) { case DisplayType.PAL: apu = new APU(this, apu, true); ppu.region = PPU.Region.PAL; cpuclockrate = 1662607; VsyncNum = cpuclockrate * 2; VsyncDen = 66495; cpu_sequence = cpu_sequence_PAL; _display_type = DisplayType.PAL; ClockRate = 5320342.5; break; case DisplayType.NTSC: apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; cpuclockrate = 1789773; VsyncNum = cpuclockrate * 2; VsyncDen = 59561; cpu_sequence = cpu_sequence_NTSC; ClockRate = 5369318.1818181818181818181818182; break; // this is in bootgod, but not used at all case DisplayType.Dendy: apu = new APU(this, apu, false); ppu.region = PPU.Region.Dendy; cpuclockrate = 1773448; VsyncNum = cpuclockrate; VsyncDen = 35464; cpu_sequence = cpu_sequence_NTSC; _display_type = DisplayType.Dendy; ClockRate = 5320342.5; break; default: throw new Exception("Unknown displaytype!"); } blip.SetRates((uint)cpuclockrate, 44100); BoardSystemHardReset(); // apu has some specific power up bahaviour that we will emulate here apu.NESHardReset(); if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any()) { for (int i = 0; i < 0x800; i++) { ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count]; } } else { // check fceux's PowerNES and FCEU_MemoryRand function for more information: // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack for (int i = 0; i < 0x800; i++) { if ((i & 4) != 0) { ram[i] = 0xFF; } else { ram[i] = 0x00; } } } SetupMemoryDomains(); // some boards cannot have specific values in RAM upon initialization // Let's hard code those cases here // these will be defined through the gameDB exclusively for now. if (cart.GameInfo != null) { if (cart.GameInfo.Hash is RomChecksums.CamericaGolden5 or RomChecksums.CamericaGolden5Overdump or RomChecksums.CamericaPegasus4in1) { ram[0x701] = 0xFF; } else if (cart.GameInfo.Hash == RomChecksums.DancingBlocks) { ram[0xEC] = 0; ram[0xED] = 0; } else if (cart.GameInfo.Hash == RomChecksums.SilvaSaga) { for (int i = 0; i < Board.Wram.Length; i++) { Board.Wram[i] = 0xFF; } } }