SMS: bring controllers in line with other cores and fix virtual pads

This commit is contained in:
alyosha-tas 2021-07-07 21:16:56 -04:00
parent bed5cc66ab
commit bcd47a1ad9
9 changed files with 1149 additions and 558 deletions

View File

@ -93,16 +93,11 @@ namespace BizHawk.Client.Common.movie.import
byte[] md5 = r.ReadBytes(16);
Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();
var ss = new SMS.SmsSyncSettings
{
ControllerType = SMS.SmsSyncSettings.ControllerTypes.Standard
};
var ss = new SMS.SmsSyncSettings();
var cd = new SMSControllerDeck(ss.Port1, ss.Port2, isGameGear, ss.UseKeyboard);
var controllers = new SimpleController
{
Definition = isGameGear
? SMS.SmsController
: SMS.GGController
Definition = cd.Definition
};
/*

View File

@ -7,38 +7,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition
{
get
{
if (IsGameGear_C)
{
return GGController;
}
// Sorta a hack but why not
PortDEEnabled = SyncSettings.ControllerType == SmsSyncSettings.ControllerTypes.Keyboard;
switch(SyncSettings.ControllerType)
{
case SmsSyncSettings.ControllerTypes.Paddle:
return SMSPaddleController;
case SmsSyncSettings.ControllerTypes.LightPhaser:
// scale the vertical to the display mode
var axisName = SMSLightPhaserController.Axes[1];
SMSLightPhaserController.Axes[axisName] = SMSLightPhaserController.Axes[axisName]
.With(0.RangeTo(Vdp.FrameHeight - 1), Vdp.FrameHeight / 2);
return SMSLightPhaserController;
case SmsSyncSettings.ControllerTypes.SportsPad:
return SMSSportsPadController;
case SmsSyncSettings.ControllerTypes.Keyboard:
return SMSKeyboardController;
default:
return SmsController;
}
}
}
// not savestated variables
private int s_L, s_R;

View File

@ -1,5 +1,6 @@
using System.ComponentModel;
using BizHawk.Emulation.Common;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
@ -80,9 +81,21 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
[DisplayName("Display Type")]
public DisplayTypes DisplayType { get; set; } = DisplayTypes.Auto;
[DisplayName("Controller Type")]
[Description("Currently controllers can not be configured separately")]
public ControllerTypes ControllerType { get; set; } = ControllerTypes.Standard;
[DisplayName("Use keyboard")]
[Description("Enables keyboard for non-GG systems")]
public bool UseKeyboard { get; set; } = false;
[DefaultValue(SMSControllerTypes.Standard)]
[DisplayName("Port 1 Device")]
[Description("The type of controller plugged into the first controller port")]
[TypeConverter(typeof(DescribableEnumConverter))]
public SMSControllerTypes Port1 { get; set; } = SMSControllerTypes.Standard;
[DefaultValue(SMSControllerTypes.Standard)]
[DisplayName("Port 2 Device")]
[Description("The type of controller plugged into the second controller port")]
[TypeConverter(typeof(DescribableEnumConverter))]
public SMSControllerTypes Port2 { get; set; } = SMSControllerTypes.Standard;
public SmsSyncSettings Clone() => (SmsSyncSettings)MemberwiseClone();
@ -94,7 +107,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
|| x.UseBios != y.UseBios
|| x.ConsoleRegion != y.ConsoleRegion
|| x.DisplayType != y.DisplayType
|| x.ControllerType != y.ControllerType;
|| x.UseKeyboard != y.UseKeyboard
|| x.Port1 != y.Port1
|| x.Port2 != y.Port2;
}
public enum ControllerTypes

View File

@ -19,6 +19,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
Cpu.SyncState(ser);
Vdp.SyncState(ser);
PSG.SyncState(ser);
_controllerDeck.SyncState(ser);
ser.Sync("RAM", ref SystemRam, false);
ser.Sync(nameof(RomBank0), ref RomBank0);
ser.Sync(nameof(RomBank1), ref RomBank1);
@ -32,9 +33,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
ser.Sync(nameof(Port05), ref Port05);
ser.Sync(nameof(Port3E), ref Port3E);
ser.Sync(nameof(Port3F), ref Port3F);
ser.Sync(nameof(Controller1SelectHigh), ref Controller1SelectHigh);
ser.Sync(nameof(Controller2SelectHigh), ref Controller2SelectHigh);
ser.Sync(nameof(LatchLightPhaser), ref LatchLightPhaser);
ser.Sync(nameof(LatchLightPhaser1), ref LatchLightPhaser1);
ser.Sync(nameof(LatchLightPhaser2), ref LatchLightPhaser2);
ser.Sync(nameof(ControllerTick), ref ControllerTick);
ser.Sync(nameof(start_pressed), ref start_pressed);
ser.Sync(nameof(cntr_rd_0), ref cntr_rd_0);
ser.Sync(nameof(cntr_rd_1), ref cntr_rd_1);

View File

@ -6,78 +6,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
public partial class SMS
{
public static readonly ControllerDefinition SmsController = new ControllerDefinition
{
Name = "SMS Controller",
BoolButtons =
{
"Reset", "Pause",
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B1", "P2 B2"
}
};
public static readonly ControllerDefinition GGController = new ControllerDefinition
{
Name = "GG Controller",
BoolButtons =
{
"Reset",
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2", "P1 Start"
}
};
public static readonly ControllerDefinition SMSPaddleController = new ControllerDefinition
{
Name = "SMS Paddle Controller",
BoolButtons =
{
"Reset", "Pause",
"P1 Left", "P1 Right", "P1 B1",
"P2 Left", "P2 Right", "P2 B1",
}
}.AddAxis("P1 Paddle", 0.RangeTo(255), 128)
.AddAxis("P2 Paddle", 0.RangeTo(255), 128);
public static readonly ControllerDefinition SMSLightPhaserController = new ControllerDefinition
{
Name = "SMS Light Phaser Controller",
BoolButtons =
{
"Reset", "Pause",
"P1 Trigger"
}
}.AddXYPair("P1 {0}", AxisPairOrientation.RightAndUp, 0.RangeTo(127), 64, 0.RangeTo(1000), 500); //TODO verify direction against hardware
public static readonly ControllerDefinition SMSSportsPadController = new ControllerDefinition
{
Name = "SMS Sports Pad Controller",
BoolButtons =
{
"Reset", "Pause",
"P1 Left", "P1 Right", "P1 Up", "P1 Down", "P1 B1", "P1 B2",
"P2 Left", "P2 Right", "P2 Up", "P2 Down", "P2 B1", "P2 B2"
}
}.AddXYPair("P1 {0}", AxisPairOrientation.RightAndUp, (-64).RangeTo(63), 0) //TODO verify direction against hardware
.AddXYPair("P2 {0}", AxisPairOrientation.RightAndUp, (-64).RangeTo(63), 0); //TODO ditto
public static readonly ControllerDefinition SMSKeyboardController = new ControllerDefinition
{
Name = "SMS Keyboard Controller",
BoolButtons =
{
"Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Minus", "Key Caret", "Key Yen", "Key Break",
"Key Function", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", "Key At", "Key Left Bracket", "Key Return", "Key Up Arrow",
"Key Control", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Semicolon", "Key Colon", "Key Right Bracket", "Key Left Arrow", "Key Right Arrow",
"Key Shift", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Comma", "Key Period", "Key Slash", "Key PI", "Key Down Arrow",
"Key Graph", "Key Kana", "Key Space", "Key Home/Clear", "Key Insert/Delete",
"Reset", "Pause",
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B1", "P2 B2"
}
};
private static readonly string[] KeyboardMap =
{
"Key 1", "Key Q", "Key A", "Key Z", "Key Kana", "Key Comma", "Key K", "Key I", "Key 8", null, null, null,
@ -90,20 +18,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2", "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B1", "P2 B2"
};
private const int PaddleMin = 0;
private const int PaddleMax = 255;
private const int SportsPadMin = -64;
private const int SportsPadMax = 63;
private bool LatchLightPhaser1 = false;
private bool LatchLightPhaser2 = false;
// The paddles and sports pads have data select states
private bool Controller1SelectHigh = true;
private bool Controller2SelectHigh = true;
private bool LatchLightPhaser = false;
// further state value for sports pad, may be useful for other controllers in future
private int Controller1State = 3;
private int Controller2State = 3;
private int ControllerTick = 0; // for timing in japan
private byte ReadControls1()
@ -112,247 +29,32 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
_lagged = false;
byte value = 0xFF;
switch (SyncSettings.ControllerType)
PresetControllerState(1);
// Hard-wired together for paddles?
_controllerDeck.SetPin_c2(_controller, _controllerDeck.GetPin_c1(_controller));
value &= _controllerDeck.ReadPort1_c1(_controller);
value &= _controllerDeck.ReadPort1_c2(_controller);
PostsetControllerState(1);
if (!IsGameGear_C && SyncSettings.UseKeyboard)
{
case SmsSyncSettings.ControllerTypes.Paddle:
// 7 represents ordinary controller reads
if ((PortDE & 7) != 7)
{
value = 0xFF;
for (int bit = 0; bit < 8; ++bit)
{
// use analog values from a controller, see http://www.smspower.org/Development/Paddle
string key = KeyboardMap[(PortDE & 0x07) * 12 + bit];
int paddle1Pos;
if (_controller.IsPressed("P1 Left"))
paddle1Pos = PaddleMin;
else if (_controller.IsPressed("P1 Right"))
paddle1Pos = PaddleMax;
else
paddle1Pos = (int)_controller.AxisValue("P1 Paddle");
int paddle2Pos;
if (_controller.IsPressed("P2 Left"))
paddle2Pos = PaddleMin;
else if (_controller.IsPressed("P2 Right"))
paddle2Pos = PaddleMax;
else
paddle2Pos = (int)_controller.AxisValue("P2 Paddle");
PresetControllerState(1);
// Hard-wired together?
Controller2SelectHigh = Controller1SelectHigh;
if (Controller1SelectHigh)
if (key != null && _controller.IsPressed(key))
{
if ((paddle1Pos & 0x10) == 0) value &= 0xFE;
if ((paddle1Pos & 0x20) == 0) value &= 0xFD;
if ((paddle1Pos & 0x40) == 0) value &= 0xFB;
if ((paddle1Pos & 0x80) == 0) value &= 0xF7;
}
else
{
if ((paddle1Pos & 0x01) == 0) value &= 0xFE;
if ((paddle1Pos & 0x02) == 0) value &= 0xFD;
if ((paddle1Pos & 0x04) == 0) value &= 0xFB;
if ((paddle1Pos & 0x08) == 0) value &= 0xF7;
}
if (_controller.IsPressed("P1 B1")) value &= 0xEF;
if (!Controller1SelectHigh) value &= 0xDF;
if (Controller2SelectHigh)
{
if ((paddle2Pos & 0x10) == 0) value &= 0xBF;
if ((paddle2Pos & 0x20) == 0) value &= 0x7F;
}
else
{
if ((paddle2Pos & 0x01) == 0) value &= 0xBF;
if ((paddle2Pos & 0x02) == 0) value &= 0x7F;
}
PostsetControllerState(1);
}
break;
case SmsSyncSettings.ControllerTypes.LightPhaser:
if (_controller.IsPressed("P1 Trigger")) value &= 0xEF;
break;
case SmsSyncSettings.ControllerTypes.SportsPad:
{
int p1X;
if (_controller.IsPressed("P1 Left"))
p1X = SportsPadMin;
else if (_controller.IsPressed("P1 Right"))
p1X = SportsPadMax;
else
p1X = (int)_controller.AxisValue("P1 X");
int p1Y;
if (_controller.IsPressed("P1 Up"))
p1Y = SportsPadMin;
else if (_controller.IsPressed("P1 Down"))
p1Y = SportsPadMax;
else
p1Y = (int)_controller.AxisValue("P1 Y");
int p2X;
if (_controller.IsPressed("P2 Left"))
p2X = SportsPadMin;
else if (_controller.IsPressed("P2 Right"))
p2X = SportsPadMax;
else
p2X = (int)_controller.AxisValue("P2 X");
int p2Y;
if (_controller.IsPressed("P2 Up"))
p2Y = SportsPadMin;
else if (_controller.IsPressed("P2 Down"))
p2Y = SportsPadMax;
else
p2Y = (int)_controller.AxisValue("P2 Y");
if (_region == SmsSyncSettings.Regions.Japan)
{
p1X += 128;
p1Y += 128;
p2X += 128;
p2Y += 128;
}
else
{
p1X *= -1;
p1Y *= -1;
p2X *= -1;
p2Y *= -1;
}
PresetControllerState(1);
// advance state
if (Controller1SelectHigh && (Controller1State % 2 == 0))
{
++Controller1State;
}
else if (!Controller1SelectHigh && (Controller1State % 2 == 1))
{
if (++Controller1State == (_region == SmsSyncSettings.Regions.Japan ? 6 : 4))
Controller1State = 0;
}
if (Controller2SelectHigh && (Controller2State % 2 == 0))
{
++Controller2State;
}
else if (!Controller2SelectHigh && (Controller2State % 2 == 1))
{
if (++Controller2State == (_region == SmsSyncSettings.Regions.Japan ? 6 : 4))
Controller2State = 0;
}
switch (Controller1State)
{
case 0:
if ((p1X & 0x10) == 0) value &= 0xFE;
if ((p1X & 0x20) == 0) value &= 0xFD;
if ((p1X & 0x40) == 0) value &= 0xFB;
if ((p1X & 0x80) == 0) value &= 0xF7;
break;
case 1:
if ((p1X & 0x01) == 0) value &= 0xFE;
if ((p1X & 0x02) == 0) value &= 0xFD;
if ((p1X & 0x04) == 0) value &= 0xFB;
if ((p1X & 0x08) == 0) value &= 0xF7;
break;
case 2:
if ((p1Y & 0x10) == 0) value &= 0xFE;
if ((p1Y & 0x20) == 0) value &= 0xFD;
if ((p1Y & 0x40) == 0) value &= 0xFB;
if ((p1Y & 0x80) == 0) value &= 0xF7;
break;
case 3:
if ((p1Y & 0x01) == 0) value &= 0xFE;
if ((p1Y & 0x02) == 0) value &= 0xFD;
if ((p1Y & 0x04) == 0) value &= 0xFB;
if ((p1Y & 0x08) == 0) value &= 0xF7;
break;
case 4:
// specific to Japan: sync via TR
value &= 0xDF;
break;
case 5:
// specific to Japan: buttons
if (_controller.IsPressed("P1 B1")) value &= 0xFE;
if (_controller.IsPressed("P1 B2")) value &= 0xFD;
break;
}
if (_region != SmsSyncSettings.Regions.Japan)
{
// Buttons like normal in Export
if (_controller.IsPressed("P1 B1")) value &= 0xEF;
if (_controller.IsPressed("P1 B2")) value &= 0xDF;
}
else
{
// In Japan, it contains selectHigh
if (!Controller1SelectHigh) value &= 0xEF;
}
switch (Controller2State)
{
case 0:
if ((p2X & 0x10) == 0) value &= 0xBF;
if ((p2X & 0x20) == 0) value &= 0x7F;
break;
case 1:
if ((p2X & 0x01) == 0) value &= 0xBF;
if ((p2X & 0x02) == 0) value &= 0x7F;
break;
case 2:
if ((p2Y & 0x10) == 0) value &= 0xBF;
if ((p2Y & 0x20) == 0) value &= 0x7F;
break;
case 3:
if ((p2Y & 0x01) == 0) value &= 0xBF;
if ((p2Y & 0x02) == 0) value &= 0x7F;
break;
case 5:
// specific to Japan: buttons
if (_controller.IsPressed("P2 B1")) value &= 0xBF;
if (_controller.IsPressed("P2 B2")) value &= 0x7F;
break;
}
PostsetControllerState(1);
}
break;
case SmsSyncSettings.ControllerTypes.Keyboard:
{
// use keyboard map to get each bit
for (int bit = 0; bit < 8; ++bit)
{
string key = KeyboardMap[(PortDE & 0x07) * 12 + bit];
if (key != null && _controller.IsPressed(key))
{
value &= (byte)~(1 << bit);
}
value &= (byte)~(1 << bit);
}
}
break;
default:
// Normal controller
if (_controller.IsPressed("P1 Up")) value &= 0xFE;
if (_controller.IsPressed("P1 Down")) value &= 0xFD;
if (_controller.IsPressed("P1 Left")) value &= 0xFB;
if (_controller.IsPressed("P1 Right")) value &= 0xF7;
if (_controller.IsPressed("P1 B1")) value &= 0xEF;
if (_controller.IsPressed("P1 B2")) value &= 0xDF;
if (_controller.IsPressed("P2 Up")) value &= 0xBF;
if (_controller.IsPressed("P2 Down")) value &= 0x7F;
break;
}
}
return value;
@ -364,150 +66,12 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
_lagged = false;
byte value = 0xFF;
switch (SyncSettings.ControllerType)
{
case SmsSyncSettings.ControllerTypes.Paddle:
{
// use analog values from a controller, see http://www.smspower.org/Development/Paddle
PresetControllerState(2);
int paddle2Pos;
if (_controller.IsPressed("P2 Left"))
paddle2Pos = PaddleMin;
else if (_controller.IsPressed("P2 Right"))
paddle2Pos = PaddleMax;
else
paddle2Pos = (int)_controller.AxisValue("P2 Paddle");
value &= _controllerDeck.ReadPort2_c1(_controller);
value &= _controllerDeck.ReadPort2_c2(_controller);
PresetControllerState(2);
if (Controller2SelectHigh)
{
if ((paddle2Pos & 0x40) == 0) value &= 0xFE;
if ((paddle2Pos & 0x80) == 0) value &= 0xFD;
}
else
{
if ((paddle2Pos & 0x04) == 0) value &= 0xFE;
if ((paddle2Pos & 0x08) == 0) value &= 0xFD;
}
if (_controller.IsPressed("P2 B1")) value &= 0xFB;
if (!Controller2SelectHigh) value &= 0xF7;
PostsetControllerState(2);
}
break;
case SmsSyncSettings.ControllerTypes.LightPhaser:
if (LatchLightPhaser)
{
value &= 0xBF;
LatchLightPhaser = false;
}
break;
case SmsSyncSettings.ControllerTypes.SportsPad:
{
int p2X;
if (_controller.IsPressed("P2 Left"))
p2X = SportsPadMin;
else if (_controller.IsPressed("P2 Right"))
p2X = SportsPadMax;
else
p2X = (int)_controller.AxisValue("P2 X");
int p2Y;
if (_controller.IsPressed("P2 Down"))
p2Y = SportsPadMin;
else if (_controller.IsPressed("P2 Up"))
p2Y = SportsPadMax;
else
p2Y = (int)_controller.AxisValue("P2 Y");
if (_region == SmsSyncSettings.Regions.Japan)
{
p2X += 128;
p2Y += 128;
}
else
{
p2X *= -1;
p2Y *= -1;
}
PresetControllerState(2);
if (Controller2SelectHigh && (Controller2State % 2 == 0))
{
++Controller2State;
}
else if (!Controller2SelectHigh && (Controller2State % 2 == 1))
{
if (++Controller2State == (_region == SmsSyncSettings.Regions.Japan ? 6 : 4))
Controller2State = 0;
}
switch (Controller2State)
{
case 0:
if ((p2X & 0x40) == 0) value &= 0xFE;
if ((p2X & 0x80) == 0) value &= 0xFD;
break;
case 1:
if ((p2X & 0x04) == 0) value &= 0xFE;
if ((p2X & 0x08) == 0) value &= 0xFD;
break;
case 2:
if ((p2Y & 0x40) == 0) value &= 0xFE;
if ((p2Y & 0x80) == 0) value &= 0xFD;
break;
case 3:
if ((p2Y & 0x04) == 0) value &= 0xFE;
if ((p2Y & 0x08) == 0) value &= 0xFD;
break;
}
if (_region != SmsSyncSettings.Regions.Japan)
{
// Buttons like normal in Export
if (_controller.IsPressed("P2 B1")) value &= 0xFB;
if (_controller.IsPressed("P2 B2")) value &= 0xF7;
}
else
{
if (!Controller2SelectHigh) value &= 0xF7;
}
PostsetControllerState(2);
}
break;
case SmsSyncSettings.ControllerTypes.Keyboard:
{
value &= 0x7F;
// use keyboard map to get each bit
for (int bit = 0; bit < 4; ++bit)
{
string key = KeyboardMap[(PortDE & 0x07) * 12 + bit + 8];
if (key != null && _controller.IsPressed(key))
{
value &= (byte)~(1 << bit);
}
}
}
break;
default:
// Normal controller
if (_controller.IsPressed("P2 Left")) value &= 0xFE;
if (_controller.IsPressed("P2 Right")) value &= 0xFD;
if (_controller.IsPressed("P2 B1")) value &= 0xFB;
if (_controller.IsPressed("P2 B2")) value &= 0xF7;
break;
}
PostsetControllerState(2);
if (_controller.IsPressed("Reset")) value &= 0xEF;
@ -526,6 +90,37 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
}
}
if (LatchLightPhaser1)
{
value &= 0xBF;
LatchLightPhaser1 = false;
}
if (LatchLightPhaser2)
{
value &= 0x7F;
LatchLightPhaser2 = false;
}
if (!IsGameGear_C && SyncSettings.UseKeyboard)
{
// 7 represents ordinary controller reads
if ((PortDE & 7) != 7)
{
value = 0x7F;
for (int bit = 0; bit < 4; ++bit)
{
string key = KeyboardMap[(PortDE & 0x07) * 12 + bit + 8];
if (key != null && _controller.IsPressed(key))
{
value &= (byte)~(1 << bit);
}
}
}
}
return value;
}
@ -534,24 +129,48 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
const int phaserRadius = 4;
// specifically lightgun needs to do things on a per-line basis
if (SyncSettings.ControllerType == SmsSyncSettings.ControllerTypes.LightPhaser)
if (!IsGameGear_C)
{
byte phaserX = (byte)(_controller.AxisValue("P1 X") + 20);
int phaserY = (int)_controller.AxisValue("P1 Y");
int scanline = Vdp.ScanLine;
if (!LatchLightPhaser && phaserY >= scanline - phaserRadius && phaserY <= scanline + phaserRadius)
if (SyncSettings.Port1 == SMSControllerTypes.Phaser)
{
if (scanline >= Vdp.FrameHeight)
return;
byte phaserX = (byte)(_controller.AxisValue("P1 X") + 20);
int phaserY = (int)_controller.AxisValue("P1 Y");
int scanline = Vdp.ScanLine;
// latch HCounter via TH
Vdp.HCounter = phaserX;
LatchLightPhaser = true;
if (!LatchLightPhaser1 && phaserY >= scanline - phaserRadius && phaserY <= scanline + phaserRadius)
{
if (scanline >= Vdp.FrameHeight)
return;
// latch HCounter via TH
Vdp.HCounter = phaserX;
LatchLightPhaser1 = true;
}
else
{
LatchLightPhaser1 = false;
}
}
else
if (SyncSettings.Port2 == SMSControllerTypes.Phaser)
{
LatchLightPhaser = false;
byte phaserX = (byte)(_controller.AxisValue("P2 X") + 20);
int phaserY = (int)_controller.AxisValue("P2 Y");
int scanline = Vdp.ScanLine;
if (!LatchLightPhaser2 && phaserY >= scanline - phaserRadius && phaserY <= scanline + phaserRadius)
{
if (scanline >= Vdp.FrameHeight)
return;
// latch HCounter via TH
Vdp.HCounter = phaserX;
LatchLightPhaser2 = true;
}
else
{
LatchLightPhaser2 = false;
}
}
}
}
@ -589,18 +208,18 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
if ((Port3F & 0x02) == 0x00)
{
Controller1SelectHigh = (Port3F & 0x20) != 0;
_controllerDeck.SetPin_c1(_controller, (Port3F & 0x20) != 0);
// resync
Controller2State = 3;
_controllerDeck.SetCounter_c2(_controller, 3);
}
if ((Port3F & 0x08) == 0x00)
{
Controller2SelectHigh = (Port3F & 0x80) != 0;
_controllerDeck.SetPin_c2(_controller, (Port3F & 0x80) != 0);
// resync
Controller1State = 3;
_controllerDeck.SetCounter_c1(_controller, 3);
}
}
}
@ -614,12 +233,15 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
if (pin == 1)
{
Controller1SelectHigh ^= true;
bool temp = _controllerDeck.GetPin_c1(_controller);
_controllerDeck.SetPin_c1(_controller, temp ^ true);
}
else
{
Controller1SelectHigh = false;
Controller2SelectHigh ^= true;
_controllerDeck.SetPin_c1(_controller, false);
bool temp = _controllerDeck.GetPin_c2(_controller);
_controllerDeck.SetPin_c2(_controller, temp ^ true);
}
}
}

View File

@ -213,6 +213,13 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
ser.Register<ISmsGpuView>(new SmsGpuView(Vdp));
}
_controllerDeck = new SMSControllerDeck(syncSettings.Port1, syncSettings.Port2, IsGameGear_C, syncSettings.UseKeyboard);
// Sorta a hack but why not
PortDEEnabled = syncSettings.UseKeyboard && !IsGameGear_C;
_controllerDeck.SetRegion(_controller, _region == SmsSyncSettings.Regions.Japan);
}
public void HardReset()
@ -421,6 +428,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
else if (port == 0xF2 && HasYM2413) YM2413.DetectionValue = value;
}
private readonly SMSControllerDeck _controllerDeck;
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
private readonly SmsSyncSettings.Regions _region;
public class SmsGpuView : ISmsGpuView

View File

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
public class SMSControllerDeck
{
public SMSControllerDeck(SMSControllerTypes controller1, SMSControllerTypes controller2, bool is_GG, bool use_keyboard)
{
if (is_GG)
{
Port1 = new GGController(1);
// Port 2 is defined, but not used for Game Gear
Port2 = new GGController(2);
Definition = new ControllerDefinition
{
Name = Port1.Definition.Name,
BoolButtons = new[] { "Reset" }
.Concat(Port1.Definition.BoolButtons)
.ToList()
};
}
else
{
Port1 = ControllerCtors[controller1](1);
Port2 = ControllerCtors[controller2](2);
if (!use_keyboard)
{
Definition = new ControllerDefinition
{
Name = Port1.Definition.Name,
BoolButtons = new[] { "Reset", "Pause" }
.Concat(Port1.Definition.BoolButtons)
.Concat(Port2.Definition.BoolButtons)
.ToList()
};
}
else
{
Definition = new ControllerDefinition
{
Name = Port1.Definition.Name,
BoolButtons = new[] { "Reset", "Pause" }
.Concat(Port1.Definition.BoolButtons)
.Concat(Port2.Definition.BoolButtons)
.Concat(KeyboardMap)
.ToList()
};
}
foreach (var kvp in Port1.Definition.Axes) Definition.Axes.Add(kvp);
foreach (var kvp in Port2.Definition.Axes) Definition.Axes.Add(kvp);
}
}
public byte ReadPort1_c1(IController c)
{
return Port1.Read_p1_c1(c);
}
public byte ReadPort1_c2(IController c)
{
return Port2.Read_p1_c2(c);
}
public byte ReadPort2_c1(IController c)
{
return Port1.Read_p2_c1(c);
}
public byte ReadPort2_c2(IController c)
{
return Port2.Read_p2_c2(c);
}
public bool GetPin_c1(IController c)
{
return Port1.PinStateGet(c);
}
public bool GetPin_c2(IController c)
{
return Port2.PinStateGet(c);
}
public void SetPin_c1(IController c, bool val)
{
Port1.PinStateSet(c, val);
}
public void SetPin_c2(IController c, bool val)
{
Port2.PinStateSet(c, val);
}
public void SetCounter_c1(IController c, int val)
{
Port1.CounterSet(c, val);
}
public void SetCounter_c2(IController c, int val)
{
Port2.CounterSet(c, val);
}
public void SetRegion(IController c, bool val)
{
Port1.RegionSet(c, val);
Port2.RegionSet(c, val);
}
public ControllerDefinition Definition { get; }
public void SyncState(Serializer ser)
{
ser.BeginSection(nameof(Port1));
Port1.SyncState(ser);
Port2.SyncState(ser);
ser.EndSection();
}
private readonly IPort Port1, Port2;
private static IReadOnlyDictionary<SMSControllerTypes, Func<int, IPort>> _controllerCtors;
public static IReadOnlyDictionary<SMSControllerTypes, Func<int, IPort>> ControllerCtors => _controllerCtors
??= new Dictionary<SMSControllerTypes, Func<int, IPort>>
{
[SMSControllerTypes.Standard] = portNum => new SmsController(portNum),
[SMSControllerTypes.Paddle] = portNum => new SMSPaddleController(portNum),
[SMSControllerTypes.SportsPad] = portNum => new SMSSportsPadController(portNum),
[SMSControllerTypes.Phaser] = portNum => new SMSLightPhaserController(portNum),
};
public static string DefaultControllerName => typeof(SmsController).DisplayName();
private static readonly string[] KeyboardMap =
{
"Key 1", "Key Q", "Key A", "Key Z", "Key Kana", "Key Comma", "Key K", "Key I", "Key 8", null, null, null,
"Key 2", "Key W", "Key S", "Key X", "Key Space", "Key Period", "Key L", "Key O", "Key 9", null, null, null,
"Key 3", "Key E", "Key D", "Key C", "Key Home/Clear", "Key Slash", "Key Semicolon", "Key P", "Key 0", null, null, null,
"Key 4", "Key R", "Key F", "Key V", "Key Insert/Delete", "Key PI", "Key Colon", "Key At", "Key Minus", null, null, null,
"Key 5", "Key T", "Key G", "Key B", null, "Key Down Arrow", "Key Right Bracket", "Key Left Bracket", "Key Caret", null, null, null,
"Key 6", "Key Y", "Key H", "Key N", null, "Key Left Arrow", "Key Return", null, "Key Yen", null, null, "Key Function",
"Key 7", "Key U", "Key J", "Key M", null, "Key Right Arrow", "Key Up Arrow", null, "Key Break", "Key Graph", "Key Control", "Key Shift",
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2", "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B1", "P2 B2"
};
}
}

View File

@ -0,0 +1,690 @@
using System;
using System.ComponentModel;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
public enum SMSControllerTypes
{
Standard,
Paddle,
SportsPad,
Phaser
}
/// <summary>
/// Represents a SMS controller
/// </summary>
public interface IPort
{
byte Read_p1_c1(IController c);
byte Read_p1_c2(IController c);
byte Read_p2_c1(IController c);
byte Read_p2_c2(IController c);
ControllerDefinition Definition { get; }
void SyncState(Serializer ser);
int PortNum { get; }
bool PinStateGet(IController c);
void PinStateSet(IController c, bool val);
void CounterSet(IController c, int val);
void RegionSet(IController c, bool val);
}
[DisplayName("Standarad Controller")]
public class SmsController : IPort
{
public SmsController(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "SMS Controller",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList()
};
}
public int PortNum { get; }
public ControllerDefinition Definition { get; }
public byte Read_p1_c1(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0])) result &= 0xFE;
if (c.IsPressed(Definition.BoolButtons[1])) result &= 0xFD;
if (c.IsPressed(Definition.BoolButtons[2])) result &= 0xFB;
if (c.IsPressed(Definition.BoolButtons[3])) result &= 0xF7;
if (c.IsPressed(Definition.BoolButtons[4])) result &= 0xEF;
if (c.IsPressed(Definition.BoolButtons[5])) result &= 0xDF;
return result;
}
public byte Read_p1_c2(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0])) result &= 0xBF;
if (c.IsPressed(Definition.BoolButtons[1])) result &= 0x7F;
return result;
}
public byte Read_p2_c1(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c2(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[2])) result &= 0xFE;
if (c.IsPressed(Definition.BoolButtons[3])) result &= 0xFD;
if (c.IsPressed(Definition.BoolButtons[4])) result &= 0xFB;
if (c.IsPressed(Definition.BoolButtons[5])) result &= 0xF7;
return result;
}
public bool PinStateGet(IController c) { return false; }
public void PinStateSet(IController c, bool val) { }
public void CounterSet(IController c, int val) { }
public void RegionSet(IController c, bool val) { }
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "B1", "B2"
};
public void SyncState(Serializer ser)
{
// nothing
}
}
[DisplayName("Game Gear Controller")]
public class GGController : IPort
{
public GGController(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "GG Controller",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList()
};
}
public int PortNum { get; }
public ControllerDefinition Definition { get; }
public byte Read_p1_c1(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0])) result &= 0xFE;
if (c.IsPressed(Definition.BoolButtons[1])) result &= 0xFD;
if (c.IsPressed(Definition.BoolButtons[2])) result &= 0xFB;
if (c.IsPressed(Definition.BoolButtons[3])) result &= 0xF7;
if (c.IsPressed(Definition.BoolButtons[4])) result &= 0xEF;
if (c.IsPressed(Definition.BoolButtons[5])) result &= 0xDF;
return result;
}
public byte Read_p1_c2(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c1(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c2(IController c)
{
byte result = 0xFF;
return result;
}
public bool PinStateGet(IController c) { return false; }
public void PinStateSet(IController c, bool val) { }
public void CounterSet(IController c, int val) { }
public void RegionSet(IController c, bool val) { }
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "B1", "B2", "Start"
};
public void SyncState(Serializer ser)
{
//nothing
}
}
[DisplayName("SMS Paddle Controller")]
public class SMSPaddleController : IPort
{
public SMSPaddleController(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "SMS Paddle Controller",
BoolButtons = BaseDefinition.Select(b => $"P{PortNum} {b}").ToList()
}.AddAxis($"P{PortNum} Paddle", 0.RangeTo(255), 128);
}
public int PortNum { get; }
public bool pin;
public ControllerDefinition Definition { get; }
public byte Read_p1_c1(IController c)
{
byte result = 0xFF;
int paddle1Pos;
if (c.IsPressed("P1 Left"))
paddle1Pos = 0;
else if (c.IsPressed("P1 Right"))
paddle1Pos = 255;
else
paddle1Pos = (int)c.AxisValue("P1 Paddle");
if (pin)
{
if ((paddle1Pos & 0x10) == 0) result &= 0xFE;
if ((paddle1Pos & 0x20) == 0) result &= 0xFD;
if ((paddle1Pos & 0x40) == 0) result &= 0xFB;
if ((paddle1Pos & 0x80) == 0) result &= 0xF7;
}
else
{
if ((paddle1Pos & 0x01) == 0) result &= 0xFE;
if ((paddle1Pos & 0x02) == 0) result &= 0xFD;
if ((paddle1Pos & 0x04) == 0) result &= 0xFB;
if ((paddle1Pos & 0x08) == 0) result &= 0xF7;
}
if (c.IsPressed("P1 B1")) result &= 0xEF;
if (!pin) result &= 0xDF;
return result;
}
public byte Read_p1_c2(IController c)
{
byte result = 0xFF;
int paddle2Pos;
if (c.IsPressed("P2 Left"))
paddle2Pos = 0;
else if (c.IsPressed("P2 Right"))
paddle2Pos = 255;
else
paddle2Pos = (int)c.AxisValue("P2 Paddle");
if (pin)
{
if ((paddle2Pos & 0x10) == 0) result &= 0xBF;
if ((paddle2Pos & 0x20) == 0) result &= 0x7F;
}
else
{
if ((paddle2Pos & 0x01) == 0) result &= 0xBF;
if ((paddle2Pos & 0x02) == 0) result &= 0x7F;
}
return result;
}
public byte Read_p2_c1(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c2(IController c)
{
byte result = 0xFF;
int paddle2Pos;
if (c.IsPressed("P2 Left"))
paddle2Pos = 0;
else if (c.IsPressed("P2 Right"))
paddle2Pos = 255;
else
paddle2Pos = (int)c.AxisValue("P2 Paddle");
if (pin)
{
if ((paddle2Pos & 0x40) == 0) result &= 0xFE;
if ((paddle2Pos & 0x80) == 0) result &= 0xFD;
}
else
{
if ((paddle2Pos & 0x04) == 0) result &= 0xFE;
if ((paddle2Pos & 0x08) == 0) result &= 0xFD;
}
if (c.IsPressed("P2 B1")) result &= 0xFB;
if (!pin) result &= 0xF7;
return result;
}
public bool PinStateGet(IController c) { return pin; }
public void PinStateSet(IController c, bool val) { pin = val;}
public void CounterSet(IController c, int val) { }
public void RegionSet(IController c, bool val) { }
private static readonly string[] BaseDefinition =
{
"Left", "Right", "B1"
};
public void SyncState(Serializer ser)
{
ser.Sync(nameof(pin), ref pin);
}
}
[DisplayName("SMS Sports Pad Controller")]
public class SMSSportsPadController : IPort
{
public SMSSportsPadController(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "SMS Sports Pad Controller",
BoolButtons = BaseDefinition.Select(b => $"P{PortNum} {b}").ToList()
}.AddXYPair($"P{PortNum} {{0}}", AxisPairOrientation.RightAndUp, (-64).RangeTo(63), 0); //TODO verify direction against hardware
}
public int PortNum { get; }
private const int SportsPadMin = -64;
private const int SportsPadMax = 63;
public bool pin;
// further state value for sports pad, may be useful for other controllers in future
private int ControllerCounter = 3;
private bool is_JPN;
public ControllerDefinition Definition { get; }
public byte Read_p1_c1(IController c)
{
byte result = 0xFF;
int p1X;
if (c.IsPressed("P1 Left"))
p1X = SportsPadMin;
else if (c.IsPressed("P1 Right"))
p1X = SportsPadMax;
else
p1X = (int)c.AxisValue("P1 X");
int p1Y;
if (c.IsPressed("P1 Up"))
p1Y = SportsPadMin;
else if (c.IsPressed("P1 Down"))
p1Y = SportsPadMax;
else
p1Y = (int)c.AxisValue("P1 Y");
if (is_JPN)
{
p1X += 128;
p1Y += 128;
}
else
{
p1X *= -1;
p1Y *= -1;
}
// advance state
if (pin && (ControllerCounter % 2 == 0))
{
++ControllerCounter;
}
else if (!pin && (ControllerCounter % 2 == 1))
{
if (++ControllerCounter == (is_JPN ? 6 : 4))
ControllerCounter = 0;
}
switch (ControllerCounter)
{
case 0:
if ((p1X & 0x10) == 0) result &= 0xFE;
if ((p1X & 0x20) == 0) result &= 0xFD;
if ((p1X & 0x40) == 0) result &= 0xFB;
if ((p1X & 0x80) == 0) result &= 0xF7;
break;
case 1:
if ((p1X & 0x01) == 0) result &= 0xFE;
if ((p1X & 0x02) == 0) result &= 0xFD;
if ((p1X & 0x04) == 0) result &= 0xFB;
if ((p1X & 0x08) == 0) result &= 0xF7;
break;
case 2:
if ((p1Y & 0x10) == 0) result &= 0xFE;
if ((p1Y & 0x20) == 0) result &= 0xFD;
if ((p1Y & 0x40) == 0) result &= 0xFB;
if ((p1Y & 0x80) == 0) result &= 0xF7;
break;
case 3:
if ((p1Y & 0x01) == 0) result &= 0xFE;
if ((p1Y & 0x02) == 0) result &= 0xFD;
if ((p1Y & 0x04) == 0) result &= 0xFB;
if ((p1Y & 0x08) == 0) result &= 0xF7;
break;
case 4:
// specific to Japan: sync via TR
result &= 0xDF;
break;
case 5:
// specific to Japan: buttons
if (c.IsPressed("P1 B1")) result &= 0xFE;
if (c.IsPressed("P1 B2")) result &= 0xFD;
break;
}
if (is_JPN)
{
// Buttons like normal in Export
if (c.IsPressed("P1 B1")) result &= 0xEF;
if (c.IsPressed("P1 B2")) result &= 0xDF;
}
else
{
// In Japan, it contains selectHigh
if (!pin) result &= 0xEF;
}
return result;
}
public byte Read_p1_c2(IController c)
{
byte result = 0xFF;
int p2X;
if (c.IsPressed("P2 Left"))
p2X = SportsPadMin;
else if (c.IsPressed("P2 Right"))
p2X = SportsPadMax;
else
p2X = (int)c.AxisValue("P2 X");
int p2Y;
if (c.IsPressed("P2 Up"))
p2Y = SportsPadMin;
else if (c.IsPressed("P2 Down"))
p2Y = SportsPadMax;
else
p2Y = (int)c.AxisValue("P2 Y");
if (is_JPN)
{
p2X += 128;
p2Y += 128;
}
else
{
p2X *= -1;
p2Y *= -1;
}
if (pin && (ControllerCounter % 2 == 0))
{
++ControllerCounter;
}
else if (!pin && (ControllerCounter % 2 == 1))
{
if (++ControllerCounter == (is_JPN ? 6 : 4))
ControllerCounter = 0;
}
switch (ControllerCounter)
{
case 0:
if ((p2X & 0x10) == 0) result &= 0xBF;
if ((p2X & 0x20) == 0) result &= 0x7F;
break;
case 1:
if ((p2X & 0x01) == 0) result &= 0xBF;
if ((p2X & 0x02) == 0) result &= 0x7F;
break;
case 2:
if ((p2Y & 0x10) == 0) result &= 0xBF;
if ((p2Y & 0x20) == 0) result &= 0x7F;
break;
case 3:
if ((p2Y & 0x01) == 0) result &= 0xBF;
if ((p2Y & 0x02) == 0) result &= 0x7F;
break;
case 5:
// specific to Japan: buttons
if (c.IsPressed("P2 B1")) result &= 0xBF;
if (c.IsPressed("P2 B2")) result &= 0x7F;
break;
}
return result;
}
public byte Read_p2_c1(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c2(IController c)
{
byte result = 0xFF;
int p2X;
if (c.IsPressed("P2 Left"))
p2X = SportsPadMin;
else if (c.IsPressed("P2 Right"))
p2X = SportsPadMax;
else
p2X = (int)c.AxisValue("P2 X");
int p2Y;
if (c.IsPressed("P2 Down"))
p2Y = SportsPadMin;
else if (c.IsPressed("P2 Up"))
p2Y = SportsPadMax;
else
p2Y = (int)c.AxisValue("P2 Y");
if (is_JPN)
{
p2X += 128;
p2Y += 128;
}
else
{
p2X *= -1;
p2Y *= -1;
}
if (pin && (ControllerCounter % 2 == 0))
{
++ControllerCounter;
}
else if (!pin && (ControllerCounter % 2 == 1))
{
if (++ControllerCounter == (is_JPN ? 6 : 4))
ControllerCounter = 0;
}
switch (ControllerCounter)
{
case 0:
if ((p2X & 0x40) == 0) result &= 0xFE;
if ((p2X & 0x80) == 0) result &= 0xFD;
break;
case 1:
if ((p2X & 0x04) == 0) result &= 0xFE;
if ((p2X & 0x08) == 0) result &= 0xFD;
break;
case 2:
if ((p2Y & 0x40) == 0) result &= 0xFE;
if ((p2Y & 0x80) == 0) result &= 0xFD;
break;
case 3:
if ((p2Y & 0x04) == 0) result &= 0xFE;
if ((p2Y & 0x08) == 0) result &= 0xFD;
break;
}
if (is_JPN)
{
// Buttons like normal in Export
if (c.IsPressed("P2 B1")) result &= 0xFB;
if (c.IsPressed("P2 B2")) result &= 0xF7;
}
else
{
if (!pin) result &= 0xF7;
}
return result;
}
public bool PinStateGet(IController c) { return pin; }
public void PinStateSet(IController c, bool val) { pin = val; }
public void CounterSet(IController c, int val) { ControllerCounter = val; }
public void RegionSet(IController c, bool val) { is_JPN = val; }
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "B1", "B2"
};
public void SyncState(Serializer ser)
{
ser.Sync(nameof(pin), ref pin);
ser.Sync(nameof(ControllerCounter), ref ControllerCounter);
ser.Sync(nameof(is_JPN), ref is_JPN);
}
}
[DisplayName("SMS Light Phaser Controller")]
public class SMSLightPhaserController : IPort
{
public SMSLightPhaserController(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "SMS Light Phaser Controller",
BoolButtons = BaseDefinition.Select(b => $"P{PortNum} {b}").ToList()
}.AddXYPair($"P{PortNum} {{0}}", AxisPairOrientation.RightAndUp, 0.RangeTo(127), 64, 0.RangeTo(192), 96); //TODO verify direction against hardware
}
public int PortNum { get; }
public ControllerDefinition Definition { get; }
public byte Read_p1_c1(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0])) result &= 0xEF;
return result;
}
public byte Read_p1_c2(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c1(IController c)
{
byte result = 0xFF;
return result;
}
public byte Read_p2_c2(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0])) result &= 0xFB;
return result;
}
public bool PinStateGet(IController c) { return false; }
public void PinStateSet(IController c, bool val) { }
public void CounterSet(IController c, int val) { }
public void RegionSet(IController c, bool val) { }
private static readonly string[] BaseDefinition =
{
"Trigger"
};
public void SyncState(Serializer ser)
{
// nothing
}
}
}

View File

@ -2,22 +2,35 @@
using System.Collections.Generic;
using System.Drawing;
using BizHawk.Emulation.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
namespace BizHawk.Emulation.Cores
{
internal abstract class SmsSchemaControls
[Schema("GG")]
public class GameGearSchema : IVirtualPadSchema
{
public static PadSchema StandardController(int controller, bool isSms)
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core, Action<string> showMessageBox)
{
yield return GGSchemaControls.StandardController(1);
yield return GGSchemaControls.Console();
}
}
internal abstract class GGSchemaControls
{
public static PadSchema StandardController(int controller)
{
return new PadSchema
{
Size = new Size(174, 90),
Buttons = StandardButtons(controller, isSms)
Buttons = StandardButtons(controller)
};
}
public static IEnumerable<ButtonSchema> StandardButtons(int controller, bool isSms)
public static IEnumerable<ButtonSchema> StandardButtons(int controller)
{
yield return ButtonSchema.Up(14, 12, controller);
@ -26,52 +39,170 @@ namespace BizHawk.Emulation.Cores
yield return ButtonSchema.Right(24, 34, controller);
yield return new ButtonSchema(122, 34, controller, "B1", "1");
yield return new ButtonSchema(146, 34, controller, "B2", "2");
if (!isSms)
{
yield return new ButtonSchema(134, 12, controller, "Start", "S");
}
yield return new ButtonSchema(134, 12, controller, "Start", "S");
}
public static PadSchema Console(bool isSms)
public static PadSchema Console()
{
return new ConsoleSchema
{
Size = new Size(150, 50),
Buttons = ConsoleButtons(isSms)
Buttons = ConsoleButtons()
};
}
public static IEnumerable<ButtonSchema> ConsoleButtons(bool isSms)
public static IEnumerable<ButtonSchema> ConsoleButtons()
{
yield return new ButtonSchema(10, 15, "Reset");
if (isSms)
{
yield return new ButtonSchema(58, 15, "Pause");
}
}
}
[Schema("GG")]
public class GameGearSchema : IVirtualPadSchema
{
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core, Action<string> showMessageBox)
{
yield return SmsSchemaControls.StandardController(1, false);
yield return SmsSchemaControls.Console(false);
}
}
[Schema("SG")]
public class SG1000Schema : SMSSchema {} // are these really the same controller layouts? --yoshi
public class SG1000Schema : SMSSchema { } // are these really the same controller layouts? --yoshi
[Schema("SMS")]
public class SMSSchema : IVirtualPadSchema
{
private static string StandardControllerName => typeof(SmsController).DisplayName();
private static string PaddleControllerName => typeof(SMSPaddleController).DisplayName();
private static string SportControllerName => typeof(SMSSportsPadController).DisplayName();
private static string LightGunControllerName => typeof(SMSLightPhaserController).DisplayName();
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core, Action<string> showMessageBox)
{
yield return SmsSchemaControls.StandardController(1, true);
yield return SmsSchemaControls.StandardController(2, true);
yield return SmsSchemaControls.Console(true);
var ss = ((SMS)core).GetSyncSettings().Clone();
var port1 = SchemaFor(ss.Port1, 1);
if (port1 != null)
{
yield return port1;
}
var port2 = SchemaFor(ss.Port2, 2);
if (port2 != null)
{
yield return port2;
}
yield return ConsoleButtons();
}
private static PadSchema SchemaFor(SMSControllerTypes controllerName, int portNum)
{
if (controllerName == SMSControllerTypes.Standard)
{
return JoystickController(portNum);
}
if (controllerName == SMSControllerTypes.Paddle)
{
return PaddleController(portNum);
}
if (controllerName == SMSControllerTypes.SportsPad)
{
return SportController(portNum);
}
if (controllerName == SMSControllerTypes.Phaser)
{
return LightGunController(portNum);
}
return null;
}
private static PadSchema JoystickController(int controller)
{
return new PadSchema
{
DisplayName = $"Player {controller}",
Size = new Size(174, 90),
Buttons = new[]
{
ButtonSchema.Up(14, 12, controller),
ButtonSchema.Down(14, 56, controller),
ButtonSchema.Left(2, 34, controller),
ButtonSchema.Right(24, 34, controller),
new ButtonSchema(122, 34, controller, "B1", "1"),
new ButtonSchema(146, 34, controller, "B2", "2")
}
};
}
private static PadSchema PaddleController(int controller)
{
return new PadSchema
{
DisplayName = $"Player {controller}",
Size = new Size(334, 94),
Buttons = new PadSchemaControl[]
{
new ButtonSchema(5, 24, controller, "B1", "1"),
new SingleAxisSchema(55, 17, controller, "Paddle")
{
TargetSize = new Size(128, 69),
MaxValue = 255,
MinValue = 0
}
}
};
}
private static PadSchema SportController(int controller)
{
return new PadSchema
{
DisplayName = $"Player {controller}",
Size = new Size(334, 94),
Buttons = new PadSchemaControl[]
{
new ButtonSchema(5, 24, controller, "B1", "1"),
new ButtonSchema(5, 48, controller, "B2", "2"),
new SingleAxisSchema(55, 17, controller, "X")
{
TargetSize = new Size(128, 69),
MaxValue = 63,
MinValue = -64
},
new SingleAxisSchema(193, 17, controller, "Y")
{
TargetSize = new Size(128, 69),
MaxValue = 63,
MinValue = -64
}
}
};
}
private static PadSchema LightGunController(int controller)
{
return new PadSchema
{
DisplayName = "Light Gun",
Size = new Size(356, 290),
Buttons = new PadSchemaControl[]
{
new TargetedPairSchema(14, 17, $"P{controller} X")
{
TargetSize = new Size(127, 192)
},
new ButtonSchema(284, 17, controller, "Trigger")
}
};
}
private static PadSchema ConsoleButtons()
{
return new ConsoleSchema
{
Size = new Size(150, 50),
Buttons = new[]
{
new ButtonSchema(10, 15, "Reset"),
new ButtonSchema(60, 15, "Pause")
}
};
}
}
}