quicknes: support unplugging controllers

This commit is contained in:
goyuken 2014-11-01 17:44:04 +00:00
parent 01909dfb98
commit f3e9958f07
10 changed files with 131 additions and 56 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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
{

View File

@ -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<PadEnt> 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;
/// <summary>
/// the syncsettings that this run of emulation is using (was passed to ctor)
/// </summary>
QuickNESSyncSettings _SyncSettings;
/// <summary>
/// the syncsettings that were requested but won't be used yet
/// </summary>
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

Binary file not shown.

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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 };

View File

@ -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];