Gambatte - some cleanup

This commit is contained in:
adelikat 2017-04-25 10:11:43 -05:00
parent 2de20e956b
commit 21aa648318
3 changed files with 191 additions and 150 deletions
BizHawk.Emulation.Cores

View File

@ -416,7 +416,7 @@
<DependentUpon>Intellivision.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Intellivision\Intellivision.IDisassemblable.cs">
<DependentUpon>Intellivision.cs</DependentUpon>
<DependentUpon>Intellivision.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Intellivision\Intellivision.IInputPollable.cs">
<DependentUpon>Intellivision.cs</DependentUpon>
@ -447,6 +447,9 @@
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IDebuggable.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IEmulator.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IMemoryDomains.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>

View File

@ -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();
}
}
}

View File

@ -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<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>
@ -33,59 +32,60 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <summary>
/// keep a copy of the input callback delegate so it doesn't get GCed
/// </summary>
LibGambatte.InputGetter InputCallback;
private LibGambatte.InputGetter InputCallback;
/// <summary>
/// whatever keys are currently depressed
/// </summary>
LibGambatte.Buttons CurrentButtons = 0;
private LibGambatte.Buttons CurrentButtons = 0;
#region RTC
/// <summary>
/// RTC time when emulation begins.
/// </summary>
uint zerotime = 0;
private readonly uint zerotime = 0;
/// <summary>
/// if true, RTC will run off of real elapsed time
/// </summary>
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
/// <summary>
/// internal gambatte state
/// </summary>
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
/// </summary>
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
/// <summary>
/// true if the emulator is currently emulating CGB
/// </summary>
/// <returns></returns>
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;
/// <summary>
/// for use in dual core
/// </summary>
/// <param name="ics"></param>
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
/// <summary>
/// throw exception with intelligible message on some kinds of bad rom
/// </summary>
/// <param name="romdata"></param>
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
}
/// <summary>
///
/// </summary>
/// <param name="lcdc">current value of register $ff40 (LCDC)</param>
public delegate void ScanlineCallback(int lcdc);
@ -496,26 +430,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <summary>
/// set up callback
/// </summary>
/// <param name="callback"></param>
/// <param name="line">scanline. -1 = end of frame, -2 = RIGHT NOW</param>
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
/// <summary>
@ -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)