Wiring up GPGX as core for SMS, GG, and SG (#3902)

* Adding GPGX as core for SMS and GG
* Enabling SMS FM selection
* Allow selecting PicoDrive as the preferred core for MD/Genesis

---------

Co-authored-by: James Groom <OSSYoshiRulz+GitHub@gmail.com>
Co-authored-by: feos <feykomylce@gmail.com>
This commit is contained in:
Sergio Martin 2024-04-28 19:10:44 +02:00 committed by GitHub
parent 8a0bf19869
commit 9dcb84336e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 110 additions and 33 deletions

2
.gitmodules vendored
View File

@ -76,4 +76,4 @@
[submodule "waterbox/gpgx/Genesis-Plus-GX"]
path = waterbox/gpgx/Genesis-Plus-GX
url = https://github.com/TASEmulators/Genesis-Plus-GX.git
branch = tasvideos-3
branch = tasvideos-2.1

Binary file not shown.

View File

@ -36,6 +36,10 @@ namespace BizHawk.Client.Common
new[] { CoreNames.Gambatte, CoreNames.Sameboy, CoreNames.GbHawk, CoreNames.SubGbHawk }),
(new[] { VSystemID.Raw.GBL },
new[] { CoreNames.GambatteLink, CoreNames.GBHawkLink, CoreNames.GBHawkLink3x, CoreNames.GBHawkLink4x }),
(new[] { VSystemID.Raw.GEN },
new[] { CoreNames.Gpgx, CoreNames.PicoDrive }),
(new[] { VSystemID.Raw.SMS, VSystemID.Raw.GG, VSystemID.Raw.SG },
new[] { CoreNames.Gpgx, CoreNames.SMSHawk }),
(new[] { VSystemID.Raw.PCE, VSystemID.Raw.PCECD, VSystemID.Raw.SGX, VSystemID.Raw.SGXCD },
new[] { CoreNames.TurboNyma, CoreNames.HyperNyma, CoreNames.PceHawk }),
(new[] { VSystemID.Raw.PSX },

View File

@ -88,7 +88,7 @@ namespace BizHawk.Client.Common.movie.import
: LibGPGX.INPUT_DEVICE.DEVICE_PAD3B;
}
var controlConverter = new GPGXControlConverter(input, false);
GPGXControlConverter controlConverter = new(input, systemId: VSystemID.Raw.GEN, cdButtons: false);
SimpleController controller = new(controlConverter.ControllerDef);

View File

@ -77,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public int Frame { get; private set; }
public string SystemId => VSystemID.Raw.GEN;
public string SystemId { get; }
public bool DeterministicEmulation => true;

View File

@ -243,7 +243,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[CoreSettings]
public class GPGXSyncSettings
{
[DisplayName("Use Six Button Controllers")]
[DisplayName("[Genesis/CD] Use Six Button Controllers")]
[Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")]
[DefaultValue(false)]
public bool UseSixButton { get; set; }
@ -263,7 +263,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[DefaultValue(LibGPGX.Region.Autodetect)]
public LibGPGX.Region Region { get; set; }
[DisplayName("FM Sound Chip Type")]
[DisplayName("[SMS/GG] Load BIOS")]
[Description("Indicates whether to load the system BIOS rom.")]
[DefaultValue(false)]
public bool loadBIOS { get; set; }
[DisplayName("[SMS] FM Sound Chip Type")]
[Description("Sets the method used to emulate the FM Sound Unit of the Sega Mark III/Master System. 'MAME' is fast and runs full speed on most systems.'Nuked' is cycle accurate, very high quality, and have substantial CPU requirements.")]
[DefaultValue(LibGPGX.InitSettings.SMSFMSoundChipType.YM2413_MAME)]
public LibGPGX.InitSettings.SMSFMSoundChipType SMSFMSoundChip { get; set; }
[DisplayName("[Genesis/CD] FM Sound Chip Type")]
[Description("Sets the method used to emulate the FM synthesizer (main sound generator) of the Mega Drive/Genesis. 'MAME' options are fast, and run full speed on most systems. 'Nuked' options are cycle accurate, very high quality, and have substantial CPU requirements. The 'YM2612' chip is used by the original Model 1 Mega Drive/Genesis. The 'YM3438' is used in later Mega Drive/Genesis revisions.")]
[DefaultValue(LibGPGX.InitSettings.GenesisFMSoundChipType.MAME_YM2612)]
public LibGPGX.InitSettings.GenesisFMSoundChipType GenesisFMSoundChip { get; set; }
@ -329,8 +339,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
InputSystemA = SystemForSystem(ControlTypeLeft),
InputSystemB = SystemForSystem(ControlTypeRight),
Region = Region,
loadBIOS = loadBIOS,
ForceSram = game["sram"],
GenesisFMSoundChip = GenesisFMSoundChip,
SMSFMSoundChip = SMSFMSoundChip,
GenesisFMSoundChip = GenesisFMSoundChip,
SpritesAlwaysOnTop = SpritesAlwaysOnTop
};
}

View File

@ -6,16 +6,25 @@ using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
using BizHawk.Common;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.DiscSystem;
using System.Linq;
using System.IO;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
[PortedCore(CoreNames.Gpgx, "Eke-Eke", "25a90c6", "https://github.com/ekeeke/Genesis-Plus-GX")]
[PortedCore(
name: CoreNames.Gpgx,
author: "Eke-Eke",
portedVersion: "0c45a8a",
portedUrl: "https://github.com/ekeeke/Genesis-Plus-GX")]
public partial class GPGX : IEmulator, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
[CoreConstructor(VSystemID.Raw.GEN)]
[CoreConstructor(VSystemID.Raw.SMS)]
[CoreConstructor(VSystemID.Raw.GG)]
[CoreConstructor(VSystemID.Raw.SG)]
public GPGX(CoreLoadParameters<GPGXSettings, GPGXSyncSettings> lp)
{
LoadCallback = load_archive;
@ -26,8 +35,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
ServiceProvider = new BasicServiceProvider(this);
// this can influence some things internally (autodetect romtype, etc)
string romextension = "GEN";
// Determining system ID from the rom. If no rom provided, assume Genesis (Sega CD)
SystemId = VSystemID.Raw.GEN;
var RomExtension = string.Empty;
if (lp.Roms.Count >= 1)
{
SystemId = lp.Roms[0].Game.System;
// We need to pass the exact file extension to GPGX for it to correctly interpret the console
RomExtension = Path.GetExtension(lp.Roms[0].RomPath).RemovePrefix('.');
}
// three or six button?
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
@ -82,7 +100,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
LibGPGX.INPUT_SYSTEM system_a = SystemForSystem(_syncSettings.ControlTypeLeft);
LibGPGX.INPUT_SYSTEM system_b = SystemForSystem(_syncSettings.ControlTypeRight);
var initResult = Core.gpgx_init(romextension, LoadCallback, _syncSettings.GetNativeSettings(lp.Game));
var initResult = Core.gpgx_init(RomExtension, LoadCallback, _syncSettings.GetNativeSettings(lp.Game));
if (!initResult)
throw new Exception($"{nameof(Core.gpgx_init)}() failed");
@ -228,22 +246,25 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
// use fromtend firmware interface
string firmwareID = null;
switch (filename)
{
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
default:
break;
}
FirmwareID? firmwareID = filename switch
{
"CD_BIOS_EU" => new(system: VSystemID.Raw.GEN, firmware: "CD_BIOS_EU"),
"CD_BIOS_JP" => new(system: VSystemID.Raw.GEN, firmware: "CD_BIOS_JP"),
"CD_BIOS_US" => new(system: VSystemID.Raw.GEN, firmware: "CD_BIOS_US"),
"GG_BIOS" => new(system: VSystemID.Raw.SMS, firmware: "Japan"),
"MS_BIOS_EU" => new(system: VSystemID.Raw.SMS, firmware: "Export"),
"MS_BIOS_JP" => new(system: VSystemID.Raw.SMS, firmware: "Japan"),
"MS_BIOS_US" => new(system: VSystemID.Raw.SMS, firmware: "Export"),
_ => null
};
if (firmwareID != null)
{
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
srcdata = CoreComm.CoreFileProvider.GetFirmware(new("GEN", firmwareID), "GPGX firmwares are usually required.");
srcdata = CoreComm.CoreFileProvider.GetFirmware(firmwareID.Value, "GPGX firmwares are usually required.");
if (srcdata == null)
{
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
Console.WriteLine($"Frontend couldn't satisfy firmware request {firmwareID}");
return 0;
}
}
@ -365,7 +386,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
if (!Core.gpgx_get_control(input, inputsize))
throw new Exception($"{nameof(Core.gpgx_get_control)}() failed");
ControlConverter = new GPGXControlConverter(input, _cds != null);
ControlConverter = new(input, systemId: SystemId, cdButtons: _cds is not null);
ControllerDefinition = ControlConverter.ControllerDef;
}

View File

@ -21,6 +21,27 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
}
private static readonly CName[] SMS2B =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("B1", LibGPGX.INPUT_KEYS.INPUT_BUTTON1),
new CName("B2", LibGPGX.INPUT_KEYS.INPUT_BUTTON2)
};
private static readonly CName[] GameGear =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("B1", LibGPGX.INPUT_KEYS.INPUT_BUTTON1),
new CName("B2", LibGPGX.INPUT_KEYS.INPUT_BUTTON2),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
};
private static readonly CName[] Genesis3 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
@ -169,7 +190,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
});
}
public GPGXControlConverter(LibGPGX.InputData input, bool cdButtons)
public GPGXControlConverter(LibGPGX.InputData input, string systemId, bool cdButtons)
{
Console.WriteLine("Genesis Controller report:");
foreach (var e in input.system)
@ -215,10 +236,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
AddToController(i, player, systemId is VSystemID.Raw.SMS ? SMS2B : GameGear);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
throw new Exception("Master System only device? Something went wrong.");
throw new Exception("Not implemented yet.");
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
AddToController(i, player, Activator);
player++;

View File

@ -54,6 +54,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public bool SixButton;
public bool ForceSram;
public enum SMSFMSoundChipType : byte
{
YM2413_DISABLED,
YM2413_MAME,
YM2413_NUKED
}
public SMSFMSoundChipType SMSFMSoundChip;
public enum GenesisFMSoundChipType : byte
{
MAME_YM2612,
@ -65,6 +73,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public GenesisFMSoundChipType GenesisFMSoundChip;
public bool SpritesAlwaysOnTop;
public bool loadBIOS;
}
[BizImport(CallingConvention.Cdecl)]

@ -1 +1 @@
Subproject commit 5ed4f70cf6831331b110357d23495d08f46b2a87
Subproject commit 1cc182153291ee69e15c6ad2dc2cb51492b39c58

View File

@ -619,8 +619,10 @@ struct InitSettings
char InputSystemB;
char SixButton;
char ForceSram;
uint8_t SMSFMSoundChip;
uint8_t GenesisFMSoundChip;
uint8_t SpritesAlwaysOnTop;
uint8_t loadBios;
};
@ -704,6 +706,8 @@ void bk_cpu_hook(hook_type_t type, int width, unsigned int address, unsigned int
break;
}
default: break;
}
}
@ -755,8 +759,7 @@ GPGX_EX int gpgx_init(const char* feromextension,
// Selecting FM Sound chip to use for SMS / GG emulation. Using a default for now, until we also
// accept this core for SMS/GG emulation in BizHawk
int smsFMChipType = YM2413_NUKED;
switch (smsFMChipType)
switch (settings->SMSFMSoundChip)
{
case YM2413_DISABLED:
config.opll = 0;
@ -770,7 +773,7 @@ GPGX_EX int gpgx_init(const char* feromextension,
case YM2413_NUKED:
config.opll = 1;
config.ym2413 = 0;
config.ym2413 = 1;
break;
}
@ -810,7 +813,7 @@ GPGX_EX int gpgx_init(const char* feromextension,
config.master_clock = 0; /* = AUTO (1 = NTSC, 2 = PAL) */
config.force_dtack = 0;
config.addr_error = 1;
config.bios = 0;
config.bios = settings->loadBios;
config.lock_on = 0; /* = OFF (or TYPE_SK, TYPE_GG & TYPE_AR) */
config.add_on = 0; /* = HW_ADDON_AUTO (or HW_ADDON_MEGACD, HW_ADDON_MEGASD & HW_ADDON_ONE) */
config.cd_latency = 1;
@ -836,13 +839,16 @@ GPGX_EX int gpgx_init(const char* feromextension,
cinterface_custom_backdrop_color = settings->BackdropColor;
// Default: Genesis
// apparently, the only part of config.input used is the padtype identifier,
// and that's used only for choosing pad type when system_md
{
int i;
for (i = 0; i < MAX_INPUTS; i++)
config.input[i].padtype = settings->SixButton ? DEVICE_PAD6B : DEVICE_PAD3B;
}
for (int i = 0; i < MAX_INPUTS; i++)
config.input[i].padtype = settings->SixButton ? DEVICE_PAD6B : DEVICE_PAD3B;
// Hacky but effective. Setting the correct controller type here if this is sms or GG
if (system_hw == SYSTEM_SMS || system_hw == SYSTEM_SMS2 || system_hw == SYSTEM_GG || system_hw == SYSTEM_SG)
for (int i = 0; i < MAX_INPUTS; i++)
config.input[i].padtype = DEVICE_PAD2B;
// first try to load our main CD
if (!load_rom("PRIMARY_CD"))

View File

@ -92,3 +92,4 @@ extern char MS_BIOS_JP[256];
extern void osd_input_update(void);
extern int load_archive(const char *filename, unsigned char *buffer, int maxsize, char *extension);
extern void real_input_callback(void);