From 21aa64831889b8a4026b4bd2df74ae8eca9e0fe4 Mon Sep 17 00:00:00 2001 From: adelikat Date: Tue, 25 Apr 2017 10:11:43 -0500 Subject: [PATCH] Gambatte - some cleanup --- .../BizHawk.Emulation.Cores.csproj | 5 +- .../Nintendo/Gameboy/Gambatte.IEmulator.cs | 107 ++++++++ .../Consoles/Nintendo/Gameboy/Gambatte.cs | 229 ++++++------------ 3 files changed, 191 insertions(+), 150 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs 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)