diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 11e551e347..2f96d23e1a 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1216,7 +1216,7 @@ namespace BizHawk.Client.EmuHawk MovieSettingsMenuItem.Enabled = Global.Emulator is NES; - NesControllerSettingsMenuItem.Enabled = Global.Emulator is NES && !Global.MovieSession.Movie.IsActive; + NesControllerSettingsMenuItem.Enabled = (Global.Emulator is NES || Global.Emulator is QuickNES) && !Global.MovieSession.Movie.IsActive; MovieSettingsMenuItem.Enabled = Global.Emulator is NES && !Global.MovieSession.Movie.IsActive; barcodeReaderToolStripMenuItem.Enabled = BarcodeEntry.HasReader(); @@ -1285,7 +1285,10 @@ namespace BizHawk.Client.EmuHawk private void NesControllerSettingsMenuItem_Click(object sender, EventArgs e) { - new NesControllerSettings().ShowDialog(); + if (Global.Emulator is NES) + new NesControllerSettings().ShowDialog(); + else if (Global.Emulator is QuickNES) + GenericCoreConfig.DoDialog(this, "QuickNES Controller Settings", true, false); } private void MovieSettingsMenuItem_Click(object sender, EventArgs e) diff --git a/BizHawk.Client.EmuHawk/config/GenericCoreConfig.cs b/BizHawk.Client.EmuHawk/config/GenericCoreConfig.cs index 4c1a738a29..9adacfe74c 100644 --- a/BizHawk.Client.EmuHawk/config/GenericCoreConfig.cs +++ b/BizHawk.Client.EmuHawk/config/GenericCoreConfig.cs @@ -18,15 +18,15 @@ namespace BizHawk.Client.EmuHawk object ss; bool syncsettingschanged = false; - GenericCoreConfig() + GenericCoreConfig(bool ignoresettings, bool ignoresyncsettings) { InitializeComponent(); var settable = new SettingsAdapter(Global.Emulator); - if (settable.HasSettings) + if (settable.HasSettings && !ignoresettings) s = settable.GetSettings(); - if (settable.HasSyncSettings) + if (settable.HasSyncSettings && !ignoresyncsettings) ss = settable.GetSyncSettings(); if (s != null) @@ -42,6 +42,11 @@ namespace BizHawk.Client.EmuHawk propertyGrid2.Enabled = false; // disable changes to sync setting when movie, so as not to confuse user } + GenericCoreConfig() + :this(false, false) + { + } + private void button1_Click(object sender, EventArgs e) { var settable = new SettingsAdapter(Global.Emulator); @@ -63,6 +68,12 @@ namespace BizHawk.Client.EmuHawk dlg.ShowDialog(owner); } + public static void DoDialog(IWin32Window owner, string title, bool hidesettings, bool hidesyncsettings) + { + using (var dlg = new GenericCoreConfig(hidesettings, hidesyncsettings) { Text = title }) + dlg.ShowDialog(owner); + } + private void propertyGrid2_PropertyValueChanged(object s, PropertyValueChangedEventArgs e) { syncsettingschanged = true; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs index 05e2ad6021..114bb87714 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs @@ -285,6 +285,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public bool StartAsyncSound() { return true; } public void EndAsyncSound() { } + [Obsolete] // with the changes to both nes and quicknes cores, nothing uses this anymore public static readonly ControllerDefinition NESController = new ControllerDefinition { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs index 3e730452f5..c8cf956dc2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs @@ -62,7 +62,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES } [CoreConstructor("NES")] - public QuickNES(CoreComm comm, byte[] file, object Settings) + public QuickNES(CoreComm comm, byte[] file, object Settings, object SyncSettings) { using (FP.Save()) { @@ -88,6 +88,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES CoreComm.VsyncDen = 655171; PutSettings((QuickNESSettings)Settings ?? new QuickNESSettings()); + _SyncSettings = (QuickNESSyncSettings)SyncSettings ?? new QuickNESSyncSettings(); + _SyncSettings_next = _SyncSettings.Clone(); + + SetControllerDefinition(); ComputeBootGod(); } catch @@ -100,45 +104,71 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES #region Controller - public ControllerDefinition ControllerDefinition { get { return Emulation.Cores.Nintendo.NES.NES.NESController; } } + public ControllerDefinition ControllerDefinition { get; private set; } public IController Controller { get; set; } + void SetControllerDefinition() + { + var def = new ControllerDefinition(); + def.Name = "NES Controller"; + def.BoolButtons.AddRange(new[] { "Reset", "Power" }); // console buttons + if (_SyncSettings.LeftPortConnected || _SyncSettings.RightPortConnected) + def.BoolButtons.AddRange(PadP1.Select(p => p.Name)); + if (_SyncSettings.LeftPortConnected && _SyncSettings.RightPortConnected) + def.BoolButtons.AddRange(PadP2.Select(p => p.Name)); + ControllerDefinition = def; + } + + private struct PadEnt + { + public readonly string Name; + public readonly int Mask; + public PadEnt(string Name, int Mask) + { + this.Name = Name; + this.Mask = Mask; + } + } + + private static PadEnt[] GetPadList(int player) + { + string prefix = string.Format("P{0} ", player); + return PadNames.Zip(PadMasks, (s, i) => new PadEnt(prefix + s, i)).ToArray(); + } + + private static string[] PadNames = new[] + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A" + }; + private static int[] PadMasks = new[] + { + 16, 32, 64, 128, 8, 4, 2, 1 + }; + + private static PadEnt[] PadP1 = GetPadList(1); + private static PadEnt[] PadP2 = GetPadList(2); + + private int GetPad(IEnumerable buttons) + { + int ret = 0; + foreach (var b in buttons) + { + if (Controller[b.Name]) + ret |= b.Mask; + } + return ret; + } + void SetPads(out int j1, out int j2) { - j1 = 0; - j2 = 0; - if (Controller["P1 A"]) - j1 |= 1; - if (Controller["P1 B"]) - j1 |= 2; - if (Controller["P1 Select"]) - j1 |= 4; - if (Controller["P1 Start"]) - j1 |= 8; - if (Controller["P1 Up"]) - j1 |= 16; - if (Controller["P1 Down"]) - j1 |= 32; - if (Controller["P1 Left"]) - j1 |= 64; - if (Controller["P1 Right"]) - j1 |= 128; - if (Controller["P2 A"]) - j2 |= 1; - if (Controller["P2 B"]) - j2 |= 2; - if (Controller["P2 Select"]) - j2 |= 4; - if (Controller["P2 Start"]) - j2 |= 8; - if (Controller["P2 Up"]) - j2 |= 16; - if (Controller["P2 Down"]) - j2 |= 32; - if (Controller["P2 Left"]) - j2 |= 64; - if (Controller["P2 Right"]) - j2 |= 128; + if (_SyncSettings.LeftPortConnected) + j1 = GetPad(PadP1) | unchecked((int)0xffffff00); + else + j1 = 0; + if (_SyncSettings.RightPortConnected) + j2 = GetPad(_SyncSettings.LeftPortConnected ? PadP2 : PadP1) | unchecked((int)0xffffff00); + else + j2 = 0; } #endregion @@ -378,7 +408,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES void ComputeBootGod() { // inefficient, sloppy, etc etc - Emulation.Cores.Nintendo.NES.NES.BootGodDB.Initialize(); + Emulation.Cores.Nintendo.NES.NES.BootGodDB.Initialize(); var chrrom = MemoryDomains["CHR VROM"]; var prgrom = MemoryDomains["PRG ROM"]; @@ -504,17 +534,42 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES } } - QuickNESSettings _Settings; - - // what is this for? public class QuickNESSyncSettings { + [DefaultValue(true)] + public bool LeftPortConnected { get; set; } + + [DefaultValue(true)] + public bool RightPortConnected { get; set; } + + public QuickNESSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + public QuickNESSyncSettings Clone() { - return new QuickNESSyncSettings(); + return (QuickNESSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(QuickNESSyncSettings x, QuickNESSyncSettings y) + { + // the core can handle dynamic plugging and unplugging, but that changes + // the controllerdefinition, and we're not ready for that + return !DeepEquality.DeepEquals(x, y); } } + QuickNESSettings _Settings; + /// + /// the syncsettings that this run of emulation is using (was passed to ctor) + /// + QuickNESSyncSettings _SyncSettings; + /// + /// the syncsettings that were requested but won't be used yet + /// + QuickNESSyncSettings _SyncSettings_next; + public QuickNESSettings GetSettings() { return _Settings.Clone(); @@ -522,7 +577,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES public QuickNESSyncSettings GetSyncSettings() { - return new QuickNESSyncSettings(); + return _SyncSettings_next.Clone(); } public bool PutSettings(QuickNESSettings o) @@ -536,7 +591,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES public bool PutSyncSettings(QuickNESSyncSettings o) { - return false; + bool ret = QuickNESSyncSettings.NeedsReboot(_SyncSettings, o); + _SyncSettings_next = o; + return ret; } #endregion diff --git a/output/dll/libquicknes.dll b/output/dll/libquicknes.dll index 8310b2ed79..4c8bc9642a 100644 Binary files a/output/dll/libquicknes.dll and b/output/dll/libquicknes.dll differ diff --git a/quicknes/nes_emu/Nes_Core.cpp b/quicknes/nes_emu/Nes_Core.cpp index de99321189..e79d3e89cf 100644 --- a/quicknes/nes_emu/Nes_Core.cpp +++ b/quicknes/nes_emu/Nes_Core.cpp @@ -305,9 +305,9 @@ int Nes_Core::read_io( nes_addr_t addr ) // to do: to aid with recording, doesn't emulate transparent latch, // so a game that held strobe at 1 and read $4016 or $4017 would not get // the current A status as occurs on a NES - unsigned long result = joypad.joypad_latches [addr & 1]; + int32_t result = joypad.joypad_latches [addr & 1]; if ( !(joypad.w4016 & 1) ) - joypad.joypad_latches [addr & 1] = (result >> 1) | 0x80000000; + joypad.joypad_latches [addr & 1] = result >> 1; // ASR is intentional return result & 1; } diff --git a/quicknes/nes_emu/Nes_Core.h b/quicknes/nes_emu/Nes_Core.h index b4dd759703..179f9ef779 100644 --- a/quicknes/nes_emu/Nes_Core.h +++ b/quicknes/nes_emu/Nes_Core.h @@ -50,7 +50,7 @@ public: private: friend class Nes_Emu; bool sram_present; public: - unsigned long current_joypad [2]; + uint32_t current_joypad [2]; int joypad_read_count; Nes_Cart const* cart; Nes_Mapper* mapper; diff --git a/quicknes/nes_emu/Nes_Emu.cpp b/quicknes/nes_emu/Nes_Emu.cpp index 98edee5baa..6c8aa6ca9e 100644 --- a/quicknes/nes_emu/Nes_Emu.cpp +++ b/quicknes/nes_emu/Nes_Emu.cpp @@ -124,10 +124,10 @@ void Nes_Emu::set_palette_range( int begin, int end ) require( host_palette_size >= palette_alignment ); } -blargg_err_t Nes_Emu::emulate_frame( int joypad1, int joypad2 ) +blargg_err_t Nes_Emu::emulate_frame( const uint32_t joypad1, const uint32_t joypad2 ) { - emu.current_joypad [0] = (joypad1 |= ~0xFF); - emu.current_joypad [1] = (joypad2 |= ~0xFF); + emu.current_joypad [0] = joypad1; + emu.current_joypad [1] = joypad2; emu.ppu.host_pixels = NULL; diff --git a/quicknes/nes_emu/Nes_Emu.h b/quicknes/nes_emu/Nes_Emu.h index e0fee4298f..c08d1f4afe 100644 --- a/quicknes/nes_emu/Nes_Emu.h +++ b/quicknes/nes_emu/Nes_Emu.h @@ -52,7 +52,10 @@ public: // Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image // and sound are available for output using the accessors below. - virtual blargg_err_t emulate_frame( int joypad1, int joypad2 = 0 ); + // A connected controller should have 0xffffff** in the high bits, or 0x000000** + // if emulating an incorrectly made third party controller. A disconnected controller + // should be 0x00000000 exactly. + virtual blargg_err_t emulate_frame( uint32_t joypad1, uint32_t joypad2 ); // Maximum size of palette that can be generated enum { max_palette_size = 256 }; diff --git a/quicknes/nes_emu/nes_data.h b/quicknes/nes_emu/nes_data.h index 4e33472502..e831866952 100644 --- a/quicknes/nes_emu/nes_data.h +++ b/quicknes/nes_emu/nes_data.h @@ -87,7 +87,7 @@ BOOST_STATIC_ASSERT( sizeof (nes_state_t) == 8 ); struct joypad_state_t { - BOOST::uint32_t joypad_latches [2]; // joypad 1 & 2 shift registers + uint32_t joypad_latches [2]; // joypad 1 & 2 shift registers byte w4016; // strobe byte unused [3];