[Gambatte] Many Updates (#2823)

* [Gambatte] Port setregs from upstream, implement SetCpuRegister, misc cleanup/fixes elsewhere

* [Gambatte] Add more proper uninitalized AGB WRAM, based of a GBP dump

* cleanup Gambatte's CpuSetRegister (credits to stringflow)

* additional Gambatte code cleanup

* [Gambatte] cleanup tracelogger code readability

* fix

* Initalize VideoBuffer with a white screen instead of a black screen, misc video fixes

* misc cleanup

* [Gambatte] hdma work

* [Gambatte] add warning for recording a movie without a bios enabled. doing this as I am annoyed enough from disabled bios being the default and you cannot say anything about the profile system since recent submissions have shown that ineffective

* misc cleanup

* [Gambatte] init state tweaks

* [Gambatte] add setting for patching similar bioses

* fix this

Co-authored-by: TiKevin83 <travismcgeehan@gmail.com>
Co-authored-by: alyosha-tas <alexei.f.k@gmail.com>
This commit is contained in:
CasualPokePlayer 2021-08-01 06:54:19 -07:00 committed by GitHub
parent 4973e3eba1
commit 7518ce962b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 126 additions and 102 deletions

Binary file not shown.

View File

@ -14,21 +14,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
return new Dictionary<string, RegisterValue>
{
["PC"] = (ushort)(data[(int)LibGambatte.RegIndicies.PC] & 0xffff),
["SP"] = (ushort)(data[(int)LibGambatte.RegIndicies.SP] & 0xffff),
["A"] = (byte)(data[(int)LibGambatte.RegIndicies.A] & 0xff),
["B"] = (byte)(data[(int)LibGambatte.RegIndicies.B] & 0xff),
["C"] = (byte)(data[(int)LibGambatte.RegIndicies.C] & 0xff),
["D"] = (byte)(data[(int)LibGambatte.RegIndicies.D] & 0xff),
["E"] = (byte)(data[(int)LibGambatte.RegIndicies.E] & 0xff),
["F"] = (byte)(data[(int)LibGambatte.RegIndicies.F] & 0xff),
["H"] = (byte)(data[(int)LibGambatte.RegIndicies.H] & 0xff),
["L"] = (byte)(data[(int)LibGambatte.RegIndicies.L] & 0xff)
["PC"] = (ushort)(data[(int)LibGambatte.RegIndices.PC] & 0xffff),
["SP"] = (ushort)(data[(int)LibGambatte.RegIndices.SP] & 0xffff),
["A"] = (byte)(data[(int)LibGambatte.RegIndices.A] & 0xff),
["B"] = (byte)(data[(int)LibGambatte.RegIndices.B] & 0xff),
["C"] = (byte)(data[(int)LibGambatte.RegIndices.C] & 0xff),
["D"] = (byte)(data[(int)LibGambatte.RegIndices.D] & 0xff),
["E"] = (byte)(data[(int)LibGambatte.RegIndices.E] & 0xff),
["F"] = (byte)(data[(int)LibGambatte.RegIndices.F] & 0xff),
["H"] = (byte)(data[(int)LibGambatte.RegIndices.H] & 0xff),
["L"] = (byte)(data[(int)LibGambatte.RegIndices.L] & 0xff)
};
}
[FeatureNotImplemented]
public void SetCpuRegister(string register, int value) => throw new NotImplementedException();
public void SetCpuRegister(string register, int value)
{
int[] data = new int[10];
LibGambatte.gambatte_getregs(GambatteState, data);
LibGambatte.RegIndices index = (LibGambatte.RegIndices)Enum.Parse(typeof(LibGambatte.RegIndices), register);
data[(int)index] = value & (index <= LibGambatte.RegIndices.SP ? 0xffff : 0xff);
LibGambatte.gambatte_setregs(GambatteState, data);
}
public bool CanStep(StepType type) => false;
@ -37,46 +43,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public long TotalExecutedCycles => Math.Max((long)_cycleCount, (long)callbackCycleCount);
private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
private const string systemBusScope = "System Bus";
private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { systemBusScope });
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
private LibGambatte.MemoryCallback _readcb;
private LibGambatte.MemoryCallback _writecb;
private LibGambatte.MemoryCallback _execcb;
private void ReadCallback(uint address, ulong cycleOffset)
{
callbackCycleCount = _cycleCount + cycleOffset;
if (MemoryCallbacks.HasReads)
{
uint flags = (uint)MemoryCallbackFlags.AccessRead;
MemoryCallbacks.CallMemoryCallbacks(address, 0, flags, "System Bus");
}
}
private void WriteCallback(uint address, ulong cycleOffset)
{
callbackCycleCount = _cycleCount + cycleOffset;
if (MemoryCallbacks.HasWrites)
{
uint flags = (uint)MemoryCallbackFlags.AccessWrite;
MemoryCallbacks.CallMemoryCallbacks(address, 0, flags,"System Bus");
}
}
private void ExecCallback(uint address, ulong cycleOffset)
{
callbackCycleCount = _cycleCount + cycleOffset;
if (MemoryCallbacks.HasExecutes)
{
uint flags = (uint)MemoryCallbackFlags.AccessExecute;
MemoryCallbacks.CallMemoryCallbacks(address, 0, flags, "System Bus");
}
}
/// <summary>
/// for use in dual core
/// </summary>
@ -87,19 +62,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private void InitMemoryCallbacks()
{
_readcb = new LibGambatte.MemoryCallback(ReadCallback);
_writecb = new LibGambatte.MemoryCallback(WriteCallback);
_execcb = new LibGambatte.MemoryCallback(ExecCallback);
_memorycallbacks.ActiveChanged += RefreshMemoryCallbacks;
}
LibGambatte.MemoryCallback CreateCallback(MemoryCallbackFlags flags, Func<bool> getHasCBOfType)
{
var rawFlags = (uint)flags;
return (address, cycleOffset) =>
{
callbackCycleCount = _cycleCount + cycleOffset;
if (getHasCBOfType()) MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, systemBusScope);
};
}
private void RefreshMemoryCallbacks()
{
var mcs = MemoryCallbacks;
_readcb = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads);
_writecb = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites);
_execcb = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes);
LibGambatte.gambatte_setreadcallback(GambatteState, mcs.HasReads ? _readcb : null);
LibGambatte.gambatte_setwritecallback(GambatteState, mcs.HasWrites ? _writecb : null);
LibGambatte.gambatte_setexeccallback(GambatteState, mcs.HasExecutes ? _execcb : null);
_memorycallbacks.ActiveChanged += () =>
{
LibGambatte.gambatte_setreadcallback(GambatteState, MemoryCallbacks.HasReads ? _readcb : null);
LibGambatte.gambatte_setwritecallback(GambatteState, MemoryCallbacks.HasWrites ? _writecb : null);
LibGambatte.gambatte_setexeccallback(GambatteState, MemoryCallbacks.HasExecutes ? _execcb : null);
};
}
}
}

View File

@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
case GambatteSyncSettings.FrameLengthType.UserDefinedFrames:
while (true)
{
// target number of samples to emit: input length minus whatever overflow
// target number of samples to emit: input length
float inputFrameLength = controller.AxisValue("Input Length");
uint inputFrameLengthInt = (uint)Math.Floor(inputFrameLength);
if (inputFrameLengthInt == 0)
@ -89,7 +89,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (frameOverflow >= inputFrameLengthInt)
{
frameOverflow -= inputFrameLengthInt;
frameOverflow = 0;
break;
}
}

View File

@ -100,6 +100,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[DefaultValue(false)]
public bool EnableBIOS { get; set; }
[DisplayName("Patch Similar BootROMs")]
[Description("If true, BootROMs (or \"BIOSes\") are patched a similar other. GBC is patched to GBA. DMG is patched to MGB (and vice versa). Should not be used for unofficial BootROMs. Ignored (treated as false) when booting without a BIOS.")]
[DefaultValue(false)]
public bool PatchBIOS { get; set; }
public enum ConsoleModeType
{
Auto,

View File

@ -14,6 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
int[] s = new int[14];
System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 14);
ushort PC = (ushort)s[1];
Tracer.Put(new(
disassembly: LR35902.Disassemble(
@ -34,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
s[8] & 0xff,
s[9] & 0xff,
s[10] & 0xff,
s[11] != 0 ? "skip" : "",
s[11] != 0 ? "prefetched" : "",
s[12] & 0xffffff,
s[13] & 0xff)));
}

View File

@ -5,7 +5,17 @@
/// <summary>
/// stored image of most recent frame
/// </summary>
private readonly int[] VideoBuffer = new int[160 * 144];
private readonly int[] VideoBuffer = CreateVideoBuffer();
private static int[] CreateVideoBuffer()
{
var b = new int[160 * 144];
for (int i = 0; i < (160 * 144); i++)
{
b[i] = -1; // GB/C screen is disabled on bootup, so it always starts as white, not black
}
return b;
}
public int[] GetVideoBuffer()
{

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
@ -45,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
try
{
_syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();
_syncSettings = syncSettings ?? new GambatteSyncSettings();
LibGambatte.LoadFlags flags = 0;
@ -73,22 +74,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
byte[] bios;
string biosSystemId;
string biosId;
if ((flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE)
{
biosSystemId = "GBC";
biosId = _syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World";
IsCgb = true;
}
else
{
biosSystemId = "GB";
biosId = "World";
IsCgb = false;
}
IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE;
biosSystemId = IsCgb ? "GBC" : "GB";
biosId = ((_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA) && !_syncSettings.PatchBIOS) ? "AGB" : "World";
if (_syncSettings.EnableBIOS)
{
bios = comm.CoreFileProvider.GetFirmwareOrThrow(new(biosSystemId, biosId), "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS.");
if (_syncSettings.PatchBIOS)
{
if (!IsCgb)
{
bios[0xFD] ^= 0xFE; // patch from dmg<->mgb
}
else if (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA)
{
// patch from cgb->agb re
bios[0xF3] ^= 0x03;
for (var i = 0xF5; i < 0xFB;)
{
bios[i] = bios[++i];
}
bios[0xFB] ^= 0x74;
}
}
if (LibGambatte.gambatte_loadbios(GambatteState, bios, (uint)bios.Length) != 0)
{
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
@ -96,6 +106,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
else
{
if (DeterministicEmulation) // throw a warning if a movie is being recorded with the bios disabled
{
comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS.");
}
flags |= LibGambatte.LoadFlags.NO_BIOS;
}
@ -133,6 +147,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
cgbDmgColors = ColorsFromTitleHash(file);
}
_settings.GBPalette = cgbDmgColors;
ChangeDMGColors(cgbDmgColors);
}
@ -295,27 +310,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
_inputCallbacks = ics;
}
// needs to match the reverse order of Libgambatte's button enum
static readonly IReadOnlyList<String> BUTTON_ORDER_IN_BITMASK = new string[] { "Down", "Up", "Left", "Right", "Start", "Select", "B", "A" };
internal void FrameAdvancePrep(IController controller)
{
// update our local copy of the controller data
CurrentButtons = 0;
if (controller.IsPressed("Up"))
CurrentButtons |= LibGambatte.Buttons.UP;
if (controller.IsPressed("Down"))
CurrentButtons |= LibGambatte.Buttons.DOWN;
if (controller.IsPressed("Left"))
CurrentButtons |= LibGambatte.Buttons.LEFT;
if (controller.IsPressed("Right"))
CurrentButtons |= LibGambatte.Buttons.RIGHT;
if (controller.IsPressed("A"))
CurrentButtons |= LibGambatte.Buttons.A;
if (controller.IsPressed("B"))
CurrentButtons |= LibGambatte.Buttons.B;
if (controller.IsPressed("Select"))
CurrentButtons |= LibGambatte.Buttons.SELECT;
if (controller.IsPressed("Start"))
CurrentButtons |= LibGambatte.Buttons.START;
byte b = 0;
for (var i = 0; i < 8; i++)
{
b <<= 1;
if (controller.IsPressed(BUTTON_ORDER_IN_BITMASK[i])) b |= 1;
}
CurrentButtons = (LibGambatte.Buttons)b;
// the controller callback will set this to false if it actually gets called during the frame
IsLagFrame = true;

View File

@ -17,6 +17,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public int[] GetVideoBuffer() => VideoBuffer;
private readonly int[] VideoBuffer = new int[160 * 2 * 144];
private readonly int[] VideoBuffer = CreateVideoBuffer();
private static int[] CreateVideoBuffer()
{
var b = new int[160 * 2 * 144];
for (int i = 0; i < (160 * 2 * 144); i++)
{
b[i] = -1; // GB/C screen is disabled on bootup, so it always starts as white, not black
}
return b;
}
}
}

View File

@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private readonly byte[] image = new byte[160 * 200];
private ushort image_offset;
private byte compression_run_lenth;
private byte compression_run_length;
private bool compression_run_is_compressed;
public GambattePrinter(Gameboy gb, PrinterCallback callback)
@ -136,27 +136,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
if (compression)
{
if (compression_run_lenth == 0)
if (compression_run_length == 0)
{
compression_run_is_compressed = (byte_received & 0x80) != 0;
compression_run_lenth = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0));
compression_run_length = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0));
}
else if (compression_run_is_compressed)
{
while (compression_run_lenth > 0)
while (compression_run_length > 0)
{
command_data[command_length++] = byte_received;
compression_run_lenth--;
compression_run_length--;
if (command_length == GB_PRINTER_MAX_COMMAND_LENGTH)
{
compression_run_lenth = 0;
compression_run_length = 0;
}
}
}
else
{
command_data[command_length++] = byte_received;
compression_run_lenth--;
compression_run_length--;
}
}
else

View File

@ -432,8 +432,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <param name="dest">length of at least 10, please</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_getregs(IntPtr core, int[] dest);
/// <summary>
/// set reg and flag values
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="src">length of at least 10, please</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setregs(IntPtr core, int[] src);
public enum RegIndicies : int
public enum RegIndices : int
{
PC, SP, A, B, C, D, E, F, H, L
}

@ -1 +1 @@
Subproject commit 1f125d8d00174546a4dc4b95752ad2f4a63fce61
Subproject commit 1fe731bd6f0ca40737aa2e577d791ea6b084829a