From 36be9e9fc760ed5f4d30407b0e89a10905cf4f0a Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sat, 10 Jun 2017 13:43:03 -0400 Subject: [PATCH] snes: waterboxification phase 2 --- .../tools/SNES/SNESGraphicsDebugger.cs | 4 +- .../Consoles/Nintendo/SNES/LibsnesApi.cs | 79 ++++++-- .../Consoles/Nintendo/SNES/LibsnesApi_CMD.cs | 11 -- .../Nintendo/SNES/LibsnesApi_QUERY.cs | 6 +- .../Consoles/Nintendo/SNES/LibsnesApi_SIG.cs | 174 +++++++----------- .../Nintendo/SNES/LibsnesCore.IEmulator.cs | 35 +--- .../SNES/LibsnesCore.IMemoryDomains.cs | 65 ++----- .../Nintendo/SNES/LibsnesCore.IStatable.cs | 47 +---- .../Consoles/Nintendo/SNES/LibsnesCore.cs | 31 +--- waterbox/libco/coswap.s | 6 +- .../bsnes/target-libsnes/libsnes_pwrap.cpp | 35 ++++ 11 files changed, 204 insertions(+), 289 deletions(-) diff --git a/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs b/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs index 733b40c3a5..0ab130c7e0 100644 --- a/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs +++ b/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs @@ -659,9 +659,11 @@ namespace BizHawk.Client.EmuHawk SNESGraphicsDecoder NewDecoder() { //wtf to do? now we need an api all the time + throw new NotImplementedException("TODO"); + /* if (currentSnesCore != null) return new SNESGraphicsDecoder(currentSnesCore.Api, currentSnesCore.CurrPalette); - else return new SNESGraphicsDecoder(currentSnesCore.Api, SnesColors.ColorType.BizHawk); + else return new SNESGraphicsDecoder(currentSnesCore.Api, SnesColors.ColorType.BizHawk);*/ } void RenderPalette() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs index 0efbb9286a..27047f7f91 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -10,28 +10,44 @@ using BizHawk.Common.BizInvoke; namespace BizHawk.Emulation.Cores.Nintendo.SNES { - public unsafe partial class LibsnesApi : IDisposable + public abstract unsafe class CoreImpl { - /*[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] - public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);*/ + [BizImport(CallingConvention.Cdecl, Compatibility = true)] + public abstract IntPtr DllInit(); + [BizImport(CallingConvention.Cdecl, Compatibility = true)] + public abstract void Message(LibsnesApi.eMessage msg); + [BizImport(CallingConvention.Cdecl, Compatibility = true)] + public abstract void CopyBuffer(int id, void* ptr, int size); + [BizImport(CallingConvention.Cdecl, Compatibility = true)] + public abstract void SetBuffer(int id, void* ptr, int size); + } + + public unsafe partial class LibsnesApi : IDisposable, IMonitor + { + static LibsnesApi() + { + if (sizeof(CommStruct) != 232) + { + throw new InvalidOperationException("sizeof(comm)"); + } + } + private PeRunner _exe; private CoreImpl _core; private bool _disposed; private CommStruct* _comm; + private readonly Dictionary _sharedMemoryBlocks = new Dictionary(); - private abstract class CoreImpl + public void Enter() { - [BizImport(CallingConvention.Cdecl, Compatibility = true)] - public abstract IntPtr DllInit(); - [BizImport(CallingConvention.Cdecl, Compatibility = true)] - public abstract void Message(eMessage msg); - [BizImport(CallingConvention.Cdecl, Compatibility = true)] - public abstract void CopyBuffer(int id, void* ptr, int size); - [BizImport(CallingConvention.Cdecl, Compatibility = true)] - public abstract void SetBuffer(int id, void* ptr, int size); + _exe.Enter(); } + public void Exit() + { + _exe.Exit(); + } public LibsnesApi(string dllPath) { @@ -41,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES Path = dllPath, SbrkHeapSizeKB = 32 * 1024, InvisibleHeapSizeKB = 32 * 1024, - MmapHeapSizeKB = 32 * 1024, + MmapHeapSizeKB = 256 * 1024, PlainHeapSizeKB = 32 * 1024, SealedHeapSizeKB = 32 * 1024 }); @@ -54,6 +70,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES { if (!_disposed) { + _disposed = true; _exe.Dispose(); _exe = null; _core = null; @@ -137,9 +154,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES BRR = 0x80, }; - //Dictionary SharedMemoryBlocks = new Dictionary(); - //Dictionary DeallocatedMemoryBlocks = new Dictionary(); - snes_video_refresh_t video_refresh; snes_input_poll_t input_poll; snes_input_state_t input_state; @@ -165,15 +179,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public delegate void snes_trace_t(uint which, string msg); + [StructLayout(LayoutKind.Explicit)] public struct CPURegs { + [FieldOffset(0)] public uint pc; + [FieldOffset(4)] public ushort a, x, y, z, s, d, vector; //7x + [FieldOffset(18)] public byte p, nothing; + [FieldOffset(20)] public uint aa, rd; + [FieldOffset(28)] public byte sp, dp, db, mdr; } + [StructLayout(LayoutKind.Sequential)] public struct LayerEnables { byte _BG1_Prio0, _BG1_Prio1; @@ -197,43 +218,63 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public bool Obj_Prio3 { get { return _Obj_Prio3 != 0; } set { _Obj_Prio3 = (byte)(value ? 1 : 0); } } } + [StructLayout(LayoutKind.Explicit)] struct CommStruct { + [FieldOffset(0)] //the cmd being executed public eMessage cmd; - + [FieldOffset(4)] //the status of the core public eStatus status; - + [FieldOffset(8)] //the SIG or BRK that the core is halted in public eMessage reason; //flexible in/out parameters //these are all "overloaded" a little so it isn't clear what's used for what in for any particular message.. //but I think it will beat having to have some kind of extremely verbose custom layouts for every message + [FieldOffset(16)] public sbyte* str; + [FieldOffset(24)] public void* ptr; + [FieldOffset(32)] public uint id, addr, value, size; + [FieldOffset(48)] public int port, device, index, slot; + [FieldOffset(64)] public int width, height; + [FieldOffset(72)] public int scanline; + [FieldOffset(76)] public fixed int inports[2]; + [FieldOffset(88)] //this should always be used in pairs - public fixed uint buf[3]; //ACTUALLY A POINTER but can't marshal it :( + public fixed long buf[3]; //ACTUALLY A POINTER but can't marshal it :( + [FieldOffset(112)] public fixed int buf_size[3]; + [FieldOffset(128)] //bleck. this is a long so that it can be a 32/64bit pointer public fixed long cdl_ptr[4]; + [FieldOffset(160)] public fixed int cdl_size[4]; + [FieldOffset(176)] public CPURegs cpuregs; + [FieldOffset(208)] public LayerEnables layerEnables; + [FieldOffset(220)] //static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command public SNES_REGION region; + [FieldOffset(224)] public SNES_MAPPER mapper; + [FieldOffset(228)] + public int unused; + //utilities //TODO: make internal, wrap on the API instead of the comm public unsafe string GetAscii() { return _getAscii(str); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs index 37a35f468d..442f25f367 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs @@ -76,16 +76,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES return _comm->GetBool(); } } - - public void CMD_term() - { - _core.Message(eMessage.eMessage_CMD_term); - WaitForCMD(); - } - public void CMD_unload_cartridge() - { - _core.Message(eMessage.eMessage_CMD_unload_cartridge); - WaitForCMD(); - } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_QUERY.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_QUERY.cs index 7fb7d1296c..62c963911e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_QUERY.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_QUERY.cs @@ -31,9 +31,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public byte* QUERY_get_memory_data(SNES_MEMORY id) { string name = QUERY_MemoryNameForId(id); - if (!SharedMemoryBlocks.ContainsKey(name)) return null; - var smb = SharedMemoryBlocks[name]; - return (byte*)smb.Ptr; + IntPtr ret; + _sharedMemoryBlocks.TryGetValue(name, out ret); + return (byte*)ret; } public byte QUERY_peek(SNES_MEMORY id, uint addr) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs index 0110f65951..da1d74c111 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs @@ -8,127 +8,89 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES { bool Handle_SIG(eMessage msg) { - switch (msg) + using (_exe.EnterExit()) { - default: - return false; + switch (msg) + { + default: + return false; - case eMessage.eMessage_SIG_video_refresh: - { - int width = comm->width; - int height = comm->height; - if (video_refresh != null) + case eMessage.eMessage_SIG_video_refresh: { - video_refresh((int*)comm->ptr, width, height); + int width = _comm->width; + int height = _comm->height; + video_refresh?.Invoke((int*)_comm->ptr, width, height); + break; } + case eMessage.eMessage_SIG_input_poll: break; - } - case eMessage.eMessage_SIG_input_poll: - break; - case eMessage.eMessage_SIG_input_state: - { - int port = comm->port; - int device = comm->device; - int index = comm->index; - int id = (int)comm->id; - if (input_state != null) - comm->value = (uint)input_state(port, device, index, id); - break; - } - case eMessage.eMessage_SIG_input_notify: - { - if (input_notify != null) - input_notify(comm->index); - break; - } - case eMessage.eMessage_SIG_audio_flush: - { - uint nsamples = comm->size; - - if (audio_sample != null) + case eMessage.eMessage_SIG_input_state: { - ushort* audiobuffer = ((ushort*)comm->ptr); - for (uint i = 0; i < nsamples; ) + int port = _comm->port; + int device = _comm->device; + int index = _comm->index; + int id = (int)_comm->id; + if (input_state != null) + _comm->value = (uint)input_state(port, device, index, id); + break; + } + case eMessage.eMessage_SIG_input_notify: + { + input_notify?.Invoke(_comm->index); + break; + } + case eMessage.eMessage_SIG_audio_flush: + { + uint nsamples = _comm->size; + + if (audio_sample != null) { - ushort left = audiobuffer[i++]; - ushort right = audiobuffer[i++]; - audio_sample(left, right); + ushort* audiobuffer = ((ushort*)_comm->ptr); + for (uint i = 0; i < nsamples;) + { + ushort left = audiobuffer[i++]; + ushort right = audiobuffer[i++]; + audio_sample(left, right); + } } + + break; } - - break; - } - case eMessage.eMessage_SIG_path_request: - { - int slot = comm->slot; - string hint = comm->GetAscii(); - string ret = hint; - if (pathRequest != null) - hint = pathRequest(slot, hint); - CopyAscii(0, hint); - break; - } - case eMessage.eMessage_SIG_trace_callback: - { - if (traceCallback != null) - traceCallback(comm->value, comm->GetAscii()); - break; - } - case eMessage.eMessage_SIG_allocSharedMemory: - { - var name = comm->GetAscii(); - var size = comm->size; - - if (SharedMemoryBlocks.ContainsKey(name)) + case eMessage.eMessage_SIG_path_request: { - throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name); + int slot = _comm->slot; + string hint = _comm->GetAscii(); + string ret = hint; + if (pathRequest != null) + hint = pathRequest(slot, hint); + CopyAscii(0, hint); + break; } - - //try reusing existing block; dispose it if it exists and if the size doesnt match - SharedMemoryBlock smb = null; - if (DeallocatedMemoryBlocks.ContainsKey(name)) + case eMessage.eMessage_SIG_trace_callback: { - smb = DeallocatedMemoryBlocks[name]; - DeallocatedMemoryBlocks.Remove(name); - if (smb.Size != size) - { - smb.Dispose(); - smb = null; - } + traceCallback?.Invoke(_comm->value, _comm->GetAscii()); + break; } - - //allocate a new block if we have to - if (smb == null) + case eMessage.eMessage_SIG_allocSharedMemory: { - smb = new SharedMemoryBlock(); - smb.Name = name; - smb.Size = (int)size; - smb.BlockName = InstanceName + smb.Name; - smb.Allocate(); - } + // NB: shared memory blocks are allocated on the unmanaged side + var name = _comm->GetAscii(); + var size = _comm->size; + var ptr = _comm->ptr; - comm->ptr = smb.Ptr; - SharedMemoryBlocks[smb.Name] = smb; - CopyAscii(0, smb.BlockName); - break; - } - case eMessage.eMessage_SIG_freeSharedMemory: - { - foreach (var block in SharedMemoryBlocks.Values) - { - if (block.Ptr == comm->ptr) - { - DeallocatedMemoryBlocks[block.Name] = block; - SharedMemoryBlocks.Remove(block.Name); - break; - } - } - break; - } - } //switch(msg) + if (_sharedMemoryBlocks.ContainsKey(name)) + throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name); - Message(eMessage.eMessage_Resume); - return true; + _sharedMemoryBlocks.Add(name, (IntPtr)ptr); + break; + } + case eMessage.eMessage_SIG_freeSharedMemory: + throw new InvalidOperationException("Unexpected call: SIG_freeSharedMemory"); + } //switch(msg) + + _core.Message(eMessage.eMessage_Resume); + return true; + } } } -} \ No newline at end of file +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IEmulator.cs index 9a767cee40..a245fbdbbf 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IEmulator.cs @@ -22,7 +22,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES IsLagFrame = true; - if (!_nocallbacks && _tracer.Enabled) + if (_tracer.Enabled) { //Api.QUERY_set_trace_callback(1<<(int)LibsnesApi.eTRACE.SMP, _tracecb); //TEST -- it works but theres no way to control it from the frontend now @@ -36,21 +36,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES Api.QUERY_set_trace_callback(0,null); } - // for deterministic emulation, save the state we're going to use before frame advance - // don't do this during nocallbacks though, since it's already been done - if (!_nocallbacks && DeterministicEmulation) - { - var ms = new MemoryStream(); - var bw = new BinaryWriter(ms); - bw.Write(CoreSaveState()); - bw.Write(false); // not framezero - var ssc = new SaveController(); - ssc.CopyFrom(controller); - ssc.Serialize(bw); - bw.Close(); - _savestatebuff = ms.ToArray(); - } - // speedup when sound rendering is not needed Api.QUERY_set_audio_sample(rendersound ? _soundcb : null); @@ -104,14 +89,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES } public string SystemId { get; } - - // adelikat: Nasty hack to force new business logic. Compatibility (and Accuracy when fully supported) will ALWAYS be in deterministic mode, - // a consequence is a permanent performance hit to the compatibility core - // Perormance will NEVER be in deterministic mode (and the client side logic will prohibit movie recording on it) - // feos: Nasty hack to a nasty hack. Allow user disable it with a strong warning. - public bool DeterministicEmulation => - _settings.ForceDeterminism - && (CurrentProfile == "Compatibility" || CurrentProfile == "Accuracy"); + public bool DeterministicEmulation => true; public void ResetCounters() { @@ -129,15 +107,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES return; } - _disposed = true; - - Api.CMD_unload_cartridge(); - Api.CMD_term(); - - _resampler.Dispose(); Api.Dispose(); + _resampler.Dispose(); - _currCdl?.Unpin(); + _disposed = true; } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IMemoryDomains.cs index b03c27c1c0..a18902fda2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IMemoryDomains.cs @@ -84,8 +84,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES private unsafe void MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian, int byteSize = 1) { int size = Api.QUERY_get_memory_size(id); - int mask = size - 1; - bool pow2 = Util.IsPowerOfTwo(size); // if this type of memory isnt available, dont make the memory domain (most commonly save ram) if (size == 0) @@ -95,46 +93,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES byte* blockptr = Api.QUERY_get_memory_data(id); - MemoryDomain md; - - if (id == LibsnesApi.SNES_MEMORY.OAM) - { - // OAM is actually two differently sized banks of memory which arent truly considered adjacent. - // maybe a better way to visualize it is with an empty bus and adjacent banks - // so, we just throw away everything above its size of 544 bytes - if (size != 544) - { - throw new InvalidOperationException("oam size isnt 544 bytes.. wtf?"); - } - - md = new MemoryDomainDelegate( - name, - size, - endian, - addr => addr < 544 ? blockptr[addr] : (byte)0x00, - (addr, value) => { if (addr < 544) { blockptr[addr] = value; } }, - byteSize); - } - else if (pow2) - { - md = new MemoryDomainDelegate( - name, - size, - endian, - addr => blockptr[addr & mask], - (addr, value) => blockptr[addr & mask] = value, - byteSize); - } - else - { - md = new MemoryDomainDelegate( - name, - size, - endian, - addr => blockptr[addr % size], - (addr, value) => blockptr[addr % size] = value, - byteSize); - } + var md = new MemoryDomainIntPtrMonitor(name, MemoryDomain.Endian.Little, (IntPtr)blockptr, size, true, byteSize, Api); _memoryDomainList.Add(md); } @@ -152,19 +111,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES var md = new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little, addr => { - var a = FakeBusMap((int)addr); - if (a.HasValue) + using (Api.EnterExit()) { - return blockptr[a.Value]; - } + var a = FakeBusMap((int)addr); + if (a.HasValue) + { + return blockptr[a.Value]; + } - return FakeBusRead((int)addr); + return FakeBusRead((int)addr); + } }, (addr, val) => { - var a = FakeBusMap((int)addr); - if (a.HasValue) - blockptr[a.Value] = val; + using (Api.EnterExit()) + { + var a = FakeBusMap((int)addr); + if (a.HasValue) + blockptr[a.Value] = val; + } }, wordSize: 2); _memoryDomainList.Add(md); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs index c90de87d53..2a3d61bb04 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs @@ -12,26 +12,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public void SaveStateText(TextWriter writer) { - var temp = SaveStateBinary(); + /*var temp = SaveStateBinary(); temp.SaveAsHexFast(writer); writer.WriteLine("Frame {0}", Frame); // we don't parse this, it's only for the client to use - writer.WriteLine("Profile {0}", CurrentProfile); + writer.WriteLine("Profile {0}", CurrentProfile);*/ } public void LoadStateText(TextReader reader) { - string hex = reader.ReadLine(); + /*string hex = reader.ReadLine(); byte[] state = new byte[hex.Length / 2]; state.ReadFromHexFast(hex); LoadStateBinary(new BinaryReader(new MemoryStream(state))); reader.ReadLine(); // Frame # var profile = reader.ReadLine().Split(' ')[1]; - ValidateLoadstateProfile(profile); + ValidateLoadstateProfile(profile);*/ } public void SaveStateBinary(BinaryWriter writer) { - writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState()); + /*writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState()); // other variables writer.Write(IsLagFrame); @@ -39,12 +39,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES writer.Write(Frame); writer.Write(CurrentProfile); - writer.Flush(); + writer.Flush();*/ } public void LoadStateBinary(BinaryReader reader) { - int size = Api.QUERY_serialize_size(); + /*int size = Api.QUERY_serialize_size(); byte[] buf = reader.ReadBytes(size); CoreLoadState(buf); @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES LagCount = reader.ReadInt32(); Frame = reader.ReadInt32(); var profile = reader.ReadString(); - ValidateLoadstateProfile(profile); + ValidateLoadstateProfile(profile);*/ } public byte[] SaveStateBinary() @@ -90,34 +90,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES return ms.ToArray(); } - // handle the unmanaged part of loadstating - private void CoreLoadState(byte[] data) - { - int size = Api.QUERY_serialize_size(); - if (data.Length != size) - { - throw new Exception("Libsnes internal savestate size mismatch!"); - } - - Api.CMD_init(); - - // zero 01-sep-2014 - this approach isn't being used anymore, it's too slow! - // LoadCurrent(); //need to make sure chip roms are reloaded - fixed (byte* pbuf = &data[0]) - Api.CMD_unserialize(new IntPtr(pbuf), size); - } - - - // handle the unmanaged part of savestating - private byte[] CoreSaveState() - { - int size = Api.QUERY_serialize_size(); - byte[] buf = new byte[size]; - fixed (byte* pbuf = &buf[0]) - Api.CMD_serialize(new IntPtr(pbuf), size); - return buf; - } - private void ValidateLoadstateProfile(string profile) { if (profile != CurrentProfile) @@ -125,8 +97,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES throw new InvalidOperationException($"You've attempted to load a savestate made using a different SNES profile ({profile}) than your current configuration ({CurrentProfile}). We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now."); } } - - // most recent internal savestate, for deterministic mode ONLY - private byte[] _savestatebuff; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs index ad63299a0e..046333cebb 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs @@ -58,7 +58,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES _settings = (SnesSettings)settings ?? new SnesSettings(); _syncSettings = (SnesSyncSettings)syncSettings ?? new SnesSyncSettings(); - Api = new LibsnesApi(GetDllPath()) + // TODO: pass profile here + Api = new LibsnesApi(CoreComm.CoreFileProvider.DllPath()) { ReadHook = ReadHook, ExecHook = ExecHook, @@ -169,17 +170,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES SetupMemoryDomains(romData, sgbRomData); - // DeterministicEmulation = deterministicEmulation; // Note we don't respect the value coming in and force it instead - if (DeterministicEmulation) // save frame-0 savestate now - { - var ms = new MemoryStream(); - var bw = new BinaryWriter(ms); - bw.Write(CoreSaveState()); - bw.Write(true); // framezero, so no controller follows and don't frameadvance on load - bw.Close(); - _savestatebuff = ms.ToArray(); - } - if (CurrentProfile == "Compatibility") { ser.Register(_tracer); @@ -198,7 +188,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES private LoadParams _currLoadParams; private SpeexResampler _resampler; private int _timeFrameCounter; - private bool _nocallbacks; // disable all external callbacks. the front end should not even know the core is frame advancing private bool _disposed; public bool IsSGB { get; } @@ -222,7 +211,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES } } - public LibsnesApi Api { get; } + private LibsnesApi Api { get; } public SnesColors.ColorType CurrPalette { get; private set; } @@ -374,20 +363,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES Api.QUERY_set_color_lut((IntPtr)p); } - private string GetDllPath() - { - var exename = "libsneshawk-32-" + CurrentProfile.ToLower() + ".dll"; - - string dllPath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), exename); - - if (!File.Exists(dllPath)) - { - throw new InvalidOperationException("Couldn't locate the DLL for SNES emulation for profile: " + CurrentProfile + ". Please make sure you're using a fresh dearchive of a BizHawk distribution."); - } - - return dllPath; - } - private void ReadHook(uint addr) { MemoryCallbacks.CallReads(addr); diff --git a/waterbox/libco/coswap.s b/waterbox/libco/coswap.s index 6f48858b0c..e9660c462c 100644 --- a/waterbox/libco/coswap.s +++ b/waterbox/libco/coswap.s @@ -2,9 +2,13 @@ section .text global co_swap global __imp_co_swap +; TODO: how to tell GCC it doesn't need this? +align 16 +__imp_co_swap: + dq co_swap + align 16 co_swap: -__imp_co_swap: mov [rdx],rsp mov rsp,[rcx] pop rax diff --git a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp index 9250e1bee2..5708407171 100644 --- a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp +++ b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp @@ -7,6 +7,7 @@ #define LIBSNES_IMPORT #include "snes/snes.hpp" #include "libsnes.hpp" +#include #include @@ -136,6 +137,8 @@ struct CommStruct //the SIG or BRK that the core is halted in eMessage reason; + int32 padding1; + //flexible in/out parameters //these are all "overloaded" a little so it isn't clear what's used for what in for any particular message.. //but I think it will beat having to have some kind of extremely verbose custom layouts for every message @@ -147,10 +150,14 @@ struct CommStruct int32 scanline; SNES::Input::Device inports[2]; + int32 padding2; + //always used in pairs void* buf[3]; int32 buf_size[3]; + int32 padding3; + int64 cdl_ptr[4]; int32 cdl_size[4]; @@ -161,6 +168,8 @@ struct CommStruct uint32 region; uint32 mapper; + int32 padding4; + //=========================================================== //private stuff @@ -282,8 +291,11 @@ void* snes_allocSharedMemory(const char* memtype, size_t amt) //its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?) //if(!running) return NULL; + auto ret = alloc_plain(amt); + comm.str = (char*)memtype; comm.size = amt; + comm.ptr = ret; BREAK(eMessage_SIG_allocSharedMemory); @@ -573,6 +585,29 @@ void new_emuthread() EXPORT void* DllInit() { + #define T(s,n) static_assert(offsetof(CommStruct,s)==n,#n) + T(cmd, 0); + T(status, 4); + T(reason, 8); + T(str, 16); + T(ptr, 24); + T(id, 32); + T(port, 48); + T(width, 64); + T(scanline, 72); + T(inports, 76); + T(buf, 88); + T(buf_size, 112); + T(cdl_ptr, 128); + T(cdl_size, 160); + T(cpuregs, 176); + T(layerEnables, 208); + T(region, 220); + T(mapper, 224); + // start of private stuff + T(privbuf, 232); + #undef T + memset(&comm,0,sizeof(comm)); //make a coroutine thread to run the emulation in. we'll switch back to this cothread when communicating with the frontend