[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:
parent
4973e3eba1
commit
7518ce962b
Binary file not shown.
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue