diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs index 3a37421135..c82e32b466 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs @@ -4,30 +4,12 @@ using System.Linq; using System.Text; using System.Runtime.InteropServices; using BizHawk.Common.BizInvoke; +using BizHawk.Emulation.Cores.Waterbox; namespace BizHawk.Emulation.Cores.Nintendo.SNES9X { - public abstract class LibSnes9x + public abstract class LibSnes9x : LibWaterboxCore { - [StructLayout(LayoutKind.Sequential)] - public class memory_area - { - public IntPtr ptr; - public int size; - }; - - [StructLayout(LayoutKind.Sequential)] - public class frame_info - { - public IntPtr vptr; - public int vpitch; - public int vwidth; - public int vheight; - public IntPtr sptr; - public int slen; - public int padread; - }; - public enum LeftPortDevice : uint { //None = 0, // something in the libretro spaghetti input goes wonky with None @@ -44,11 +26,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X Justifier = 5 } - [UnmanagedFunctionPointer(CC)] - public delegate void InputCallback(); - - const CallingConvention CC = CallingConvention.Cdecl; - + [BizImport(CC)] + public abstract void SetButtons(short[] buttons); [BizImport(CC)] public abstract void biz_set_sound_channels(int channels); [BizImport(CC)] @@ -64,14 +43,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X [BizImport(CC)] public abstract bool biz_init(); [BizImport(CC)] - public abstract void biz_run([In, Out] frame_info frame, [In]short[] input); - [BizImport(CC)] public abstract bool biz_is_ntsc(); [BizImport(CC)] - public abstract void biz_get_memory_area(int which, [In, Out] memory_area mem); - [BizImport(CC)] public abstract void biz_post_load_state(); - [BizImport(CC)] - public abstract void biz_set_input_callback(InputCallback callback); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs index 124a6f33ce..ccc9492d6e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs @@ -12,26 +12,31 @@ using System.Linq; namespace BizHawk.Emulation.Cores.Nintendo.SNES9X { - [CoreAttributes("Snes9x", "", true, true, "5e0319ab3ef9611250efb18255186d0dc0d7e125", "https://github.com/snes9xgit/snes9x", false)] + [CoreAttributes("Snes9x", "", true, true, + "5e0319ab3ef9611250efb18255186d0dc0d7e125", "https://github.com/snes9xgit/snes9x", false)] [ServiceNotApplicable(typeof(IDriveLight))] - public class Snes9x : IEmulator, IVideoProvider, ISoundProvider, IStatable, - ISettable, - ISaveRam, IInputPollable, IRegionable + public class Snes9x : WaterboxCore, + ISettable, IRegionable { private LibSnes9x _core; - private PeRunner _exe; [CoreConstructor("SNES")] public Snes9x(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings) + :base(comm, new Configuration + { + DefaultWidth = 256, + DefaultHeight = 224, + MaxWidth = 512, + MaxHeight = 480, + MaxSamples = 8192, + SystemId = "SNES" + }) { - ServiceProvider = new BasicServiceProvider(this); - CoreComm = comm; settings = settings ?? new Settings(); syncSettings = syncSettings ?? new SyncSettings(); - _exe = new PeRunner(new PeRunnerOptions + _core = PreInit(new PeRunnerOptions { - Path = comm.CoreFileProvider.DllPath(), Filename = "snes9x.wbx", SbrkHeapSizeKB = 1024, SealedHeapSizeKB = 12 * 1024, @@ -39,16 +44,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X PlainHeapSizeKB = 64 }); - _core = BizInvoker.GetInvoker(_exe, _exe); if (!_core.biz_init()) - { throw new InvalidOperationException("Init() failed"); - } if (!_core.biz_load_rom(rom, rom.Length)) - { throw new InvalidOperationException("LoadRom() failed"); - } - _exe.Seal(); + + PostInit(); if (_core.biz_is_ntsc()) { @@ -65,16 +66,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X Region = DisplayType.PAL; } - _nsampTarget = (int)Math.Round(44100.0 * VsyncDenominator / VsyncNumerator); - _nsampWarn = (int)Math.Round(1.05 * 44100.0 * VsyncDenominator / VsyncNumerator); - _syncSettings = syncSettings; InitControllers(); PutSettings(settings); - InitMemoryDomains(); - InitSaveram(); - - _inputCallback = InputCallbacks.Call; } #region controller @@ -115,13 +109,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X break; } - ControllerDefinition = ControllerDefinitionMerger.GetMerged( + _controllerDefinition = ControllerDefinitionMerger.GetMerged( _controllers.Select(c => c.Definition), out _cdums); // add buttons that the core itself will handle - ControllerDefinition.BoolButtons.Add("Reset"); - ControllerDefinition.BoolButtons.Add("Power"); - ControllerDefinition.Name = "SNES Controller"; + _controllerDefinition.BoolButtons.Add("Reset"); + _controllerDefinition.BoolButtons.Add("Power"); + _controllerDefinition.Name = "SNES Controller"; } private void UpdateControls(IController c) @@ -285,238 +279,47 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X public override ControllerDefinition Definition => _definition; } - public ControllerDefinition ControllerDefinition { get; private set; } + private ControllerDefinition _controllerDefinition; + public override ControllerDefinition ControllerDefinition => _controllerDefinition; #endregion - private bool _disposed = false; - - public void Dispose() - { - if (!_disposed) - { - _exe.Dispose(); - _exe = null; - _disposed = true; - } - } - public DisplayType Region { get; } - public IEmulatorServiceProvider ServiceProvider { get; } - - public void FrameAdvance(IController controller, bool render, bool rendersound = true) + protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) { - _core.biz_set_input_callback(InputCallbacks.Count > 0 ? _inputCallback : null); - if (controller.IsPressed("Power")) _core.biz_hard_reset(); else if (controller.IsPressed("Reset")) _core.biz_soft_reset(); - UpdateControls(controller); - Frame++; - LibSnes9x.frame_info frame = new LibSnes9x.frame_info(); + _core.SetButtons(_inputState); - _core.biz_run(frame, _inputState); - IsLagFrame = frame.padread == 0; - if (IsLagFrame) - LagCount++; - using (_exe.EnterExit()) - { - Blit(frame); - Sblit(frame); - } + return new LibWaterboxCore.FrameInfo(); } - - public int Frame { get; private set; } - - public void ResetCounters() + protected override void FrameAdvancePost() { - Frame = 0; + _virtualHeight = BufferHeight; + _virtualWidth = BufferWidth; + if (_virtualHeight * 2 < _virtualWidth) + _virtualHeight *= 2; + if (_virtualHeight > 240) + _virtualWidth = 512; + _virtualWidth = (int)Math.Round(_virtualWidth * 1.146); } - public string SystemId { get { return "SNES"; } } - public bool DeterministicEmulation { get { return true; } } - public CoreComm CoreComm { get; private set; } - - #region IVideoProvider - - private static readonly int[] VirtualWidths = new[] { 293, 587, 587, 587 }; // 256 512 256 512 - private static readonly int[] VirtualHeights = new[] { 224, 448, 448, 448 }; // 224 224 448 448 - - private unsafe void Blit(LibSnes9x.frame_info frame) - { - BufferWidth = frame.vwidth; - BufferHeight = frame.vheight; - - int vinc = frame.vpitch / sizeof(ushort) - frame.vwidth; - - ushort* src = (ushort*)frame.vptr; - fixed (int* _dst = _vbuff) - { - byte* dst = (byte*)_dst; - - for (int j = 0; j < frame.vheight; j++) - { - for (int i = 0; i < frame.vwidth; i++) - { - var c = *src++; - - *dst++ = (byte)(c << 3 & 0xf8 | c >> 2 & 7); - *dst++ = (byte)(c >> 3 & 0xfa | c >> 9 & 3); - *dst++ = (byte)(c >> 8 & 0xf8 | c >> 13 & 7); - *dst++ = 0xff; - } - src += vinc; - } - } - - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - if (VirtualHeight * 2 < VirtualWidth) - VirtualHeight *= 2; - if (VirtualHeight > 240) - VirtualWidth = 512; - VirtualWidth = (int)Math.Round(VirtualWidth * 1.146); - } - - private int[] _vbuff = new int[512 * 480]; - public int[] GetVideoBuffer() { return _vbuff; } - public int VirtualWidth { get; private set; } = 293; - public int VirtualHeight { get; private set; } = 224; - public int BufferWidth { get; private set; } = 256; - public int BufferHeight { get; private set; } = 224; - public int BackgroundColor { get { return unchecked((int)0xff000000); } } - - public int VsyncNumerator - { - get; - } - - public int VsyncDenominator - { - get; - } - - #endregion - - #region ISoundProvider - - private void Sblit(LibSnes9x.frame_info frame) - { - Marshal.Copy(frame.sptr, _sbuff, 0, frame.slen * 2); - _nsamp = frame.slen; - if (_nsamp > _nsampWarn) - { - Console.WriteLine($"Warn: Long frame! {_nsamp} > {_nsampTarget}"); - } - } - - private readonly int _nsampWarn; - private readonly int _nsampTarget; - - private int _nsamp; - private short[] _sbuff = new short[8192]; - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - samples = _sbuff; - nsamp = _nsamp; - } - - public void DiscardSamples() - { - // Nothing to do - } - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode == SyncSoundMode.Async) - { - throw new NotSupportedException("Async mode is not supported."); - } - } - - public bool CanProvideAsync - { - get { return false; } - } - - public SyncSoundMode SyncMode - { - get { return SyncSoundMode.Sync; } - } - - public void GetSamplesAsync(short[] samples) - { - throw new InvalidOperationException("Async mode is not supported."); - } - - #endregion - - private LibSnes9x.InputCallback _inputCallback; - - public int LagCount { get; set; } - public bool IsLagFrame { get; set; } - - public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); + private int _virtualWidth; + private int _virtualHeight; + public override int VirtualWidth => _virtualWidth; + public override int VirtualHeight => _virtualHeight; #region IStatable - public bool BinarySaveStatesPreferred + protected override void LoadStateBinaryInternal(BinaryReader reader) { - 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! - _core.biz_set_input_callback(null); - _core.biz_post_load_state(); } - 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 settings @@ -686,83 +489,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X } #endregion - - #region Memory Domains - - private unsafe void InitMemoryDomains() - { - var native = new LibSnes9x.memory_area(); - var domains = new List(); - - var names = new[] { "CARTRAM", "CARTRAM B", "RTC", "WRAM", "VRAM" }; - int index = 0; - foreach (var s in names) - { - _core.biz_get_memory_area(index++, native); - if (native.ptr != IntPtr.Zero && native.size > 0) - { - domains.Add(new MemoryDomainIntPtrMonitor(s, MemoryDomain.Endian.Little, - native.ptr, native.size, true, 2, _exe)); - } - } - (ServiceProvider as BasicServiceProvider).Register(new MemoryDomainList(domains) - { - MainMemory = domains.Single(d => d.Name == "WRAM") - }); - } - - #endregion - - #region ISaveRam - - private void InitSaveram() - { - for (int i = 0; i < 2; i++) // SRAM A, SRAM B, RTC - { - var native = new LibSnes9x.memory_area(); - _core.biz_get_memory_area(i, native); - if (native.ptr != IntPtr.Zero && native.size > 0) - _saveramMemoryAreas.Add(native); - } - _saveramSize = _saveramMemoryAreas.Sum(a => a.size); - } - - private readonly List _saveramMemoryAreas = new List(); - - private int _saveramSize; - - public bool SaveRamModified => _saveramSize > 0; - - public byte[] CloneSaveRam() - { - using (_exe.EnterExit()) - { - var ret = new byte[_saveramSize]; - var offset = 0; - foreach (var area in _saveramMemoryAreas) - { - Marshal.Copy(area.ptr, ret, offset, area.size); - offset += area.size; - } - return ret; - } - } - - public void StoreSaveRam(byte[] data) - { - using (_exe.EnterExit()) - { - if (data.Length != _saveramSize) - throw new InvalidOperationException("Saveram size mismatch"); - var offset = 0; - foreach (var area in _saveramMemoryAreas) - { - Marshal.Copy(data, offset, area.ptr, area.size); - offset += area.size; - } - } - } - - #endregion } } diff --git a/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs b/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs index 929693625f..ea776fe881 100644 --- a/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs +++ b/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs @@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Waterbox public abstract class WaterboxCore : IEmulator, IVideoProvider, ISoundProvider, IStatable, IInputPollable, ISaveRam { - protected LibWaterboxCore _core; + private LibWaterboxCore _core; protected PeRunner _exe; protected LibWaterboxCore.MemoryArea[] _memoryAreas; private LibWaterboxCore.EmptyCallback _inputCallback; @@ -153,6 +153,8 @@ namespace BizHawk.Emulation.Cores.Waterbox #region IEmulator protected abstract LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound); + protected virtual void FrameAdvancePost() + { } public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true) { @@ -176,6 +178,8 @@ namespace BizHawk.Emulation.Cores.Waterbox BufferWidth = frame.Width; BufferHeight = frame.Height; _numSamples = frame.Samples; + + FrameAdvancePost(); } } } diff --git a/output/dll/snes9x.wbx.gz b/output/dll/snes9x.wbx.gz index 4e63d5f595..a6f6f9450e 100644 Binary files a/output/dll/snes9x.wbx.gz and b/output/dll/snes9x.wbx.gz differ diff --git a/waterbox/ngp/.vscode/settings.json b/waterbox/ngp/.vscode/settings.json index 1035572a13..3e56d7d118 100644 --- a/waterbox/ngp/.vscode/settings.json +++ b/waterbox/ngp/.vscode/settings.json @@ -1,12 +1,13 @@ -// Place your settings in this file to overwrite default and user settings. -{ - "editor.insertSpaces": false, - "editor.detectIndentation": false, - "files.associations": { - "algorithm": "cpp", - "vector": "cpp", - "xstring": "cpp", - "xutility": "cpp", - "xmemory0": "cpp" - } +// Place your settings in this file to overwrite default and user settings. +{ + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "files.associations": { + "algorithm": "cpp", + "vector": "cpp", + "xstring": "cpp", + "xutility": "cpp", + "xmemory0": "cpp", + "iosfwd": "cpp" + } } \ No newline at end of file diff --git a/waterbox/snes9x b/waterbox/snes9x index 33a7181fa0..06a2bc83c9 160000 --- a/waterbox/snes9x +++ b/waterbox/snes9x @@ -1 +1 @@ -Subproject commit 33a7181fa05a01434f420db3ab826d41140aad9c +Subproject commit 06a2bc83c9288c5efd5872d5d2e4b94f03d6e4b7