diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 78127c2c20..7904687d90 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -416,7 +416,7 @@
Intellivision.cs
- Intellivision.cs
+ Intellivision.cs
Intellivision.cs
@@ -447,6 +447,9 @@
Gambatte.cs
+
+ Gambatte.cs
+
Gambatte.cs
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs
new file mode 100644
index 0000000000..2854a82acf
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Diagnostics;
+
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
+{
+ public partial class Gameboy : IEmulator
+ {
+ public IEmulatorServiceProvider ServiceProvider { get; }
+
+ public ControllerDefinition ControllerDefinition => GbController;
+
+ public IController Controller { get; set; }
+
+ public void FrameAdvance(bool render, bool rendersound)
+ {
+ FrameAdvancePrep();
+ if (_syncSettings.EqualLengthFrames)
+ {
+ while (true)
+ {
+ // target number of samples to emit: length of 1 frame minus whatever overflow
+ uint samplesEmitted = TICKSINFRAME - frameOverflow;
+ Debug.Assert(samplesEmitted * 2 <= soundbuff.Length);
+ if (LibGambatte.gambatte_runfor(GambatteState, soundbuff, ref samplesEmitted) > 0)
+ {
+ LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
+ }
+
+ // account for actual number of samples emitted
+ _cycleCount += samplesEmitted;
+ frameOverflow += samplesEmitted;
+
+ if (rendersound && !Muted)
+ {
+ ProcessSound((int)samplesEmitted);
+ }
+
+ if (frameOverflow >= TICKSINFRAME)
+ {
+ frameOverflow -= TICKSINFRAME;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // target number of samples to emit: always 59.7fps
+ // runfor() always ends after creating a video frame, so sync-up is guaranteed
+ // when the display has been off, some frames can be markedly shorter than expected
+ uint samplesEmitted = TICKSINFRAME;
+ if (LibGambatte.gambatte_runfor(GambatteState, soundbuff, ref samplesEmitted) > 0)
+ {
+ LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
+ }
+
+ _cycleCount += samplesEmitted;
+ frameOverflow = 0;
+ if (rendersound && !Muted)
+ {
+ ProcessSound((int)samplesEmitted);
+ }
+ }
+
+ if (rendersound && !Muted)
+ {
+ ProcessSoundEnd();
+ }
+
+ FrameAdvancePost();
+ }
+
+ public int Frame { get; private set; }
+
+ public string SystemId => "GB";
+
+ public string BoardName { get; }
+
+ public bool DeterministicEmulation { get; }
+
+ public void ResetCounters()
+ {
+ Frame = 0;
+ LagCount = 0;
+ IsLagFrame = false;
+
+ // reset frame counters is meant to "re-zero" emulation time wherever it was
+ // so these should be reset as well
+ _cycleCount = 0;
+ frameOverflow = 0;
+ }
+
+ public CoreComm CoreComm { get; }
+
+ public void Dispose()
+ {
+ if (GambatteState != IntPtr.Zero)
+ {
+ LibGambatte.gambatte_destroy(GambatteState);
+ GambatteState = IntPtr.Zero;
+ }
+
+ DisposeSound();
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
index 974fc98b76..a3db6902ad 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -14,8 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
isPorted: true,
isReleased: true,
portedVersion: "SVN 344",
- portedUrl: "http://gambatte.sourceforge.net/"
- )]
+ portedUrl: "http://gambatte.sourceforge.net/")]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IDriveLight))]
public partial class Gameboy : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger,
IDebuggable, ISettable
@@ -33,59 +32,60 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
/// keep a copy of the input callback delegate so it doesn't get GCed
///
- LibGambatte.InputGetter InputCallback;
+ private LibGambatte.InputGetter InputCallback;
///
/// whatever keys are currently depressed
///
- LibGambatte.Buttons CurrentButtons = 0;
+ private LibGambatte.Buttons CurrentButtons = 0;
#region RTC
///
/// RTC time when emulation begins.
///
- uint zerotime = 0;
+ private readonly uint zerotime = 0;
///
/// if true, RTC will run off of real elapsed time
///
- bool real_rtc_time = false;
+ private bool real_rtc_time = false;
- LibGambatte.RTCCallback TimeCallback;
+ private LibGambatte.RTCCallback TimeCallback;
- static long GetUnixNow()
+ private static long GetUnixNow()
{
// because internally the RTC works off of relative time, we don't need to base
// this off of any particular canonical epoch.
return DateTime.UtcNow.Ticks / 10000000L - 60000000000L;
}
- uint GetCurrentTime()
+ private uint GetCurrentTime()
{
if (real_rtc_time)
{
return (uint)GetUnixNow();
}
- else
- {
- ulong fn = (ulong)Frame;
- // as we're exactly tracking cpu cycles, this can be pretty accurate
- fn *= 4389;
- fn /= 262144;
- fn += zerotime;
- return (uint)fn;
- }
+
+ ulong fn = (ulong)Frame;
+
+ // as we're exactly tracking cpu cycles, this can be pretty accurate
+ fn *= 4389;
+ fn /= 262144;
+ fn += zerotime;
+ return (uint)fn;
}
- uint GetInitialTime()
+ private uint GetInitialTime()
{
if (real_rtc_time)
+ {
return (uint)GetUnixNow();
- else
- // setting the initial boot time to 0 will cause our zerotime
- // to function as an initial offset, which is what we want
- return 0;
+ }
+
+ // setting the initial boot time to 0 will cause our zerotime
+ // to function as an initial offset, which is what we want
+ return 0;
}
#endregion
@@ -119,26 +119,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
GambatteState = LibGambatte.gambatte_create();
if (GambatteState == IntPtr.Zero)
+ {
throw new InvalidOperationException("gambatte_create() returned null???");
+ }
try
{
- this._syncSettings = (GambatteSyncSettings)SyncSettings ?? new GambatteSyncSettings();
+ _syncSettings = (GambatteSyncSettings)SyncSettings ?? new GambatteSyncSettings();
+
// copy over non-loadflag syncsettings now; they won't take effect if changed later
- zerotime = (uint)this._syncSettings.RTCInitialTime;
- real_rtc_time = DeterministicEmulation ? false : this._syncSettings.RealTimeRTC;
+ zerotime = (uint)_syncSettings.RTCInitialTime;
+
+ real_rtc_time = !DeterministicEmulation && _syncSettings.RealTimeRTC;
LibGambatte.LoadFlags flags = 0;
- if (this._syncSettings.ForceDMG)
+ if (_syncSettings.ForceDMG)
+ {
flags |= LibGambatte.LoadFlags.FORCE_DMG;
- if (this._syncSettings.GBACGB)
+ }
+
+ if (_syncSettings.GBACGB)
+ {
flags |= LibGambatte.LoadFlags.GBA_CGB;
- if (this._syncSettings.MulticartCompat)
+ }
+
+ if (_syncSettings.MulticartCompat)
+ {
flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
+ }
if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags) != 0)
+ {
throw new InvalidOperationException("gambatte_load() returned non-zero (is this not a gb or gbc rom?)");
+ }
// set real default colors (before anyone mucks with them at all)
PutSettings((GambatteSettings)Settings ?? new GambatteSettings());
@@ -155,17 +169,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
InitMemoryDomains();
- CoreComm.RomStatusDetails = string.Format("{0}\r\nSHA1:{1}\r\nMD5:{2}\r\n",
- game.Name,
- file.HashSHA1(),
- file.HashMD5());
+ CoreComm.RomStatusDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";
- {
- byte[] buff = new byte[32];
- LibGambatte.gambatte_romtitle(GambatteState, buff);
- string romname = System.Text.Encoding.ASCII.GetString(buff);
- Console.WriteLine("Core reported rom name: {0}", romname);
- }
+ byte[] buff = new byte[32];
+ LibGambatte.gambatte_romtitle(GambatteState, buff);
+ string romname = System.Text.Encoding.ASCII.GetString(buff);
+ Console.WriteLine("Core reported rom name: {0}", romname);
TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime);
LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback);
@@ -181,17 +190,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
}
-
- public IEmulatorServiceProvider ServiceProvider { get; private set; }
-
#region ALL SAVESTATEABLE STATE GOES HERE
///
/// internal gambatte state
///
- internal IntPtr GambatteState = IntPtr.Zero;
+ internal IntPtr GambatteState { get; private set; } = IntPtr.Zero;
- public int Frame { get; set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
@@ -206,7 +211,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// number of extra cycles we overran in the last frame
///
private uint frameOverflow = 0;
- public ulong CycleCount { get { return _cycleCount; } }
+ public ulong CycleCount => _cycleCount;
#endregion
@@ -221,14 +226,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
};
- public ControllerDefinition ControllerDefinition
- {
- get { return GbController; }
- }
-
- public IController Controller { get; set; }
-
- LibGambatte.Buttons ControllerCallback()
+ private LibGambatte.Buttons ControllerCallback()
{
InputCallbacks.Call();
IsLagFrame = false;
@@ -240,21 +238,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
/// true if the emulator is currently emulating CGB
///
- ///
public bool IsCGBMode()
{
- return (LibGambatte.gambatte_iscgb(GambatteState));
+ return LibGambatte.gambatte_iscgb(GambatteState);
}
private InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
+
// low priority TODO: due to certain aspects of the core implementation,
// we don't smartly use the ActiveChanged event here.
- public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
+ public IInputCallbackSystem InputCallbacks => _inputCallbacks;
///
/// for use in dual core
///
- ///
public void ConnectInputCallbackSystem(InputCallbackSystem ics)
{
_inputCallbacks = ics;
@@ -288,12 +285,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
IsLagFrame = true;
if (Controller.IsPressed("Power"))
+ {
LibGambatte.gambatte_reset(GambatteState, GetCurrentTime());
+ }
if (Tracer.Enabled)
+ {
tracecb = MakeTrace;
+ }
else
+ {
tracecb = null;
+ }
+
LibGambatte.gambatte_settracecallback(GambatteState, tracecb);
LibGambatte.gambatte_setlayers(GambatteState, (_settings.DisplayBG ? 1 : 0) | (_settings.DisplayOBJ ? 2 : 0) | (_settings.DisplayWindow ? 4 : 0 ) );
@@ -302,65 +306,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
internal void FrameAdvancePost()
{
if (IsLagFrame)
+ {
LagCount++;
-
- if (endofframecallback != null)
- endofframecallback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
- }
-
- public void FrameAdvance(bool render, bool rendersound)
- {
- FrameAdvancePrep();
- if (_syncSettings.EqualLengthFrames)
- {
- while (true)
- {
- // target number of samples to emit: length of 1 frame minus whatever overflow
- uint samplesEmitted = TICKSINFRAME - frameOverflow;
- System.Diagnostics.Debug.Assert(samplesEmitted * 2 <= soundbuff.Length);
- if (LibGambatte.gambatte_runfor(GambatteState, soundbuff, ref samplesEmitted) > 0)
- LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
-
- // account for actual number of samples emitted
- _cycleCount += (ulong)samplesEmitted;
- frameOverflow += samplesEmitted;
-
- if (rendersound && !Muted)
- {
- ProcessSound((int)samplesEmitted);
- }
-
- if (frameOverflow >= TICKSINFRAME)
- {
- frameOverflow -= TICKSINFRAME;
- break;
- }
- }
- }
- else
- {
- // target number of samples to emit: always 59.7fps
- // runfor() always ends after creating a video frame, so sync-up is guaranteed
- // when the display has been off, some frames can be markedly shorter than expected
- uint samplesEmitted = TICKSINFRAME;
- if (LibGambatte.gambatte_runfor(GambatteState, soundbuff, ref samplesEmitted) > 0)
- LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
-
- _cycleCount += (ulong)samplesEmitted;
- frameOverflow = 0;
- if (rendersound && !Muted)
- {
- ProcessSound((int)samplesEmitted);
- }
}
- if (rendersound && !Muted)
- ProcessSoundEnd();
-
- FrameAdvancePost();
+ endofframecallback?.Invoke(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
}
- static string MapperName(byte[] romdata)
+
+ private static string MapperName(byte[] romdata)
{
switch (romdata[0x147])
{
@@ -391,11 +345,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
/// throw exception with intelligible message on some kinds of bad rom
///
- ///
- static void ThrowExceptionForBadRom(byte[] romdata)
+ private static void ThrowExceptionForBadRom(byte[] romdata)
{
if (romdata.Length < 0x148)
+ {
throw new ArgumentException("ROM is far too small to be a valid GB\\GBC rom!");
+ }
switch (romdata[0x147])
{
@@ -436,30 +391,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
case 0xfd: throw new UnsupportedGameException("\"Bandai TAMA5\" Mapper not supported!");
case 0xfe: throw new UnsupportedGameException("\"HuC3\" Mapper not supported!");
case 0xff: break;
- default: throw new UnsupportedGameException(string.Format("Unknown mapper: {0:x2}", romdata[0x147]));
+ default: throw new UnsupportedGameException($"Unknown mapper: {romdata[0x147]:x2}");
}
- return;
}
- public string SystemId { get { return "GB"; } }
-
- public string BoardName { get; private set; }
-
- public bool DeterministicEmulation { get; private set; }
-
- public void ResetCounters()
- {
- Frame = 0;
- LagCount = 0;
- IsLagFrame = false;
- // reset frame counters is meant to "re-zero" emulation time wherever it was
- // so these should be reset as well
- _cycleCount = 0;
- frameOverflow = 0;
- }
-
- public CoreComm CoreComm { get; set; }
-
#region ppudebug
public bool GetGPUMemoryAreas(out IntPtr vram, out IntPtr bgpal, out IntPtr sppal, out IntPtr oam)
@@ -488,7 +423,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
///
- ///
///
/// current value of register $ff40 (LCDC)
public delegate void ScanlineCallback(int lcdc);
@@ -496,26 +430,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
///
/// set up callback
///
- ///
/// scanline. -1 = end of frame, -2 = RIGHT NOW
public void SetScanlineCallback(ScanlineCallback callback, int line)
{
if (GambatteState == IntPtr.Zero)
- // not sure how this is being reached. tried the debugger...
- return;
+ {
+ return; // not sure how this is being reached. tried the debugger...
+ }
+
endofframecallback = null;
if (callback == null || line == -1 || line == -2)
{
scanlinecb = null;
LibGambatte.gambatte_setscanlinecallback(GambatteState, null, 0);
if (line == -1)
+ {
endofframecallback = callback;
+ }
else if (line == -2)
+ {
callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
+ }
}
else if (line >= 0 && line <= 153)
{
- scanlinecb = delegate()
+ scanlinecb = delegate
{
callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
};
@@ -532,16 +471,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
#endregion
- public void Dispose()
- {
- if (GambatteState != IntPtr.Zero)
- {
- LibGambatte.gambatte_destroy(GambatteState);
- GambatteState = IntPtr.Zero;
- }
- DisposeSound();
- }
-
#region palette
///
@@ -550,7 +479,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void ChangeDMGColors(int[] colors)
{
for (int i = 0; i < 12; i++)
+ {
LibGambatte.gambatte_setdmgpalettecolor(GambatteState, (LibGambatte.PalType)(i / 4), (uint)i % 4, (uint)colors[i]);
+ }
}
public void SetCGBColors(GBColors.ColorType type)