diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 569944da34..9897fac5b4 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1210,8 +1210,8 @@ - - + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs index d4b03d898a..439fab0884 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs @@ -225,7 +225,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB #endregion - // TODO public int LagCount { get; set; } public bool IsLagFrame { get; set; } diff --git a/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeo.cs b/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs similarity index 80% rename from BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeo.cs rename to BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs index b0bfb58289..f5ccba49e6 100644 --- a/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeo.cs +++ b/BizHawk.Emulation.Cores/Consoles/SNK/LibNeoGeoPort.cs @@ -8,16 +8,19 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Consoles.SNK { - public abstract class LibNeoGeo + public abstract class LibNeoGeoPort { private const CallingConvention CC = CallingConvention.Cdecl; + [UnmanagedFunctionPointer(CC)] + public delegate void InputCallback(); [StructLayout(LayoutKind.Sequential)] public class EmulateSpec { public IntPtr Pixels; public IntPtr SoundBuff; public long MasterCycles; + public long FrontendTime; public int SoundBufMaxSize; public int SoundBufSize; public int SkipRendering; @@ -32,5 +35,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SNK public abstract void FrameAdvance([In, Out]EmulateSpec espec); [BizImport(CC)] public abstract void HardReset(); + [BizImport(CC)] + public abstract void SetInputCallback(InputCallback callback); } } diff --git a/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeo.cs b/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeo.cs deleted file mode 100644 index e648568c30..0000000000 --- a/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeo.cs +++ /dev/null @@ -1,153 +0,0 @@ -using BizHawk.Common.BizInvoke; -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Waterbox; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Consoles.SNK -{ - [CoreAttributes("NeoPop", "Thomas Klausner", true, false, "0.9.44.1", - "https://mednafen.github.io/releases/", false)] - public class NeoGeo : IEmulator, IVideoProvider, ISoundProvider - { - private PeRunner _exe; - private LibNeoGeo _neopop; - - [CoreConstructor("NGP")] - public NeoGeo(CoreComm comm, byte[] rom) - { - ServiceProvider = new BasicServiceProvider(this); - CoreComm = comm; - - _exe = new PeRunner(new PeRunnerOptions - { - Path = comm.CoreFileProvider.DllPath(), - Filename = "ngp.wbx", - SbrkHeapSizeKB = 16 * 1024, - SealedHeapSizeKB = 16 * 1024, - InvisibleHeapSizeKB = 16 * 1024, - PlainHeapSizeKB = 16 * 1024, - MmapHeapSizeKB = 16 * 1024 - }); - - _neopop = BizInvoker.GetInvoker(_exe, _exe); - - if (!_neopop.LoadSystem(rom, rom.Length, 1)) - { - throw new InvalidOperationException("Core rejected the rom"); - } - - _exe.Seal(); - } - - public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true) - { - if (controller.IsPressed("Power")) - _neopop.HardReset(); - - fixed (int* vp = _videoBuffer) - fixed (short* sp = _soundBuffer) - { - var spec = new LibNeoGeo.EmulateSpec - { - Pixels = (IntPtr)vp, - SoundBuff = (IntPtr)sp, - SoundBufMaxSize = _soundBuffer.Length / 2, - Buttons = 0, - SkipRendering = render ? 0 : 1 - }; - - _neopop.FrameAdvance(spec); - _numSamples = spec.SoundBufSize; - - Frame++; - - /*IsLagFrame = spec.Lagged; - if (IsLagFrame) - LagCount++;*/ - } - } - - private bool _disposed = false; - - public void Dispose() - { - if (!_disposed) - { - _exe.Dispose(); - _exe = null; - _disposed = true; - } - } - - public int Frame { get; private set; } - - public void ResetCounters() - { - Frame = 0; - } - - public IEmulatorServiceProvider ServiceProvider { get; private set; } - public string SystemId { get { return "NGP"; } } - public bool DeterministicEmulation { get { return true; } } - public CoreComm CoreComm { get; } - - public ControllerDefinition ControllerDefinition => NullController.Instance.Definition; - - #region IVideoProvider - - private int[] _videoBuffer = new int[160 * 152]; - - public int[] GetVideoBuffer() - { - return _videoBuffer; - } - - public int VirtualWidth => 160; - public int VirtualHeight => 152; - public int BufferWidth => 160; - public int BufferHeight => 152; - public int VsyncNumerator { get; private set; } = 6144000; - public int VsyncDenominator { get; private set; } = 515 * 198; - public int BackgroundColor => unchecked((int)0xff000000); - - #endregion - - #region ISoundProvider - - private short[] _soundBuffer = new short[16384]; - private int _numSamples; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode == SyncSoundMode.Async) - { - throw new NotSupportedException("Async mode is not supported."); - } - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - samples = _soundBuffer; - nsamp = _numSamples; - } - - public void GetSamplesAsync(short[] samples) - { - throw new InvalidOperationException("Async mode is not supported."); - } - - public void DiscardSamples() - { - } - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs b/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs new file mode 100644 index 0000000000..f7b1b44242 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs @@ -0,0 +1,261 @@ +using BizHawk.Common.BizInvoke; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Waterbox; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Consoles.SNK +{ + [CoreAttributes("NeoPop", "Thomas Klausner", true, false, "0.9.44.1", + "https://mednafen.github.io/releases/", false)] + public class NeoGeoPort : IEmulator, IVideoProvider, ISoundProvider, IStatable, IInputPollable + { + private PeRunner _exe; + private LibNeoGeoPort _neopop; + + [CoreConstructor("NGP")] + public NeoGeoPort(CoreComm comm, byte[] rom) + { + ServiceProvider = new BasicServiceProvider(this); + CoreComm = comm; + + _exe = new PeRunner(new PeRunnerOptions + { + Path = comm.CoreFileProvider.DllPath(), + Filename = "ngp.wbx", + SbrkHeapSizeKB = 256, + SealedHeapSizeKB = 10 * 1024, // must be a bit larger than twice the ROM size + InvisibleHeapSizeKB = 4, + PlainHeapSizeKB = 4 + }); + + _neopop = BizInvoker.GetInvoker(_exe, _exe); + + if (!_neopop.LoadSystem(rom, rom.Length, 1)) + { + throw new InvalidOperationException("Core rejected the rom"); + } + + _exe.Seal(); + + _inputCallback = InputCallbacks.Call; + } + + public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true) + { + _neopop.SetInputCallback(InputCallbacks.Count > 0 ? _inputCallback : null); + + if (controller.IsPressed("Power")) + _neopop.HardReset(); + + fixed (int* vp = _videoBuffer) + fixed (short* sp = _soundBuffer) + { + var spec = new LibNeoGeoPort.EmulateSpec + { + Pixels = (IntPtr)vp, + SoundBuff = (IntPtr)sp, + SoundBufMaxSize = _soundBuffer.Length / 2, + Buttons = GetButtons(controller), + SkipRendering = render ? 0 : 1 + }; + + _neopop.FrameAdvance(spec); + _numSamples = spec.SoundBufSize; + + Frame++; + + IsLagFrame = spec.Lagged != 0; + if (IsLagFrame) + LagCount++; + } + } + + private bool _disposed = false; + + public void Dispose() + { + if (!_disposed) + { + _exe.Dispose(); + _exe = null; + _disposed = true; + } + } + + public int Frame { get; private set; } + public int LagCount { get; set; } + public bool IsLagFrame { get; set; } + public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); + private LibNeoGeoPort.InputCallback _inputCallback; + + public void ResetCounters() + { + Frame = 0; + } + + public IEmulatorServiceProvider ServiceProvider { get; private set; } + public string SystemId { get { return "NGP"; } } + public bool DeterministicEmulation { get { return true; } } + public CoreComm CoreComm { get; } + + #region IStatable + + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + public void SaveStateText(TextWriter writer) + { + var temp = SaveStateBinary(); + temp.SaveAsHexFast(writer); + // write extra copy of stuff we don't use + writer.WriteLine("Frame {0}", Frame); + } + + public void LoadStateText(TextReader reader) + { + string hex = reader.ReadLine(); + byte[] state = new byte[hex.Length / 2]; + state.ReadFromHexFast(hex); + LoadStateBinary(new BinaryReader(new MemoryStream(state))); + } + + public void LoadStateBinary(BinaryReader reader) + { + _exe.LoadStateBinary(reader); + // other variables + Frame = reader.ReadInt32(); + LagCount = reader.ReadInt32(); + IsLagFrame = reader.ReadBoolean(); + // any managed pointers that we sent to the core need to be resent now! + _neopop.SetInputCallback(null); + } + + public void SaveStateBinary(BinaryWriter writer) + { + _exe.SaveStateBinary(writer); + // other variables + writer.Write(Frame); + writer.Write(LagCount); + writer.Write(IsLagFrame); + } + + public byte[] SaveStateBinary() + { + var ms = new MemoryStream(); + var bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + ms.Close(); + return ms.ToArray(); + } + + #endregion + + #region Controller + + private static int GetButtons(IController c) + { + var ret = 0; + var val = 1; + foreach (var s in CoreButtons) + { + if (c.IsPressed(s)) + ret |= val; + val <<= 1; + } + return ret; + } + + private static readonly string[] CoreButtons = + { + "Up", "Down", "Left", "Right", "A", "B", "Option" + }; + + private static readonly Dictionary ButtonOrdinals = new Dictionary + { + ["Up"] = 1, + ["Down"] = 2, + ["Left"] = 3, + ["Right"] = 4, + ["B"] = 9, + ["A"] = 10, + ["R"] = 11, + ["L"] = 12, + ["Option"] = 13 + }; + + private static readonly ControllerDefinition NeoGeoPortableController = new ControllerDefinition + { + Name = "NeoGeo Portable Controller", + BoolButtons = CoreButtons + .OrderBy(b => ButtonOrdinals[b]) + .Concat(new[] { "Power" }) + .ToList() + }; + + public ControllerDefinition ControllerDefinition => NeoGeoPortableController; + + #endregion + + #region IVideoProvider + + private int[] _videoBuffer = new int[160 * 152]; + + public int[] GetVideoBuffer() + { + return _videoBuffer; + } + + public int VirtualWidth => 160; + public int VirtualHeight => 152; + public int BufferWidth => 160; + public int BufferHeight => 152; + public int VsyncNumerator { get; private set; } = 6144000; + public int VsyncDenominator { get; private set; } = 515 * 198; + public int BackgroundColor => unchecked((int)0xff000000); + + #endregion + + #region ISoundProvider + + private short[] _soundBuffer = new short[16384]; + private int _numSamples; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode == SyncSoundMode.Async) + { + throw new NotSupportedException("Async mode is not supported."); + } + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + samples = _soundBuffer; + nsamp = _numSamples; + } + + public void GetSamplesAsync(short[] samples) + { + throw new InvalidOperationException("Async mode is not supported."); + } + + public void DiscardSamples() + { + } + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs index 3e04a91008..a15521678a 100644 --- a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs +++ b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs @@ -234,8 +234,8 @@ namespace BizHawk.Emulation.Cores.Waterbox public void Init() { // as the inits are done in a defined order with a defined memory map, - // we don't need to savestate _pthreadSelf - _pthreadSelf = Z.US(_parent._plainheap.Allocate(65536, 1)); + // we don't need to savestate _pthreadSelf, only its contents + _pthreadSelf = Z.US(_parent._plainheap.Allocate(512, 1)); } [BizExport(CallingConvention.Cdecl, EntryPoint = "log_output")] diff --git a/waterbox/ngp/.vscode/settings.json b/waterbox/ngp/.vscode/settings.json index 109dd034cd..1035572a13 100644 --- a/waterbox/ngp/.vscode/settings.json +++ b/waterbox/ngp/.vscode/settings.json @@ -6,6 +6,7 @@ "algorithm": "cpp", "vector": "cpp", "xstring": "cpp", - "xutility": "cpp" + "xutility": "cpp", + "xmemory0": "cpp" } } \ No newline at end of file diff --git a/waterbox/ngp/Makefile b/waterbox/ngp/Makefile index 22aaf5b968..67181161a6 100644 --- a/waterbox/ngp/Makefile +++ b/waterbox/ngp/Makefile @@ -4,7 +4,7 @@ CCFLAGS:= -I. -I../emulibc \ -Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \ -std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \ -DLSB_FIRST \ - -O0 -g + -O3 -flto TARGET = ngp.wbx diff --git a/waterbox/ngp/defs.h b/waterbox/ngp/defs.h index 294dd421ec..08b902450b 100644 --- a/waterbox/ngp/defs.h +++ b/waterbox/ngp/defs.h @@ -52,6 +52,9 @@ struct EmulateSpecStruct // Set by emulation code. int64 MasterCycles; + // unix time for RTC + int64 FrontendTime; + // Maximum size of the sound buffer, in frames. Set by the driver code. int32 SoundBufMaxSize; diff --git a/waterbox/ngp/gfx.cpp b/waterbox/ngp/gfx.cpp index 36380ddd40..b9aece94b8 100644 --- a/waterbox/ngp/gfx.cpp +++ b/waterbox/ngp/gfx.cpp @@ -31,7 +31,7 @@ NGPGFX_CLASS::NGPGFX_CLASS(void) int g = ((x >> 4) & 0xF) * 17; int b = ((x >> 8) & 0xF) * 17; - ColorMap[x] = r | g << 8 | b << 16 | 0xff000000; + ColorMap[x] = b | g << 8 | r << 16 | 0xff000000; } } diff --git a/waterbox/ngp/mem.cpp b/waterbox/ngp/mem.cpp index 82ab5c7a87..457eadffff 100644 --- a/waterbox/ngp/mem.cpp +++ b/waterbox/ngp/mem.cpp @@ -27,6 +27,8 @@ namespace MDFN_IEN_NGP { extern uint8 settings_language; +extern bool lagged; +extern void (*inputcallback)(); //Hack way of returning good Flash status. bool FlashStatusEnable = FALSE; @@ -200,6 +202,13 @@ uint8 loadB(uint32 address) if (FastReadMap[address >> 16]) return (FastReadMap[address >> 16][address]); + if (address == 0x6f82) + { + lagged = false; + if (inputcallback) + inputcallback(); + } + uint8 *ptr = (uint8 *)translate_address_read(address); if (ptr) @@ -255,6 +264,13 @@ uint16 loadW(uint32 address) if (FastReadMap[address >> 16]) return (MDFN_de16lsb(&FastReadMap[address >> 16][address])); + if (address == 0x6f82) + { + lagged = false; + if (inputcallback) + inputcallback(); + } + uint16 *ptr = (uint16 *)translate_address_read(address); if (ptr) return MDFN_de16lsb(ptr); diff --git a/waterbox/ngp/neopop.cpp b/waterbox/ngp/neopop.cpp index 81a0fff569..fd4528e7ea 100644 --- a/waterbox/ngp/neopop.cpp +++ b/waterbox/ngp/neopop.cpp @@ -27,6 +27,9 @@ namespace MDFN_IEN_NGP { +bool lagged; +void (*inputcallback)(); + extern uint8 CPUExRAM[16384]; @@ -72,11 +75,13 @@ static int32 z80_runtime; static void Emulate(EmulateSpecStruct *espec) { + lagged = true; bool MeowMeow = 0; MDFN_Surface surface; surface.pixels = espec->pixels; surface.pitch32 = 160; + frontend_time = espec->FrontendTime; storeB(0x6f82, espec->Buttons); ngpc_soundTS = 0; @@ -115,6 +120,7 @@ static void Emulate(EmulateSpecStruct *espec) espec->MasterCycles = ngpc_soundTS; espec->SoundBufSize = MDFNNGPCSOUND_Flush(espec->SoundBuf, espec->SoundBufMaxSize); + espec->Lagged = lagged; } static MDFN_COLD bool Load(const uint8* romdata, int32 romlength) @@ -126,7 +132,7 @@ static MDFN_COLD bool Load(const uint8* romdata, int32 romlength) //throw MDFN_Error(0, _("NGP/NGPC ROM image is too large.")); ngpc_rom.length = fp_size; - ngpc_rom.data = new uint8[ngpc_rom.length]; + ngpc_rom.data = (uint8*)alloc_sealed(ngpc_rom.length); memcpy(ngpc_rom.data, romdata, romlength); rom_loaded(); @@ -266,3 +272,8 @@ EXPORT void HardReset() { reset(); } + +EXPORT void SetInputCallback(void (*callback)()) +{ + inputcallback = callback; +} diff --git a/waterbox/ngp/rom.cpp b/waterbox/ngp/rom.cpp index b209f1e0e1..57f057d6fd 100644 --- a/waterbox/ngp/rom.cpp +++ b/waterbox/ngp/rom.cpp @@ -15,6 +15,7 @@ #include "neopop.h" #include "flash.h" #include "interrupt.h" +#include //============================================================================= @@ -104,33 +105,10 @@ void rom_loaded(void) rom_display_header(); - ngpc_rom.orig_data = new uint8[ngpc_rom.length]; + ngpc_rom.orig_data = (uint8*)alloc_sealed(ngpc_rom.length); memcpy(ngpc_rom.orig_data, ngpc_rom.data, ngpc_rom.length); } -//----------------------------------------------------------------------------- -// rom_unload() -//----------------------------------------------------------------------------- -void rom_unload(void) -{ - if(ngpc_rom.data) - { - delete[] ngpc_rom.data; - ngpc_rom.data = NULL; - ngpc_rom.length = 0; - rom_header = 0; - - for(int i = 0; i < 16; i++) - ngpc_rom.name[i] = 0; - } - - if(ngpc_rom.orig_data) - { - delete[] ngpc_rom.orig_data; - ngpc_rom.orig_data = NULL; - } -} - } //=============================================================================