Fixes/cleanups C# for GPGX (mainly for SMS/GG mode)

This commit is contained in:
CasualPokePlayer 2024-04-30 20:07:59 -07:00
parent 1b742cf356
commit e9af682975
6 changed files with 194 additions and 96 deletions

View File

@ -280,6 +280,8 @@ namespace BizHawk.Emulation.Common
var jp_mcd_beta = File("F30D109D1C2F7C9FEAF38600C65834261DB73D1F", 131072, "MCD_jp_beta.bin", "Mega CD JP (Beta)");
var eu_mcd_221 = File("9DE4EDA59F544DB2D5FD7E6514601F7B648D8EB4", 131072, "MCD_eu_221.bin", "Mega CD EU (v2.21)");
FirmwareAndOption("1C470A9A8D0B211C5FEEA1C1C2376AA1F7934B16", 4096, "GEN", "TMSS", "TMSS.md", "Mega Drive TMSS Boot Rom (Japan)");
Firmware("GEN", "CD_BIOS_EU", "Mega CD Bios (Europe)");
Firmware("GEN", "CD_BIOS_JP", "Mega CD Bios (Japan)");
Firmware("GEN", "CD_BIOS_US", "Sega CD Bios (USA)");

View File

@ -11,20 +11,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
int n = Core.gpgx_getregs(regs);
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())
{
for (int i = 0; i < n; i++)
for (var i = 0; i < n; i++)
{
// el hacko
string name = Marshal.PtrToStringAnsi(regs[i].Name);
var name = Marshal.PtrToStringAnsi(regs[i].Name);
byte size = 32;
if (name.Contains("68K SR") || name.StartsWithOrdinal("Z80"))
if (name!.Contains("68K SR") || name.StartsWithOrdinal("Z80"))
size = 16;
ret[name] = new RegisterValue((ulong)regs[i].Value, size);
@ -40,17 +39,28 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
throw new NotImplementedException();
}
public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks;
public IMemoryCallbackSystem MemoryCallbacks
{
get
{
if (SystemId == VSystemID.Raw.GEN)
{
return _memoryCallbacks;
}
public bool CanStep(StepType type) { return false; }
throw new NotImplementedException();
}
}
public bool CanStep(StepType type) => false;
[FeatureNotImplemented]
public void Step(StepType type) { throw new NotImplementedException(); }
public void Step(StepType type) => throw new NotImplementedException();
[FeatureNotImplemented]
public long TotalExecutedCycles => throw new NotImplementedException();
private readonly MemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem(new[] { "M68K BUS" });
private readonly MemoryCallbackSystem _memoryCallbacks = new(new[] { "M68K BUS" });
private LibGPGX.mem_cb ExecCallback;
private LibGPGX.mem_cb ReadCallback;
@ -59,30 +69,30 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private void InitMemCallbacks()
{
ExecCallback = new LibGPGX.mem_cb(a =>
ExecCallback = a =>
{
if (MemoryCallbacks.HasExecutes)
{
uint flags = (uint)MemoryCallbackFlags.AccessExecute;
const uint flags = (uint)MemoryCallbackFlags.AccessExecute;
MemoryCallbacks.CallMemoryCallbacks(a, 0, flags, "M68K BUS");
}
});
ReadCallback = new LibGPGX.mem_cb(a =>
};
ReadCallback = a =>
{
if (MemoryCallbacks.HasReads)
{
uint flags = (uint)MemoryCallbackFlags.AccessRead;
const uint flags = (uint)MemoryCallbackFlags.AccessRead;
MemoryCallbacks.CallMemoryCallbacks(a, 0, flags, "M68K BUS");
}
});
WriteCallback = new LibGPGX.mem_cb(a =>
};
WriteCallback = a =>
{
if (MemoryCallbacks.HasWrites)
{
uint flags = (uint)MemoryCallbackFlags.AccessWrite;
const uint flags = (uint)MemoryCallbackFlags.AccessWrite;
MemoryCallbacks.CallMemoryCallbacks(a, 0, flags, "M68K BUS");
}
});
};
_memoryCallbacks.ActiveChanged += RefreshMemCallbacks;
}

View File

@ -25,7 +25,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
continue;
var name = Marshal.PtrToStringAnsi(pName)!;
var endian = name == "Z80 RAM"
// typically Genesis domains will be 2 bytes large (and thus big endian and byteswapped)
var oneByteWidth = name is "Z80 RAM" or "Main RAM" or "ROM" or "Cart (Volatile) RAM" or "SRAM";
var endian = oneByteWidth
? MemoryDomain.Endian.Little
: MemoryDomain.Endian.Big;
@ -33,44 +36,86 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
// vram pokes need to go through hook which invalidates cached tiles
var p = (byte*)area;
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: 2));
if (SystemId == VSystemID.Raw.GEN)
{
// 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")
{
// CRAM 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
var p = (byte*)area;
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())
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 =>
{
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));
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));
}
}
else if (name.Contains("Z80"))
else if (oneByteWidth)
{
mm.Add(new MemoryDomainIntPtrMonitor(name, endian, area, size, true, 1, _elf));
}
@ -79,44 +124,64 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
mm.Add(new MemoryDomainIntPtrSwap16Monitor(name, endian, area, size, true, _elf));
}
}
var m68Bus = new MemoryDomainDelegate("M68K BUS", 0x1000000, MemoryDomain.Endian.Big,
addr =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
return Core.gpgx_peek_m68k_bus(a);
},
(addr, val) =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
Core.gpgx_write_m68k_bus(a, val);
}, 2);
mm.Add(m68Bus);
if (IsMegaCD)
MemoryDomain systemBus;
if (SystemId == VSystemID.Raw.GEN)
{
var s68Bus = new MemoryDomainDelegate("S68K BUS", 0x1000000, MemoryDomain.Endian.Big,
addr =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
return Core.gpgx_peek_s68k_bus(a);
},
(addr, val) =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
Core.gpgx_write_s68k_bus(a, val);
}, 2);
systemBus = new MemoryDomainDelegate("M68K BUS", 0x1000000, MemoryDomain.Endian.Big,
addr =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
return Core.gpgx_peek_m68k_bus(a);
},
(addr, val) =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
Core.gpgx_write_m68k_bus(a, val);
}, 2);
mm.Add(systemBus);
mm.Add(s68Bus);
if (IsMegaCD)
{
var s68Bus = new MemoryDomainDelegate("S68K BUS", 0x1000000, MemoryDomain.Endian.Big,
addr =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
return Core.gpgx_peek_s68k_bus(a);
},
(addr, val) =>
{
var a = (uint)addr;
if (a > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
Core.gpgx_write_s68k_bus(a, val);
}, 2);
mm.Add(s68Bus);
}
}
else
{
systemBus = new MemoryDomainDelegate("Z80 BUS", 0x10000, MemoryDomain.Endian.Little,
addr =>
{
var a = (uint)addr;
if (a > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
return Core.gpgx_peek_z80_bus(a);
},
(addr, val) =>
{
var a = (uint)addr;
if (a > 0xFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
Core.gpgx_write_z80_bus(a, val);
}, 1);
}
mm.Add(_elf.GetPagesDomain());
_memoryDomains = new MemoryDomainList(mm) { SystemBus = m68Bus };
mm.Add(_elf.GetPagesDomain());
_memoryDomains = new MemoryDomainList(mm) { SystemBus = systemBus };
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);
}
}

View File

@ -263,7 +263,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[DefaultValue(LibGPGX.Region.Autodetect)]
public LibGPGX.Region Region { get; set; }
[DisplayName("[SMS/GG] Load BIOS")]
[DisplayName("Load BIOS")]
[Description("Indicates whether to load the system BIOS rom.")]
[DefaultValue(false)]
public bool LoadBIOS { get; set; }

View File

@ -104,6 +104,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var initSettings = _syncSettings.GetNativeSettings(lp.Game);
var initResult = Core.gpgx_init(romExtension, LoadCallback, ref initSettings);
// if a firmware request failed and we're recording a movie, fail now
// we should do this as to enforce the sync settings of the movie
// init might still work fine, so don't throw for more casual users
if (_firmwareRequestFailed && lp.DeterministicEmulationRequested)
{
throw new MissingFirmwareException("A GPGX firmware request failed in deterministic mode.");
}
if (!initResult)
{
throw new Exception($"{nameof(Core.gpgx_init)}() failed");
@ -183,6 +191,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private bool _disposed = false;
private LibGPGX.load_archive_cb LoadCallback;
private bool _firmwareRequestFailed;
private readonly LibGPGX.InputData input = new LibGPGX.InputData();
@ -252,6 +261,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
FirmwareID? firmwareID = filename switch
{
"MD_BIOS" => new(system: VSystemID.Raw.GEN, firmware: "TMSS"),
"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"),
@ -268,6 +278,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
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;
}
@ -407,7 +418,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
private readonly IMonitor _m;
public VDPView(LibGPGX.VDPView v, IMonitor m)
public VDPView(in LibGPGX.VDPView v, IMonitor m)
{
_m = m;
VRAM = v.VRAM;
@ -439,12 +450,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public VDPView UpdateVDPViewContext()
{
var v = new LibGPGX.VDPView();
Core.gpgx_get_vdp_view(v);
Core.gpgx_get_vdp_view(out var v);
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
return new VDPView(v, _elf);
return new VDPView(in v, _elf);
}
public int AddDeepFreezeValue(int address, byte value)
{
return Core.gpgx_add_deepfreeze_list_entry(address, value);

View File

@ -98,6 +98,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_get_control([Out]InputData dest, int bytes);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_put_control([In]InputData src, int bytes);
@ -331,7 +332,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract void gpgx_swap_disc(CDData toc);
public abstract void gpgx_swap_disc([In] CDData toc);
[StructLayout(LayoutKind.Sequential)]
public struct VDPNameTable
@ -342,7 +343,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
}
[StructLayout(LayoutKind.Sequential)]
public class VDPView
public struct VDPView
{
public IntPtr VRAM;
public IntPtr PatternCache;
@ -352,8 +353,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public VDPNameTable NTW;
}
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract void gpgx_get_vdp_view([Out] VDPView view);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_vdp_view(out VDPView view);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_poke_cram(int addr, byte value);
@ -383,8 +384,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[BizImport(CallingConvention.Cdecl)]
public abstract int gpgx_getmaxnumregs();
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract int gpgx_getregs([Out] RegisterInfo[] regs);
[BizImport(CallingConvention.Cdecl)]
public abstract int gpgx_getregs(RegisterInfo[] regs);
[Flags]
public enum DrawMask : int
@ -398,15 +399,25 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_draw_mask(DrawMask mask);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_sprite_limit_enabled(bool enabled);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_z80_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_m68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_s68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_z80_bus(uint addr);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_m68k_bus(uint addr);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_s68k_bus(uint addr);
}