Add SMS Pause button for GPGX; do various cleanups/misc fixes

resolves #3942
This commit is contained in:
CasualPokePlayer 2024-06-09 17:51:54 -07:00
parent fb76975bd2
commit dc391721c8
15 changed files with 472 additions and 442 deletions

View File

@ -10,8 +10,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public void SetCDL(ICodeDataLog cdl)
{
CDL = cdl;
if (cdl == null) Core.gpgx_set_cd_callback(null);
else Core.gpgx_set_cd_callback(CDCallback);
Core.gpgx_set_cd_callback(cdl == null ? null : CDCallback);
}
public void NewCDL(ICodeDataLog cdl)
@ -21,7 +20,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
cdl["Z80 RAM"] = new byte[_memoryDomains["Z80 RAM"]!.Size];
var found = _memoryDomains["SRAM"];
if (found is not null) cdl["SRAM"] = new byte[found.Size];
if (found is not null)
{
cdl["SRAM"] = new byte[found.Size];
}
cdl.SubType = "GEN";
cdl.SubVer = 0;
@ -29,24 +31,29 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// TODO: we have Disassembling now
// not supported
public void DisassembleCDL(Stream s, ICodeDataLog cdl) { }
public void DisassembleCDL(Stream s, ICodeDataLog cdl)
{
}
private ICodeDataLog CDL;
private void CDCallbackProc(int addr, LibGPGX.CDLog_AddrType addrtype, LibGPGX.CDLog_Flags flags)
{
//TODO - hard reset makes CDL go nuts.
// TODO - hard reset makes CDL go nuts.
if (CDL == null) return;
if (!CDL.Active) return;
string key;
switch (addrtype)
if (CDL is not { Active: true })
{
case LibGPGX.CDLog_AddrType.MDCART: key = "MD CART"; break;
case LibGPGX.CDLog_AddrType.RAM68k: key = "68K RAM"; break;
case LibGPGX.CDLog_AddrType.RAMZ80: key = "Z80 RAM"; break;
case LibGPGX.CDLog_AddrType.SRAM: key = "SRAM"; break;
default: throw new InvalidOperationException("Lagrangian earwax incident");
return;
}
var key = addrtype switch
{
LibGPGX.CDLog_AddrType.MDCART => "MD CART",
LibGPGX.CDLog_AddrType.RAM68k => "68K RAM",
LibGPGX.CDLog_AddrType.RAMZ80 => "Z80 RAM",
LibGPGX.CDLog_AddrType.SRAM => "SRAM",
_ => throw new InvalidOperationException("Lagrangian earwax incident")
};
CDL[key][addr] |= (byte)flags;
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common;
@ -14,7 +15,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
var n = Core.gpgx_getregs(regs);
if (n > regs.Length)
{
throw new InvalidOperationException("A buffer overrun has occured!");
}
var ret = new Dictionary<string, RegisterValue>();
using (_elf.EnterExit())
{
@ -24,7 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var name = Marshal.PtrToStringAnsi(regs[i].Name);
byte size = 32;
if (name!.Contains("68K SR") || name.StartsWithOrdinal("Z80"))
{
size = 16;
}
ret[name] = new RegisterValue((ulong)regs[i].Value, size);
}
@ -35,9 +41,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[FeatureNotImplemented]
public void SetCpuRegister(string register, int value)
{
throw new NotImplementedException();
}
=> throw new NotImplementedException();
public IMemoryCallbackSystem MemoryCallbacks
{
@ -60,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[FeatureNotImplemented]
public long TotalExecutedCycles => throw new NotImplementedException();
private readonly MemoryCallbackSystem _memoryCallbacks = new(new[] { "M68K BUS" });
private readonly MemoryCallbackSystem _memoryCallbacks = new([ "M68K BUS" ]);
private LibGPGX.mem_cb ExecCallback;
private LibGPGX.mem_cb ReadCallback;
@ -105,8 +109,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
private void KillMemCallbacks()
{
Core.gpgx_set_mem_callback(null, null, null);
}
=> Core.gpgx_set_mem_callback(null, null, null);
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M68000;
@ -23,17 +24,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
_disassemblerInstance.ReadWord = (a) => (short)m.PeekUshort(a, m.EndianType == MemoryDomain.Endian.Big);
_disassemblerInstance.ReadByte = (a) => (sbyte)m.PeekByte(a);
_disassemblerInstance.ReadLong = (a) => (int)m.PeekUint(a, m.EndianType == MemoryDomain.Endian.Big);
_disassemblerInstance.ReadWord = a => (short)m.PeekUshort(a, m.EndianType == MemoryDomain.Endian.Big);
_disassemblerInstance.ReadByte = a => (sbyte)m.PeekByte(a);
_disassemblerInstance.ReadLong = a => (int)m.PeekUint(a, m.EndianType == MemoryDomain.Endian.Big);
var info = _disassemblerInstance.Disassemble((int)addr);
length = info.Length;
return $"{info.RawBytes.Substring(0, 4):X4} {info.Mnemonic,-7} {info.Args}";
return $"{info.RawBytes[..4]} {info.Mnemonic,-7} {info.Args}";
}
// TODO: refactor MC6800's disassembler to be a static call
private readonly MC68000 _disassemblerInstance = new MC68000();
private readonly MC68000 _disassemblerInstance = new();
}
}

View File

@ -1,4 +1,5 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
@ -12,32 +13,47 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
{
if (controller.IsPressed("Reset"))
{
Core.gpgx_reset(false);
}
if (controller.IsPressed("Power"))
{
Core.gpgx_reset(true);
}
if (_cds != null)
{
var prev = controller.IsPressed("Previous Disk");
var next = controller.IsPressed("Next Disk");
int newDisk = _discIndex;
var newDisk = _discIndex;
if (prev && !_prevDiskPressed)
{
newDisk--;
}
if (next && !_nextDiskPressed)
{
newDisk++;
}
_prevDiskPressed = prev;
_nextDiskPressed = next;
if (newDisk < -1)
{
newDisk = -1;
}
if (newDisk >= _cds.Length)
{
newDisk = _cds.Length - 1;
}
if (newDisk != _discIndex)
{
_discIndex = newDisk;
Core.gpgx_swap_disc(_discIndex == -1 ? null : GetCDDataStruct(_cds[_discIndex]));
Console.WriteLine("IMMA CHANGING MAH DISKS");
}
}
@ -47,10 +63,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
ControlConverter.ScreenWidth = _vwidth;
ControlConverter.ScreenHeight = _vheight;
ControlConverter.Convert(controller, input);
ControlConverter.Convert(controller, _input);
if (!Core.gpgx_put_control(input, inputsize))
if (controller.IsPressed("Pause"))
{
_input.pad[0] |= LibGPGX.INPUT_KEYS.INPUT_START;
}
if (!Core.gpgx_put_control(_input, _inputSize))
{
throw new Exception($"{nameof(Core.gpgx_put_control)}() failed!");
}
IsLagFrame = true;
@ -58,18 +81,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
Core.gpgx_advance();
if (render)
if (render)
{
UpdateVideo();
}
if (renderSound)
update_audio();
if (renderSound)
{
UpdateAudio();
}
if (IsLagFrame)
{
LagCount++;
}
if (_cds != null)
DriveLightOn = _driveLight;
{
DriveLightOn = _driveLight;
}
Frame++;
return true;
@ -93,9 +124,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
if (!_disposed)
{
_elf?.Dispose();
if (_cds != null)
{
foreach (var cd in _cds)
{
cd.Dispose();
}
}
_disposed = true;
}
}

View File

@ -12,9 +12,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private readonly LibGPGX.input_cb _inputCallback;
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
private readonly InputCallbackSystem _inputCallbacks = [ ];
private void input_callback()
private void InputCallback()
{
InputCallbacks.Call();
IsLagFrame = false;

View File

@ -22,7 +22,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var size = 0;
var pName = Core.gpgx_get_memdom(i, ref area, ref size);
if (area == IntPtr.Zero || pName == IntPtr.Zero || size == 0)
{
continue;
}
var name = Marshal.PtrToStringAnsi(pName)!;
// typically Genesis domains will be 2 bytes large (and thus big endian and byteswapped)
@ -32,96 +35,108 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
? MemoryDomain.Endian.Little
: MemoryDomain.Endian.Big;
if (name == "VRAM")
switch (name)
{
// vram pokes need to go through hook which invalidates cached tiles
var p = (byte*)area;
if (SystemId == VSystemID.Raw.GEN)
case "VRAM":
{
// Genesis has more VRAM, and GPGX byteswaps it
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
return p![addr ^ 1];
},
(addr, val) =>
{
if (addr is < 0 or > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_vram((int)addr ^ 1, val);
},
wordSize: 1));
}
else
{
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x3FFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
return p![addr];
},
(addr, val) =>
{
if (addr is < 0 or > 0x3FFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_vram((int)addr, val);
},
wordSize: 1));
}
}
else if (name == "CRAM")
{
var p = (byte*)area;
if (SystemId == VSystemID.Raw.GEN)
{
// CRAM for Genesis in the core is internally a different format than what it is natively
// this internal format isn't really useful, so let's convert it back
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x7F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
// vram pokes need to go through hook which invalidates cached tiles
var p = (byte*)area;
if (SystemId == VSystemID.Raw.GEN)
{
// Genesis has more VRAM, and GPGX byteswaps it
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
var c = *(ushort*)&p![addr & ~1];
c = (ushort)(((c & 0x1C0) << 3) | ((c & 0x038) << 2) | ((c & 0x007) << 1));
return (byte)((addr & 1) != 0 ? c & 0xFF : c >> 8);
}
},
(addr, val) =>
{
if (addr is < 0 or > 0x7F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_cram((int)addr, val);
},
wordSize: 2));
}
else
{
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x3F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
if (addr is < 0 or > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
return p![addr ^ 1];
},
(addr, val) =>
{
var c = *(ushort*)&p![addr & ~1];
return (byte)((addr & 1) != 0 ? c & 0xFF : c >> 8);
}
},
(addr, val) =>
{
if (addr is < 0 or > 0x3F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_cram((int)addr, val);
},
wordSize: 2));
if (addr is < 0 or > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_vram((int)addr ^ 1, val);
},
wordSize: 1));
}
else
{
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x3FFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
return p![addr];
},
(addr, val) =>
{
if (addr is < 0 or > 0x3FFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_vram((int)addr, val);
},
wordSize: 1));
}
break;
}
case "CRAM":
{
var p = (byte*)area;
if (SystemId == VSystemID.Raw.GEN)
{
// CRAM for Genesis in the core is internally a different format than what it is natively
// this internal format isn't really useful, so let's convert it back
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x7F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
{
var c = *(ushort*)&p![addr & ~1];
c = (ushort)(((c & 0x1C0) << 3) | ((c & 0x038) << 2) | ((c & 0x007) << 1));
return (byte)((addr & 1) != 0 ? c & 0xFF : c >> 8);
}
},
(addr, val) =>
{
if (addr is < 0 or > 0x7F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_cram((int)addr, val);
},
wordSize: 2));
}
else
{
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Big,
addr =>
{
if (addr is < 0 or > 0x3F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
using (_elf.EnterExit())
{
var c = *(ushort*)&p![addr & ~1];
return (byte)((addr & 1) != 0 ? c & 0xFF : c >> 8);
}
},
(addr, val) =>
{
if (addr is < 0 or > 0x3F) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
Core.gpgx_poke_cram((int)addr, val);
},
wordSize: 2));
}
break;
}
default:
{
if (oneByteWidth)
{
mm.Add(new MemoryDomainIntPtrMonitor(name, endian, area, size, true, 1, _elf));
}
else
{
mm.Add(new MemoryDomainIntPtrSwap16Monitor(name, endian, area, size, true, _elf));
}
break;
}
}
else if (oneByteWidth)
{
mm.Add(new MemoryDomainIntPtrMonitor(name, endian, area, size, true, 1, _elf));
}
else
{
mm.Add(new MemoryDomainIntPtrSwap16Monitor(name, endian, area, size, true, _elf));
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@ -9,14 +10,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public byte[] CloneSaveRam()
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
var size = 0;
var area = Core.gpgx_get_sram(ref size);
if (size == 0 || area == IntPtr.Zero)
{
return null;
}
byte[] ret = new byte[size];
var ret = new byte[size];
using (_elf.EnterExit())
{
Marshal.Copy(area, ret, 0, size);
}
return ret;
}
@ -27,6 +33,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// not sure how this is happening, but reject them
return;
}
if (!Core.gpgx_put_sram(data, data.Length))
{
throw new Exception("Core rejected saveram");
@ -37,8 +44,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
get
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
var size = 0;
var area = Core.gpgx_get_sram(ref size);
return size > 0 && area != IntPtr.Zero;
}
}

View File

@ -6,24 +6,19 @@ using BizHawk.Common;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public partial class GPGX : ISettable<GPGX.GPGXSettings, GPGX.GPGXSyncSettings>
{
public GPGXSettings GetSettings()
{
return _settings.Clone();
}
=> _settings.Clone();
public GPGXSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
=> _syncSettings.Clone();
public PutSettingsDirtyBits PutSettings(GPGXSettings o)
{
bool ret = GPGXSettings.NeedsReboot(_settings, o);
var ret = GPGXSettings.NeedsReboot(_settings, o);
_settings = o;
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
Core.gpgx_set_sprite_limit_enabled(!_settings.NoSpriteLimit);
@ -32,7 +27,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public PutSettingsDirtyBits PutSyncSettings(GPGXSyncSettings o)
{
bool ret = GPGXSyncSettings.NeedsReboot(_syncSettings, o);
var ret = GPGXSyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
@ -40,18 +35,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private class UintToHexConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
=> sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}
=> destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value.GetType() == typeof(uint))
if (destinationType == typeof(string) && value is uint)
{
return $"0x{value:x8}";
}
@ -61,13 +52,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value.GetType() == typeof(string))
if (value?.GetType() == typeof(string))
{
string input = (string)value;
var input = (string)value;
if (input.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
input = input.Substring(2);
input = input[2..];
}
return uint.Parse(input, NumberStyles.HexNumber, culture);
}
@ -78,18 +70,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private class UshortToHexConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
=> sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}
=> destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value.GetType() == typeof(ushort))
if (destinationType == typeof(string) && value is ushort)
{
return $"0x{value:x4}";
}
@ -99,13 +87,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value.GetType() == typeof(string))
if (value?.GetType() == typeof(string))
{
string input = (string)value;
var input = (string)value;
if (input.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
input = input.Substring(2);
input = input[2..];
}
return ushort.Parse(input, NumberStyles.HexNumber, culture);
}
@ -210,16 +199,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
set => _noSpriteLimit = value;
}
public GPGXSettings()
{
SettingsUtil.SetDefaultValues(this);
}
=> SettingsUtil.SetDefaultValues(this);
public GPGXSettings Clone()
{
return (GPGXSettings)MemberwiseClone();
}
=> (GPGXSettings)MemberwiseClone();
public LibGPGX.DrawMask GetDrawMask()
{
@ -233,11 +217,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
public static bool NeedsReboot(GPGXSettings x, GPGXSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
=> !DeepEquality.DeepEquals(x, y);
}
[CoreSettings]
@ -366,19 +346,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
public GPGXSyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
=> SettingsUtil.SetDefaultValues(this);
public GPGXSyncSettings Clone()
{
return (GPGXSyncSettings)MemberwiseClone();
}
=> (GPGXSyncSettings)MemberwiseClone();
public static bool NeedsReboot(GPGXSyncSettings x, GPGXSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
=> !DeepEquality.DeepEquals(x, y);
}
}
}

View File

@ -1,7 +1,8 @@
using System;
using BizHawk.Emulation.Common;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
@ -20,9 +21,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
public void DiscardSamples()
{
_nsamp = 0;
}
=> _nsamp = 0;
public void SetSyncMode(SyncSoundMode mode)
{
@ -30,23 +29,23 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
=> throw new InvalidOperationException("Async mode is not supported.");
private void update_audio()
private void UpdateAudio()
{
IntPtr src = IntPtr.Zero;
var src = IntPtr.Zero;
Core.gpgx_get_audio(ref _nsamp, ref src);
if (src != IntPtr.Zero)
{
using (_elf.EnterExit())
{
Marshal.Copy(src, _samples, 0, _nsamp * 2);
}
}
}
}

View File

@ -1,4 +1,5 @@
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
@ -20,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// any managed pointers that we sent to the core need to be resent now!
Core.gpgx_set_input_callback(_inputCallback);
RefreshMemCallbacks();
Core.gpgx_set_cdd_callback(cd_callback_handle);
Core.gpgx_set_cdd_callback(CDReadCallback);
Core.gpgx_invalidate_pattern_cache();
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
Core.gpgx_set_sprite_limit_enabled(!_settings.NoSpriteLimit);

View File

@ -9,17 +9,18 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
private readonly ITraceable _tracer;
public class GPGXTraceBuffer : CallbackBasedTraceBuffer
public class GPGXTraceBuffer(
IDebuggable debuggableCore,
IMemoryDomains memoryDomains,
IDisassemblable disassembler)
: CallbackBasedTraceBuffer(debuggableCore, memoryDomains, disassembler, TRACE_HEADER)
{
private const string TRACE_HEADER = "M68K: PC, machine code, mnemonic, operands, registers (D0-D7, A0-A7, SR, USP), flags (XNZVC)";
public GPGXTraceBuffer(IDebuggable debuggableCore, IMemoryDomains memoryDomains, IDisassemblable disassembler)
: base(debuggableCore, memoryDomains, disassembler, TRACE_HEADER) {}
protected override void TraceFromCallback(uint addr, uint value, uint flags)
{
var regs = DebuggableCore.GetCpuFlagsAndRegisters();
uint pc = (uint)regs["M68K PC"].Value;
var pc = (uint)regs["M68K PC"].Value;
var disasm = Disassembler.Disassemble(MemoryDomains.SystemBus, pc & 0xFFFFFF, out _);
var sb = new StringBuilder();
@ -36,6 +37,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
}
}
var sr = regs["M68K SR"].Value;
sb.Append(string.Concat(
(sr & 16) > 0 ? "X" : "x",

View File

@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public int VsyncDenominator { get; }
private int[] _vidBuff = Array.Empty<int>();
private int[] _vidBuff = [ ];
private int _vwidth;
private int _vheight;
@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
_vwidth = VirtualWidth;
_vheight = VirtualHeight;
_vidBuff = new int[_vwidth * _vheight];
for (int i = 0; i < _vidBuff.Length; i++)
for (var i = 0; i < _vidBuff.Length; i++)
{
_vidBuff[i] = unchecked((int)0xff000000);
}
@ -86,8 +86,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
using (_elf.EnterExit())
{
IntPtr src = IntPtr.Zero;
var src = IntPtr.Zero;
Core.gpgx_get_video(out var gpwidth, out var gpheight, out var gppitch, ref src);
_vwidth = gpwidth;
@ -96,31 +95,41 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
if (_settings.PadScreen320 && _vwidth < 320)
_vwidth = 320;
int xpad = (_vwidth - gpwidth) / 2;
int xpad2 = _vwidth - gpwidth - xpad;
var xpad = (_vwidth - gpwidth) / 2;
var xpad2 = _vwidth - gpwidth - xpad;
if (_vidBuff.Length < _vwidth * _vheight)
{
_vidBuff = new int[_vwidth * _vheight];
}
int rinc = (gppitch / 4) - gpwidth;
var rinc = (gppitch / 4) - gpwidth;
fixed (int* pdst_ = _vidBuff)
{
int* pdst = pdst_;
int* psrc = (int*)src;
var pdst = pdst_;
var psrc = (int*)src;
for (int j = 0; j < gpheight; j++)
for (var j = 0; j < gpheight; j++)
{
for (int i = 0; i < xpad; i++)
for (var i = 0; i < xpad; i++)
{
*pdst++ = unchecked((int)0xff000000);
for (int i = 0; i < gpwidth; i++)
}
for (var i = 0; i < gpwidth; i++)
{
*pdst++ = *psrc++;
for (int i = 0; i < xpad2; i++)
}
for (var i = 0; i < xpad2; i++)
{
*pdst++ = unchecked((int)0xff000000);
}
psrc += rinc;
}
}
}
}
}
}

View File

@ -1,13 +1,13 @@
using System;
using System.Runtime.InteropServices;
using System.Linq;
using BizHawk.BizInvoke;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
using BizHawk.Common;
using BizHawk.Emulation.DiscSystem;
using System.Linq;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
@ -25,11 +25,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[CoreConstructor(VSystemID.Raw.SG)]
public GPGX(CoreLoadParameters<GPGXSettings, GPGXSyncSettings> lp)
{
LoadCallback = load_archive;
_inputCallback = input_callback;
LoadCallback = LoadArchive;
_inputCallback = InputCallback;
InitMemCallbacks(); // ExecCallback, ReadCallback, WriteCallback
CDCallback = CDCallbackProc;
cd_callback_handle = CDRead;
CDReadCallback = CDRead;
ServiceProvider = new BasicServiceProvider(this);
// this can influence some things internally (autodetect romtype, etc)
@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// 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
//hack, don't use
// hack, don't use
if (lp.Roms.FirstOrDefault()?.RomData.Length > 32 * 1024 * 1024)
{
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
@ -75,7 +75,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var callingConventionAdapter = CallingConventionAdapters.MakeWaterbox(new Delegate[]
{
LoadCallback, _inputCallback, ExecCallback, ReadCallback, WriteCallback,
CDCallback, cd_callback_handle,
CDCallback, CDReadCallback,
}, _elf);
using (_elf.EnterExit())
@ -97,7 +97,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
_cds = lp.Discs.Select(d => d.DiscData).ToArray();
_cdReaders = _cds.Select(c => new DiscSectorReader(c)).ToArray();
Core.gpgx_set_cdd_callback(cd_callback_handle);
Core.gpgx_set_cdd_callback(CDReadCallback);
DriveLightEnabled = true;
}
@ -132,7 +132,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// and CdCallback
Core.gpgx_set_cdd_callback(null);
_elf.Seal();
Core.gpgx_set_cdd_callback(cd_callback_handle);
Core.gpgx_set_cdd_callback(CDReadCallback);
SetControllerDefinition();
@ -158,29 +158,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
_romfile = null;
}
private static LibGPGX.INPUT_SYSTEM SystemForSystem(ControlType c)
private static LibGPGX.INPUT_SYSTEM SystemForSystem(ControlType c) => c switch
{
switch (c)
{
default:
case ControlType.None:
return LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
case ControlType.Normal:
return LibGPGX.INPUT_SYSTEM.SYSTEM_GAMEPAD;
case ControlType.Xea1p:
return LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
case ControlType.Activator:
return LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
case ControlType.Teamplayer:
return LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
case ControlType.Wayplay:
return LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
case ControlType.Mouse:
return LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
case ControlType.Paddle:
return LibGPGX.INPUT_SYSTEM.SYSTEM_PADDLE;
}
}
ControlType.Normal => LibGPGX.INPUT_SYSTEM.SYSTEM_GAMEPAD,
ControlType.Xea1p => LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P,
ControlType.Activator => LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR,
ControlType.Teamplayer => LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER,
ControlType.Wayplay => LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY,
ControlType.Mouse => LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE,
ControlType.Paddle => LibGPGX.INPUT_SYSTEM.SYSTEM_PADDLE,
_ => LibGPGX.INPUT_SYSTEM.SYSTEM_NONE
};
private readonly LibGPGX Core;
private readonly WaterboxHost _elf;
@ -193,12 +181,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private readonly byte[] _romfile;
private bool _disposed = false;
private bool _disposed;
private LibGPGX.load_archive_cb LoadCallback;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly LibGPGX.load_archive_cb LoadCallback;
private bool _firmwareRequestFailed;
private readonly LibGPGX.InputData input = new LibGPGX.InputData();
private readonly LibGPGX.InputData _input = new();
public enum ControlType
{
@ -212,7 +201,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
Paddle,
}
/// <summary>
/// core callback for file loading
/// </summary>
@ -220,9 +208,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
/// <param name="buffer">buffer to load file to</param>
/// <param name="maxsize">maximum length buffer can hold</param>
/// <returns>actual size loaded, or 0 on failure</returns>
private int load_archive(string filename, IntPtr buffer, int maxsize)
private int LoadArchive(string filename, IntPtr buffer, int maxsize)
{
byte[] srcdata = null;
byte[] srcdata;
if (buffer == IntPtr.Zero)
{
@ -230,69 +218,66 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
return 0;
}
if (filename == "PRIMARY_ROM")
switch (filename)
{
if (_romfile == null)
{
case "PRIMARY_ROM" when _romfile == null:
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
return 0;
}
srcdata = _romfile;
}
else if (filename is "PRIMARY_CD" or "SECONDARY_CD")
{
if (filename == "PRIMARY_CD" && _romfile != null)
{
case "PRIMARY_ROM":
srcdata = _romfile;
break;
case ("PRIMARY_CD" or "SECONDARY_CD") and "PRIMARY_CD" when _romfile != null:
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
return 0;
}
else
case "PRIMARY_CD" or "SECONDARY_CD" when _cds == null:
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
return 0;
case "PRIMARY_CD" or "SECONDARY_CD":
{
if (_cds == null)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
return 0;
}
srcdata = GetCDData(_cds[0]);
if (srcdata.Length != maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size ({1} != {2}).", filename, srcdata.Length, maxsize);
return 0;
}
break;
}
}
else
{
// use fromtend firmware interface
FirmwareID? firmwareID = filename switch
{
"MD_BIOS" => new(system: VSystemID.Raw.GEN, firmware: "Boot"),
"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.GG, firmware: "Majesco"),
"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)
default:
{
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
srcdata = CoreComm.CoreFileProvider.GetFirmware(firmwareID.Value, "GPGX firmwares are usually required.");
if (srcdata == null)
// use fromtend firmware interface
FirmwareID? firmwareID = filename switch
{
_firmwareRequestFailed = true;
Console.WriteLine($"Frontend couldn't satisfy firmware request {firmwareID}");
"MD_BIOS" => new(system: VSystemID.Raw.GEN, firmware: "Boot"),
"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.GG, firmware: "Majesco"),
"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(firmwareID.Value, "GPGX firmwares are usually required.");
if (srcdata == null)
{
_firmwareRequestFailed = true;
Console.WriteLine($"Frontend couldn't satisfy firmware request {firmwareID}");
return 0;
}
}
else
{
Console.WriteLine("Unrecognized firmware request {0}", filename);
return 0;
}
}
else
{
Console.WriteLine("Unrecognized firmware request {0}", filename);
return 0;
break;
}
}
@ -303,18 +288,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
return 0;
}
else
{
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
return srcdata.Length;
}
}
else
{
throw new InvalidOperationException("Unknown error processing firmware");
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
return srcdata.Length;
}
throw new InvalidOperationException("Unknown error processing firmware");
}
private CoreComm CoreComm { get; }
@ -339,7 +319,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
}
private readonly LibGPGX.cd_read_cb cd_callback_handle;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly LibGPGX.cd_read_cb CDReadCallback;
public static LibGPGX.CDData GetCDDataStruct(Disc cd)
{
@ -397,61 +378,41 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
/// <summary>
/// size of native input struct
/// </summary>
private int inputsize;
private int _inputSize;
private GPGXControlConverter ControlConverter;
private void SetControllerDefinition()
{
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
if (!Core.gpgx_get_control(input, inputsize))
_inputSize = Marshal.SizeOf(typeof(LibGPGX.InputData));
if (!Core.gpgx_get_control(_input, _inputSize))
{
throw new Exception($"{nameof(Core.gpgx_get_control)}() failed");
}
ControlConverter = new(input, systemId: SystemId, cdButtons: _cds is not null);
ControlConverter = new(_input, systemId: SystemId, cdButtons: _cds is not null);
ControllerDefinition = ControlConverter.ControllerDef;
}
public LibGPGX.INPUT_DEVICE[] GetDevices()
{
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
}
=> (LibGPGX.INPUT_DEVICE[])_input.dev.Clone();
public bool IsMegaCD => _cds != null;
public class VDPView : IMonitor
public class VDPView(in LibGPGX.VDPView v, IMonitor m) : IMonitor
{
private readonly IMonitor _m;
public VDPView(in LibGPGX.VDPView v, IMonitor m)
{
_m = m;
VRAM = v.VRAM;
PatternCache = v.PatternCache;
ColorCache = v.ColorCache;
NTA = v.NTA;
NTB = v.NTB;
NTW = v.NTW;
}
public IntPtr VRAM;
public IntPtr PatternCache;
public IntPtr ColorCache;
public LibGPGX.VDPNameTable NTA;
public LibGPGX.VDPNameTable NTB;
public LibGPGX.VDPNameTable NTW;
public IntPtr VRAM = v.VRAM;
public IntPtr PatternCache = v.PatternCache;
public IntPtr ColorCache = v.ColorCache;
public LibGPGX.VDPNameTable NTA = v.NTA;
public LibGPGX.VDPNameTable NTB = v.NTB;
public LibGPGX.VDPNameTable NTW = v.NTW;
public void Enter()
{
_m.Enter();
}
=> m.Enter();
public void Exit()
{
_m.Exit();
}
=> m.Exit();
}
public VDPView UpdateVDPViewContext()
@ -462,14 +423,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
public int AddDeepFreezeValue(int address, byte value)
{
return Core.gpgx_add_deepfreeze_list_entry(address, value);
}
=> Core.gpgx_add_deepfreeze_list_entry(address, value);
public void ClearDeepFreezeList()
{
Core.gpgx_clear_deepfreeze_list();
}
=> Core.gpgx_clear_deepfreeze_list();
public DisplayType Region { get; }
}

View File

@ -10,123 +10,118 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
// this isn't all done
private struct CName
private readonly struct CName(string name, LibGPGX.INPUT_KEYS key)
{
public readonly string Name;
public readonly LibGPGX.INPUT_KEYS Key;
public CName(string name, LibGPGX.INPUT_KEYS key)
{
Name = name;
Key = key;
}
public readonly string Name = name;
public readonly LibGPGX.INPUT_KEYS Key = key;
}
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)
};
[
new("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new("B1", LibGPGX.INPUT_KEYS.INPUT_BUTTON1),
new("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),
};
[
new("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new("B1", LibGPGX.INPUT_KEYS.INPUT_BUTTON1),
new("B2", LibGPGX.INPUT_KEYS.INPUT_BUTTON2),
new("Start", LibGPGX.INPUT_KEYS.INPUT_START),
];
private static readonly CName[] Genesis3 =
{
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("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
};
[
new("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new("A", LibGPGX.INPUT_KEYS.INPUT_A),
new("B", LibGPGX.INPUT_KEYS.INPUT_B),
new("C", LibGPGX.INPUT_KEYS.INPUT_C),
new("Start", LibGPGX.INPUT_KEYS.INPUT_START),
];
private static readonly CName[] Genesis6 =
{
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("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
};
[
new("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new("A", LibGPGX.INPUT_KEYS.INPUT_A),
new("B", LibGPGX.INPUT_KEYS.INPUT_B),
new("C", LibGPGX.INPUT_KEYS.INPUT_C),
new("Start", LibGPGX.INPUT_KEYS.INPUT_START),
new("X", LibGPGX.INPUT_KEYS.INPUT_X),
new("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
new("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
new("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
];
private static readonly CName[] Mouse =
{
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
};
[
new("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
new("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
new("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
new("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
];
private static readonly CName[] Lightgun =
{
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
new CName("Lightgun B", LibGPGX.INPUT_KEYS.INPUT_MENACER_B),
new CName("Lightgun C", LibGPGX.INPUT_KEYS.INPUT_MENACER_C),
};
[
new("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
new("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
new("Lightgun B", LibGPGX.INPUT_KEYS.INPUT_MENACER_B),
new("Lightgun C", LibGPGX.INPUT_KEYS.INPUT_MENACER_C),
];
private static readonly CName[] Activator =
{
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
};
[
new("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
new("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
new("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
new("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
new("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
new("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
new("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
new("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
new("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
new("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
new("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
new("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
new("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
new("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
new("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
new("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
];
private static readonly CName[] Xea1P =
{
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
};
[
new("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
new("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
new("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
new("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
new("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
new("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
new("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
new("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
];
private static readonly CName[] Paddle =
{
[
new("B1", LibGPGX.INPUT_KEYS.INPUT_BUTTON1)
};
];
private LibGPGX.InputData _target;
private IController _source;
private readonly List<Action> _converts = new List<Action>();
private readonly List<Action> _converts = [ ];
public ControllerDefinition ControllerDef { get; }
@ -134,7 +129,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
foreach (var button in buttons)
{
string name = $"P{player} {button.Name}";
var name = $"P{player} {button.Name}";
ControllerDef.BoolButtons.Add(name);
var buttonFlag = button.Key;
_converts.Add(() =>
@ -207,25 +202,37 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public GPGXControlConverter(LibGPGX.InputData input, string systemId, bool cdButtons)
{
Console.WriteLine("Genesis Controller report:");
Console.WriteLine("GPGX Controller report:");
foreach (var e in input.system)
Console.WriteLine(" S:{0}", e);
foreach (var e in input.dev)
Console.WriteLine(" D:{0}", e);
int player = 1;
var player = 1;
ControllerDef = new("GPGX Genesis Controller");
ControllerDef = new(systemId switch
{
VSystemID.Raw.SMS or VSystemID.Raw.SG => "SMS Controller",
VSystemID.Raw.GG => "GG Controller",
VSystemID.Raw.GEN => "GPGX Genesis Controller", // GPGX in controller def name is more for backwards compat sake
_ => throw new InvalidOperationException(),
});
ControllerDef.BoolButtons.Add("Power");
ControllerDef.BoolButtons.Add("Reset");
if (systemId is VSystemID.Raw.SMS or VSystemID.Raw.SG)
{
ControllerDef.BoolButtons.Add("Pause");
}
if (cdButtons)
{
ControllerDef.BoolButtons.Add("Previous Disk");
ControllerDef.BoolButtons.Add("Next Disk");
}
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
for (var i = 0; i < LibGPGX.MAX_DEVICES; i++)
{
switch (input.dev[i])
{
@ -275,7 +282,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// PICO isn't finished on the unmanaged side either
throw new Exception("Sega PICO not implemented yet!");
default:
throw new Exception("Unknown Genesis control device! Something went wrong.");
throw new Exception("Unknown GPGX control device! Something went wrong.");
}
}
@ -299,6 +306,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
/// must be set for proper lightgun operation
/// </summary>
public int ScreenWidth { get; set; }
/// <summary>
/// must be set for proper lightgun operation
/// </summary>

View File

@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
#pragma warning disable IDE1006
#pragma warning disable CA1069
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
@ -49,12 +50,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public short LowGain;
public short MidGain;
public short HighGain;
public enum FilterType : byte
{
None = 0,
LowPass = 1,
ThreeBand = 2
}
public FilterType Filter;
public INPUT_SYSTEM InputSystemA;
@ -68,6 +71,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
YM2413_MAME,
YM2413_NUKED
}
public SMSFMSoundChipType SMSFMSoundChip;
public enum GenesisFMSoundChipType : byte
@ -78,6 +82,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
Nuked_YM2612,
Nuked_YM3438
}
public GenesisFMSoundChipType GenesisFMSoundChip;
public bool SpritesAlwaysOnTop;
@ -91,6 +96,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
Horizontal = 1 << 1,
All = Vertical | Horizontal,
}
public OverscanType Overscan;
public bool GGExtra;
}
@ -166,7 +172,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
DEVICE_ACTIVATOR = 0x0a,// Activator
}
public enum CDLog_AddrType
{
MDCART, RAM68k, RAMZ80, SRAM,
@ -183,7 +188,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
DMASource = 0x40,
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void input_cb();
@ -276,22 +280,27 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
/// <summary>
/// digital inputs
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
/// <summary>
/// analog (x/y)
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
public readonly short[] analog = new short[MAX_DEVICES * 2];
/// <summary>
/// gun horizontal offset
/// </summary>
public int x_offset;
/// <summary>
/// gun vertical offset
/// </summary>
@ -299,8 +308,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public void ClearAllBools()
{
for (int i = 0; i < pad.Length; i++)
for (var i = 0; i < pad.Length; i++)
{
pad[i] = 0;
}
}
}