diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj
index 336e342bf1..aebfa68ccb 100644
--- a/BizHawk.Common/BizHawk.Common.csproj
+++ b/BizHawk.Common/BizHawk.Common.csproj
@@ -50,11 +50,14 @@
+
+
+
diff --git a/BizHawk.Common/IPC/IPCRingBuffer.cs b/BizHawk.Common/IPC/IPCRingBuffer.cs
new file mode 100644
index 0000000000..f25afcda7a
--- /dev/null
+++ b/BizHawk.Common/IPC/IPCRingBuffer.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Runtime.InteropServices; //I know.... the p/invoke in here. Lets get rid of it
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.MemoryMappedFiles;
+
+
+namespace BizHawk.Common
+{
+ ///
+ /// a ring buffer suitable for IPC. It uses a spinlock to control access, so overhead can be kept to a minimum.
+ /// you'll probably need to use this in pairs, so it will occupy two threads and degrade entirely if there is less than one processor.
+ ///
+ public unsafe class IPCRingBuffer : IDisposable
+ {
+ MemoryMappedFile mmf;
+ MemoryMappedViewAccessor mmva;
+
+ byte* mmvaPtr;
+ volatile byte* begin;
+ volatile int* head, tail;
+ int bufsize;
+
+ public string Id;
+ public bool Owner;
+
+ ///
+ /// note that a few bytes of the size will be used for a management area
+ ///
+ ///
+ public void Allocate(int size)
+ {
+ Owner = true;
+ Id = SuperGloballyUniqueID.Next();
+ mmf = MemoryMappedFile.CreateNew(Id, size);
+ Setup(size);
+ }
+
+ public void Open(string id)
+ {
+ Id = id;
+ mmf = MemoryMappedFile.OpenExisting(id);
+ Setup(-1);
+ }
+
+ void Setup(int size)
+ {
+ bool init = size != -1;
+
+ mmva = mmf.CreateViewAccessor();
+ byte* tempPtr = null;
+ mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref tempPtr);
+ mmvaPtr = tempPtr;
+
+ //setup management area
+ head = (int*)mmvaPtr;
+ tail = (int*)mmvaPtr + 1;
+ int* bufsizeptr = (int*)mmvaPtr + 2;
+ begin = mmvaPtr + 12;
+
+ if (init)
+ *bufsizeptr = bufsize = size - 12;
+ else bufsize = *bufsizeptr;
+ }
+
+ public void Dispose()
+ {
+ if (mmf == null) return;
+ mmva.Dispose();
+ mmf.Dispose();
+ mmf = null;
+ }
+
+ void WaitForWriteCapacity(int amt)
+ {
+ for (; ; )
+ {
+ //dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty
+ if (Available > amt)
+ return;
+ //this is a greedy spinlock.
+ }
+ }
+
+ public int Available
+ {
+ get
+ {
+ return bufsize - Size;
+ }
+ }
+
+
+ public int Size
+ {
+ get
+ {
+ int h = *head;
+ int t = *tail;
+ int size = h - t;
+ if (size < 0) size += bufsize;
+ else if (size >= bufsize)
+ {
+ //shouldnt be possible for size to be anything but bufsize here, but just in case...
+ if (size > bufsize)
+ throw new InvalidOperationException("Critical error code pickpocket panda! This MUST be reported to the developers!");
+ size = 0;
+ }
+ return size;
+ }
+ }
+
+ int WaitForSomethingToRead()
+ {
+ for (; ; )
+ {
+ int available = Size;
+ if (available > 0)
+ return available;
+ //this is a greedy spinlock.
+ }
+ }
+
+ [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
+ static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
+
+ public void Write(IntPtr ptr, int amt)
+ {
+ byte* bptr = (byte*)ptr;
+ int ofs = 0;
+ while (amt > 0)
+ {
+ int todo = amt;
+
+ //make sure we don't write a big chunk beyond the end of the buffer
+ int remain = bufsize - *head;
+ if (todo > remain) todo = remain;
+
+ //dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer
+ if (todo > bufsize - 1) todo = bufsize - 1;
+
+ //a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares
+ WaitForWriteCapacity(todo);
+
+ //messages are likely to be small. we should probably just loop to copy in here. but for now..
+ CopyMemory(begin + *head, bptr + ofs, (ulong)todo);
+
+ amt -= todo;
+ ofs += todo;
+ *head += todo;
+ if (*head >= bufsize) *head -= bufsize;
+ }
+ }
+
+ public void Read(IntPtr ptr, int amt)
+ {
+ byte* bptr = (byte*)ptr;
+ int ofs = 0;
+ while (amt > 0)
+ {
+ int available = WaitForSomethingToRead();
+ int todo = amt;
+ if (todo > available) todo = available;
+
+ //make sure we don't read a big chunk beyond the end of the buffer
+ int remain = bufsize - *tail;
+ if (todo > remain) todo = remain;
+
+ //messages are likely to be small. we should probably just loop to copy in here. but for now..
+ CopyMemory(bptr + ofs, begin + *tail, (ulong)todo);
+
+ amt -= todo;
+ ofs += todo;
+ *tail += todo;
+ if (*tail >= bufsize) *tail -= bufsize;
+ }
+ }
+
+ public class Tester
+ {
+ Queue shazam = new Queue();
+ string bufid;
+
+ unsafe void a()
+ {
+ var buf = new IPCRingBuffer();
+ buf.Allocate(1024);
+ bufid = buf.Id;
+
+ int ctr = 0;
+ for (; ; )
+ {
+ Random r = new Random(ctr);
+ ctr++;
+ Console.WriteLine("Writing: {0}", ctr);
+
+ byte[] temp = new byte[r.Next(2048) + 1];
+ r.NextBytes(temp);
+ for (int i = 0; i < temp.Length; i++)
+ lock (shazam) shazam.Enqueue(temp[i]);
+ fixed (byte* tempptr = &temp[0])
+ buf.Write((IntPtr)tempptr, temp.Length);
+ //Console.WriteLine("wrote {0}; ringbufsize={1}", temp.Length, buf.Size);
+ }
+ }
+
+ unsafe void b()
+ {
+ var buf = new IPCRingBuffer();
+ buf.Open(bufid);
+
+ int ctr = 0;
+ for (; ; )
+ {
+ Random r = new Random(ctr + 1000);
+ ctr++;
+ Console.WriteLine("Reading : {0}", ctr);
+
+ int tryRead = r.Next(2048) + 1;
+ byte[] temp = new byte[tryRead];
+ fixed (byte* tempptr = &temp[0])
+ buf.Read((IntPtr)tempptr, tryRead);
+ //Console.WriteLine("read {0}; ringbufsize={1}", temp.Length, buf.Size);
+ for (int i = 0; i < temp.Length; i++)
+ {
+ byte b;
+ lock (shazam) b = shazam.Dequeue();
+ Debug.Assert(b == temp[i]);
+ }
+ }
+ }
+
+ public void Test()
+ {
+ var ta = new System.Threading.Thread(a);
+ var tb = new System.Threading.Thread(b);
+ ta.Start();
+ while (bufid == null) { }
+ tb.Start();
+ }
+ }
+ } //class IPCRingBuffer
+
+ ///
+ /// A stream on top of an IPCRingBuffer
+ ///
+ public unsafe class IPCRingBufferStream : Stream
+ {
+ public IPCRingBufferStream(IPCRingBuffer buf)
+ {
+ this.buf = buf;
+ }
+ IPCRingBuffer buf;
+ public override bool CanRead { get { return true; } }
+ public override bool CanSeek { get { return false; } }
+ public override bool CanWrite { get { return true; } }
+ public override void Flush() { }
+ public override long Length { get { throw new NotImplementedException(); } }
+ public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
+ public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
+ public override void SetLength(long value) { throw new NotImplementedException(); }
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (buffer.Length < offset + count) throw new IndexOutOfRangeException();
+ if (buffer.Length != 0)
+ fixed (byte* pbuffer = &buffer[offset])
+ buf.Read((IntPtr)pbuffer, count);
+ return count;
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (buffer.Length < offset + count) throw new IndexOutOfRangeException();
+ if (buffer.Length != 0)
+ fixed (byte* pbuffer = &buffer[offset])
+ buf.Write((IntPtr)pbuffer, count);
+ }
+ } //class IPCRingBufferStream
+
+} //namespace BizHawk.Common
\ No newline at end of file
diff --git a/BizHawk.Common/IPC/SharedMemoryBlock.cs b/BizHawk.Common/IPC/SharedMemoryBlock.cs
new file mode 100644
index 0000000000..785924e0a4
--- /dev/null
+++ b/BizHawk.Common/IPC/SharedMemoryBlock.cs
@@ -0,0 +1,33 @@
+using System;
+using System.IO.MemoryMappedFiles;
+
+namespace BizHawk.Common
+{
+ public unsafe class SharedMemoryBlock : IDisposable
+ {
+ public string Name;
+ public string BlockName;
+ public int Size;
+ public MemoryMappedFile mmf;
+ public MemoryMappedViewAccessor mmva;
+ public byte* Ptr;
+
+ public void Allocate()
+ {
+ //we can't allocate 0 bytes here.. so just allocate 1 byte here if 0 was requested. it should be OK, and we dont have to handle cases where blocks havent been allocated
+ int sizeToAlloc = Size;
+ if (sizeToAlloc == 0) sizeToAlloc = 1;
+ mmf = MemoryMappedFile.CreateNew(BlockName, sizeToAlloc);
+ mmva = mmf.CreateViewAccessor();
+ mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref Ptr);
+ }
+
+ public void Dispose()
+ {
+ if (mmf == null) return;
+ mmva.Dispose();
+ mmf.Dispose();
+ mmf = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Common/SwitcherStream.cs b/BizHawk.Common/SwitcherStream.cs
new file mode 100644
index 0000000000..78d3688b5a
--- /dev/null
+++ b/BizHawk.Common/SwitcherStream.cs
@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+
+namespace BizHawk.Common
+{
+ ///
+ /// This stream redirects all operations to another stream, specified by the user
+ /// You might think you can do this just by changing out the stream instance you operate on, but this was built to facilitate some features which were never built:
+ /// The ability to have the old stream automatically flushed, or for a derived class to manage two streams at a higher level and use these facilities to switch them
+ /// without this subclass's clients knowing about the existence of two streams.
+ /// Well, it could be useful, so here it is.
+ ///
+ public class SwitcherStream : Stream
+ {
+ //switchstream method? flush old stream?
+ Stream CurrStream = null;
+
+ public void SetCurrStream(Stream str) { CurrStream = str; }
+
+ public SwitcherStream()
+ {
+ }
+
+ public override bool CanRead { get { return CurrStream.CanRead; } }
+ public override bool CanSeek { get { return CurrStream.CanSeek; } }
+ public override bool CanWrite { get { return CurrStream.CanWrite; } }
+ public override void Flush()
+ {
+ CurrStream.Flush();
+ }
+
+ public override long Length { get { return CurrStream.Length; } }
+
+ public override long Position
+ {
+ get
+ {
+ return CurrStream.Position;
+ }
+ set
+ {
+ CurrStream.Position = Position;
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return CurrStream.Read(buffer, offset, count);
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return CurrStream.Seek(offset, origin);
+ }
+
+ public override void SetLength(long value)
+ {
+ CurrStream.SetLength(value);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ CurrStream.Write(buffer, offset, count);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/BizHawk.Common/Util.cs b/BizHawk.Common/Util.cs
index b5b6109cd9..b85fdcb8d6 100644
--- a/BizHawk.Common/Util.cs
+++ b/BizHawk.Common/Util.cs
@@ -953,4 +953,24 @@ namespace BizHawk.Common
public class NotTestedException : Exception
{
}
+
+ class SuperGloballyUniqueID
+ {
+ public static string Next()
+ {
+ int myctr;
+ lock (typeof(SuperGloballyUniqueID))
+ myctr = ctr++;
+ return staticPart + "-" + myctr;
+ }
+
+ static SuperGloballyUniqueID()
+ {
+ staticPart = "bizhawk-" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString() + "-" + Guid.NewGuid().ToString();
+ }
+
+ static int ctr;
+ static string staticPart;
+ }
+
}
diff --git a/BizHawk.Emulation.Common/Interfaces/IHookManager.cs b/BizHawk.Emulation.Common/Interfaces/IHookManager.cs
new file mode 100644
index 0000000000..fec12c9b9d
--- /dev/null
+++ b/BizHawk.Emulation.Common/Interfaces/IHookManager.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Common
+{
+ public interface IDebugHookReference
+ {
+ }
+
+ public interface IDebugHookCallback
+ {
+ void Event(int address);
+ }
+
+ public interface IDebugHookManager
+ {
+ IDebugHookReference Register(DebugEvent eventType, int address, int size, IDebugHookCallback cb);
+ void Unregister(IDebugHookReference hookReference);
+ }
+
+ class BasicDebugHookManager
+ {
+ WorkingDictionary> database = new WorkingDictionary>();
+
+ class Reference : IDebugHookReference
+ {
+ public IDebugHookCallback cb;
+ public DebugEvent eventType;
+ public int address, size;
+ }
+
+ public IDebugHookReference Register(DebugEvent eventType, int address, int size, IDebugHookCallback cb)
+ {
+ var r = new Reference();
+ r.cb = cb;
+ r.eventType = eventType;
+ r.address = address;
+ r.size = size;
+ for (int i = 0; i < size; i++)
+ {
+ uint a = (uint)(address + i);
+ database[eventType][a].Add(r);
+ }
+ return r;
+ }
+
+ public void Unregister(IDebugHookReference hookReference)
+ {
+ var hr = hookReference as Reference;
+ for (int i = 0; i < hr.size; i++)
+ {
+ uint a = (uint)(hr.address + i);
+ database[hr.eventType].Remove(a,hr);
+ }
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 1bf249c9c3..ba6064dafa 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -389,6 +389,11 @@
+
+
+
+
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs
index d80b9f4f04..fa605fe9de 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs
@@ -6,296 +6,11 @@ using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO.MemoryMappedFiles;
+using BizHawk.Common;
+
namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
-
- ///
- /// a ring buffer suitable for IPC. It uses a spinlock to control access, so overhead can be kept to a minimum.
- /// you'll probably need to use this in pairs, so it will occupy two threads and degrade entirely if there is less than one processor.
- ///
- unsafe class IPCRingBuffer : IDisposable
- {
- MemoryMappedFile mmf;
- MemoryMappedViewAccessor mmva;
-
- byte* mmvaPtr;
- volatile byte* begin;
- volatile int* head, tail;
- int bufsize;
-
- public string Id;
- public bool Owner;
-
- ///
- /// note that a few bytes of the size will be used for a management area
- ///
- ///
- public void Allocate(int size)
- {
- Owner = true;
- Id = SuperGloballyUniqueID.Next();
- mmf = MemoryMappedFile.CreateNew(Id, size);
- Setup(size);
- }
-
- public void Open(string id)
- {
- Id = id;
- mmf = MemoryMappedFile.OpenExisting(id);
- Setup(-1);
- }
-
- void Setup(int size)
- {
- bool init = size != -1;
-
- mmva = mmf.CreateViewAccessor();
- byte* tempPtr = null;
- mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref tempPtr);
- mmvaPtr = tempPtr;
-
- //setup management area
- head = (int*)mmvaPtr;
- tail = (int*)mmvaPtr + 1;
- int* bufsizeptr = (int*)mmvaPtr + 2;
- begin = mmvaPtr + 12;
-
- if (init)
- *bufsizeptr = bufsize = size - 12;
- else bufsize = *bufsizeptr;
- }
-
- public void Dispose()
- {
- if (mmf == null) return;
- mmva.Dispose();
- mmf.Dispose();
- mmf = null;
- }
-
- void WaitForWriteCapacity(int amt)
- {
- for (; ; )
- {
- //dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty
- if (Available > amt)
- return;
- //this is a greedy spinlock.
- }
- }
-
- public int Available
- {
- get
- {
- return bufsize - Size;
- }
- }
-
-
- public int Size
- {
- get
- {
- int h = *head;
- int t = *tail;
- int size = h - t;
- if (size < 0) size += bufsize;
- else if (size >= bufsize)
- {
- //shouldnt be possible for size to be anything but bufsize here, but just in case...
- if (size > bufsize)
- throw new InvalidOperationException("Critical error code pickpocket panda! This MUST be reported to the developers!");
- size = 0;
- }
- return size;
- }
- }
-
- int WaitForSomethingToRead()
- {
- for (; ; )
- {
- int available = Size;
- if (available > 0)
- return available;
- //this is a greedy spinlock.
- }
- }
-
- [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
- static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
-
- public void Write(IntPtr ptr, int amt)
- {
- byte* bptr = (byte*)ptr;
- int ofs = 0;
- while (amt > 0)
- {
- int todo = amt;
-
- //make sure we don't write a big chunk beyond the end of the buffer
- int remain = bufsize - *head;
- if (todo > remain) todo = remain;
-
- //dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer
- if (todo > bufsize - 1) todo = bufsize - 1;
-
- //a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares
- WaitForWriteCapacity(todo);
-
- //messages are likely to be small. we should probably just loop to copy in here. but for now..
- CopyMemory(begin + *head, bptr + ofs, (ulong)todo);
-
- amt -= todo;
- ofs += todo;
- *head += todo;
- if (*head >= bufsize) *head -= bufsize;
- }
- }
-
- public void Read(IntPtr ptr, int amt)
- {
- byte* bptr = (byte*)ptr;
- int ofs = 0;
- while (amt > 0)
- {
- int available = WaitForSomethingToRead();
- int todo = amt;
- if (todo > available) todo = available;
-
- //make sure we don't read a big chunk beyond the end of the buffer
- int remain = bufsize - *tail;
- if (todo > remain) todo = remain;
-
- //messages are likely to be small. we should probably just loop to copy in here. but for now..
- CopyMemory(bptr + ofs, begin + *tail, (ulong)todo);
-
- amt -= todo;
- ofs += todo;
- *tail += todo;
- if (*tail >= bufsize) *tail -= bufsize;
- }
- }
-
- public class Tester
- {
- Queue shazam = new Queue();
- string bufid;
-
- unsafe void a()
- {
- var buf = new IPCRingBuffer();
- buf.Allocate(1024);
- bufid = buf.Id;
-
- int ctr = 0;
- for (; ; )
- {
- Random r = new Random(ctr);
- ctr++;
- Console.WriteLine("Writing: {0}", ctr);
-
- byte[] temp = new byte[r.Next(2048) + 1];
- r.NextBytes(temp);
- for (int i = 0; i < temp.Length; i++)
- lock (shazam) shazam.Enqueue(temp[i]);
- fixed (byte* tempptr = &temp[0])
- buf.Write((IntPtr)tempptr, temp.Length);
- //Console.WriteLine("wrote {0}; ringbufsize={1}", temp.Length, buf.Size);
- }
- }
-
- unsafe void b()
- {
- var buf = new IPCRingBuffer();
- buf.Open(bufid);
-
- int ctr = 0;
- for (; ; )
- {
- Random r = new Random(ctr + 1000);
- ctr++;
- Console.WriteLine("Reading : {0}", ctr);
-
- int tryRead = r.Next(2048) + 1;
- byte[] temp = new byte[tryRead];
- fixed (byte* tempptr = &temp[0])
- buf.Read((IntPtr)tempptr, tryRead);
- //Console.WriteLine("read {0}; ringbufsize={1}", temp.Length, buf.Size);
- for (int i = 0; i < temp.Length; i++)
- {
- byte b;
- lock (shazam) b = shazam.Dequeue();
- Debug.Assert(b == temp[i]);
- }
- }
- }
-
- public void Test()
- {
- var ta = new System.Threading.Thread(a);
- var tb = new System.Threading.Thread(b);
- ta.Start();
- while (bufid == null) { }
- tb.Start();
- }
- }
- }
-
- unsafe class IPCRingBufferStream : Stream
- {
- public IPCRingBufferStream(IPCRingBuffer buf)
- {
- this.buf = buf;
- }
- IPCRingBuffer buf;
- public override bool CanRead { get { return true; } }
- public override bool CanSeek { get { return false; } }
- public override bool CanWrite { get { return true; } }
- public override void Flush() { }
- public override long Length { get { throw new NotImplementedException(); } }
- public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
- public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
- public override void SetLength(long value) { throw new NotImplementedException(); }
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (buffer.Length < offset + count) throw new IndexOutOfRangeException();
- if(buffer.Length != 0)
- fixed (byte* pbuffer = &buffer[offset])
- buf.Read((IntPtr)pbuffer, count);
- return count;
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (buffer.Length < offset + count) throw new IndexOutOfRangeException();
- if(buffer.Length != 0)
- fixed (byte* pbuffer = &buffer[offset])
- buf.Write((IntPtr)pbuffer, count);
- }
- }
-
- class SuperGloballyUniqueID
- {
- public static string Next()
- {
- int myctr;
- lock (typeof(SuperGloballyUniqueID))
- myctr = ctr++;
- return staticPart + "-" + myctr;
- }
-
- static SuperGloballyUniqueID()
- {
- staticPart = "bizhawk-" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString() + "-" + Guid.NewGuid().ToString();
- }
-
- static int ctr;
- static string staticPart;
- }
-
- public unsafe class LibsnesApi : IDisposable
+ public unsafe partial class LibsnesApi : IDisposable
{
//this wouldve been the ideal situation to learn protocol buffers, but since the number of messages here is so limited, it took less time to roll it by hand.
//todo - could optimize a lot of the apis once we decide to commit to this. will we? then we wont be able to debug bsnes as well
@@ -316,6 +31,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
//todo - collapse input messages to one IPC operation. right now theresl ike 30 of them
//todo - collect all memory block names whenever a memory block is alloc/dealloced. that way we avoid the overhead when using them for gui stuff (gfx debugger, hex editor)
+
string InstanceName;
Process process;
NamedPipeServerStream pipe;
@@ -329,79 +45,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
SwitcherStream rstream, wstream;
bool bufio;
- public enum eMessage : int
- {
- eMessage_Complete,
-
- eMessage_snes_library_id,
- eMessage_snes_library_revision_major,
- eMessage_snes_library_revision_minor,
-
- eMessage_snes_init,
- eMessage_snes_power,
- eMessage_snes_reset,
- eMessage_snes_run,
- eMessage_snes_term,
- eMessage_snes_unload_cartridge,
-
- //snes_set_cartridge_basename, //not used
-
- eMessage_snes_load_cartridge_normal,
- eMessage_snes_load_cartridge_super_game_boy,
-
- //incoming from bsnes
- eMessage_snes_cb_video_refresh,
- eMessage_snes_cb_input_poll,
- eMessage_snes_cb_input_state,
- eMessage_snes_cb_input_notify,
- eMessage_snes_cb_audio_sample,
- eMessage_snes_cb_scanlineStart,
- eMessage_snes_cb_path_request,
- eMessage_snes_cb_trace_callback,
-
- eMessage_snes_get_region,
-
- eMessage_snes_get_memory_size,
- eMessage_snes_get_memory_data,
- eMessage_peek,
- eMessage_poke,
-
- eMessage_snes_serialize_size,
-
- eMessage_snes_serialize,
- eMessage_snes_unserialize,
-
- eMessage_snes_poll_message,
- eMessage_snes_dequeue_message,
-
- eMessage_snes_set_color_lut,
-
- eMessage_snes_enable_trace,
- eMessage_snes_enable_scanline,
- eMessage_snes_enable_audio,
- eMessage_snes_set_layer_enable,
- eMessage_snes_set_backdropColor,
- eMessage_snes_peek_logical_register,
-
- eMessage_snes_allocSharedMemory,
- eMessage_snes_freeSharedMemory,
- eMessage_GetMemoryIdName,
-
- eMessage_SetBuffer,
- eMessage_BeginBufferIO,
- eMessage_EndBufferIO,
-
- eMessage_set_state_hook_exec,
- eMessage_set_state_hook_read,
- eMessage_set_state_hook_write,
- eMessage_set_state_hook_nmi,
- eMessage_set_state_hook_irq,
- eMessage_snes_cb_hook_exec,
- eMessage_snes_cb_hook_read,
- eMessage_snes_cb_hook_write,
- eMessage_snes_cb_hook_nmi,
- eMessage_snes_cb_hook_irq,
- };
+ [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
+ public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
static bool DryRun(string exePath)
{
@@ -485,60 +130,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
bwPipe.Flush();
}
- class SwitcherStream : Stream
- {
- //switchstream method? flush old stream?
- Stream CurrStream = null;
-
- public void SetCurrStream(Stream str) { CurrStream = str; }
-
- public SwitcherStream()
- {
- }
-
- public override bool CanRead { get { return CurrStream.CanRead; } }
- public override bool CanSeek { get { return CurrStream.CanSeek; } }
- public override bool CanWrite { get { return CurrStream.CanWrite; } }
- public override void Flush()
- {
- CurrStream.Flush();
- }
-
- public override long Length { get { return CurrStream.Length; } }
-
- public override long Position
- {
- get
- {
- return CurrStream.Position;
- }
- set
- {
- CurrStream.Position = Position;
- }
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return CurrStream.Read(buffer, offset, count);
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- return CurrStream.Seek(offset, origin);
- }
-
- public override void SetLength(long value)
- {
- CurrStream.SetLength(value);
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- CurrStream.Write(buffer, offset, count);
- }
- }
-
public void Dispose()
{
process.Kill();
@@ -596,6 +187,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
void WritePipeMessage(eMessage msg)
{
if(!bufio) MessageCounter++;
+ //Console.WriteLine("write pipe message: " + msg);
bwPipe.Write((int)msg);
bwPipe.Flush();
}
@@ -612,414 +204,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
return System.Text.ASCIIEncoding.ASCII.GetString(bytes);
}
- public string snes_library_id()
- {
- WritePipeMessage(eMessage.eMessage_snes_library_id);
- return ReadPipeString();
- }
-
-
- public uint snes_library_revision_major()
- {
- WritePipeMessage(eMessage.eMessage_snes_library_revision_major);
- return brPipe.ReadUInt32();
- }
-
- public uint snes_library_revision_minor()
- {
- WritePipeMessage(eMessage.eMessage_snes_library_revision_minor);
- return brPipe.ReadUInt32();
- }
-
- public void snes_init()
- {
- WritePipeMessage(eMessage.eMessage_snes_init);
- WaitForCompletion();
- }
- public void snes_power() { WritePipeMessage(eMessage.eMessage_snes_power); }
- public void snes_reset() { WritePipeMessage(eMessage.eMessage_snes_reset); }
- public void snes_run()
- {
- WritePipeMessage(eMessage.eMessage_snes_run);
- WaitForCompletion();
- }
- public void snes_term() { WritePipeMessage(eMessage.eMessage_snes_term); }
- public void snes_unload_cartridge() { WritePipeMessage(eMessage.eMessage_snes_unload_cartridge); }
-
- public void snes_set_state_hook_exec(bool state)
- {
- WritePipeMessage(eMessage.eMessage_set_state_hook_exec);
- bwPipe.Write(state);
- }
-
- public void snes_set_state_hook_read(bool state)
- {
- WritePipeMessage(eMessage.eMessage_set_state_hook_read);
- bwPipe.Write(state);
- }
-
- public void snes_set_state_hook_write(bool state)
- {
- WritePipeMessage(eMessage.eMessage_set_state_hook_write);
- bwPipe.Write(state);
- }
-
- public bool snes_load_cartridge_super_game_boy(string rom_xml, byte[] rom_data, uint rom_size, string dmg_xml, byte[] dmg_data, uint dmg_size)
- {
- WritePipeMessage(eMessage.eMessage_snes_load_cartridge_super_game_boy);
- WritePipeString(rom_xml ?? "");
- WritePipeBlob(rom_data);
- WritePipeString(rom_xml ?? "");
- WritePipeBlob(dmg_data);
- //not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
- WaitForCompletion();
- bool ret = brPipe.ReadBoolean();
- return ret;
- }
-
- public bool snes_load_cartridge_normal(byte[] rom_xml, byte[] rom_data)
- {
- WritePipeMessage(eMessage.eMessage_snes_load_cartridge_normal);
- WritePipeBlob(rom_xml ?? new byte[0]);
- WritePipeBlob(rom_data ?? new byte[0]);
- //not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
- WaitForCompletion();
- bool ret = brPipe.ReadBoolean();
- return ret;
- }
-
- public SNES_REGION snes_get_region()
- {
- WritePipeMessage(eMessage.eMessage_snes_get_region);
- return (SNES_REGION)brPipe.ReadByte();
- }
-
- public int snes_get_memory_size(SNES_MEMORY id)
- {
- WritePipeMessage(eMessage.eMessage_snes_get_memory_size);
- bwPipe.Write((int)id);
- bwPipe.Flush();
- return brPipe.ReadInt32();
- }
-
- string MemoryNameForId(SNES_MEMORY id)
- {
- WritePipeMessage(eMessage.eMessage_GetMemoryIdName);
- bwPipe.Write((uint)id);
- bwPipe.Flush();
- return ReadPipeString();
- }
-
- public byte* snes_get_memory_data(SNES_MEMORY id)
- {
- string name = MemoryNameForId(id);
- var smb = SharedMemoryBlocks[name];
- return (byte*)smb.Ptr;
- }
-
- public byte peek(SNES_MEMORY id, uint addr)
- {
- WritePipeMessage(eMessage.eMessage_peek);
- bwPipe.Write((uint)id);
- bwPipe.Write(addr);
- bwPipe.Flush();
- return brPipe.ReadByte();
- }
- public void poke(SNES_MEMORY id, uint addr, byte val)
- {
- WritePipeMessage(eMessage.eMessage_poke);
- bwPipe.Write((uint)id);
- bwPipe.Write(addr);
- bwPipe.Write(val);
- bwPipe.Flush();
- }
-
- public int snes_serialize_size()
- {
- WritePipeMessage(eMessage.eMessage_snes_serialize_size);
- return brPipe.ReadInt32();
- }
-
- [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
- public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
-
- public bool snes_serialize(IntPtr data, int size)
- {
- WritePipeMessage(eMessage.eMessage_snes_serialize);
- bwPipe.Write(size);
- bwPipe.Write(0); //mapped memory location to serialize to
- bwPipe.Flush();
- WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
- bool ret = brPipe.ReadBoolean();
- if (ret)
- {
- CopyMemory(data.ToPointer(), mmvaPtr, (ulong)size);
- }
- return ret;
- }
-
- public bool snes_unserialize(IntPtr data, int size)
- {
- WritePipeMessage(eMessage.eMessage_snes_unserialize);
- CopyMemory(mmvaPtr, data.ToPointer(), (ulong)size);
- bwPipe.Write(size);
- bwPipe.Write(0); //mapped memory location to serialize from
- bwPipe.Flush();
- WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
- bool ret = brPipe.ReadBoolean();
- return ret;
- }
-
- int snes_poll_message()
- {
- WritePipeMessage(eMessage.eMessage_snes_poll_message);
- return brPipe.ReadInt32();
- }
-
- public bool HasMessage { get { return snes_poll_message() != -1; } }
-
-
- public string DequeueMessage()
- {
- WritePipeMessage(eMessage.eMessage_snes_dequeue_message);
- return ReadPipeString();
- }
-
- public void snes_set_color_lut(IntPtr colors)
- {
- int len = 4 * 16 * 32768;
- byte[] buf = new byte[len];
- Marshal.Copy(colors, buf, 0, len);
-
- WritePipeMessage(eMessage.eMessage_snes_set_color_lut);
- WritePipeBlob(buf);
- }
-
- public void snes_set_layer_enable(int layer, int priority, bool enable)
- {
- WritePipeMessage(eMessage.eMessage_snes_set_layer_enable);
- bwPipe.Write(layer);
- bwPipe.Write(priority);
- bwPipe.Write(enable);
- bwPipe.Flush();
- }
-
- public void snes_set_backdropColor(int backdropColor)
- {
- WritePipeMessage(eMessage.eMessage_snes_set_backdropColor);
- bwPipe.Write(backdropColor);
- bwPipe.Flush();
- }
-
- public int snes_peek_logical_register(SNES_REG reg)
- {
- WritePipeMessage(eMessage.eMessage_snes_peek_logical_register);
- bwPipe.Write((int)reg);
- bwPipe.Flush();
- return brPipe.ReadInt32();
- }
-
void WaitForCompletion()
{
for (; ; )
{
var msg = ReadPipeMessage();
if (!bufio) MessageCounter++;
- //Console.WriteLine(msg);
+ //Console.WriteLine("read pipe message: " + msg);
+
+ if (msg == eMessage.eMessage_BRK_Complete)
+ return;
+
+ //this approach is slower than having one big case. but, its easier to manage. once the code is stable, someone could clean it up (probably creating a delegate table would be best)
+ if (Handle_SIG(msg)) continue;
+ if (Handle_BRK(msg)) continue;
switch (msg)
{
case eMessage.eMessage_Complete:
return;
-
- case eMessage.eMessage_snes_cb_video_refresh:
- {
- int width = brPipe.ReadInt32();
- int height = brPipe.ReadInt32();
- bwPipe.Write(0); //offset in mapped memory buffer
- bwPipe.Flush();
- brPipe.ReadBoolean(); //dummy synchronization
- if (video_refresh != null)
- {
- video_refresh((int*)mmvaPtr, width, height);
- }
- break;
- }
- case eMessage.eMessage_snes_cb_input_poll:
- break;
- case eMessage.eMessage_snes_cb_input_state:
- {
- int port = brPipe.ReadInt32();
- int device = brPipe.ReadInt32();
- int index = brPipe.ReadInt32();
- int id = brPipe.ReadInt32();
- ushort ret = 0;
- if (input_state != null)
- ret = input_state(port, device, index, id);
- bwPipe.Write(ret);
- bwPipe.Flush();
- break;
- }
- case eMessage.eMessage_snes_cb_input_notify:
- {
- int index = brPipe.ReadInt32();
- if (input_notify != null)
- input_notify(index);
- break;
- }
- case eMessage.eMessage_snes_cb_audio_sample:
- {
- int nsamples = brPipe.ReadInt32();
- bwPipe.Write(0); //location to store audio buffer in
- bwPipe.Flush();
- brPipe.ReadInt32(); //dummy synchronization
-
- if (audio_sample != null)
- {
- ushort* audiobuffer = ((ushort*)mmvaPtr);
- for (int i = 0; i < nsamples; )
- {
- ushort left = audiobuffer[i++];
- ushort right = audiobuffer[i++];
- audio_sample(left, right);
- }
- }
-
- bwPipe.Write(0); //dummy synchronization
- bwPipe.Flush();
- brPipe.ReadInt32(); //dummy synchronization
- break;
- }
- case eMessage.eMessage_snes_cb_scanlineStart:
- {
- int line = brPipe.ReadInt32();
- if (scanlineStart != null)
- scanlineStart(line);
-
- //we have to notify the unmanaged process that we're done peeking thruogh its memory and whatnot so it can proceed with emulation
- WritePipeMessage(eMessage.eMessage_Complete);
- break;
- }
- case eMessage.eMessage_snes_cb_path_request:
- {
- int slot = brPipe.ReadInt32();
- string hint = ReadPipeString();
- string ret = hint;
- if (pathRequest != null)
- hint = pathRequest(slot, hint);
- WritePipeString(hint);
- break;
- }
- case eMessage.eMessage_snes_cb_trace_callback:
- {
- var trace = ReadPipeString();
- if (traceCallback != null)
- traceCallback(trace);
- break;
- }
- case eMessage.eMessage_snes_allocSharedMemory:
- {
- var name = ReadPipeString();
- var size = brPipe.ReadInt32();
-
- if (SharedMemoryBlocks.ContainsKey(name))
- {
- throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name);
- }
-
- //try reusing existing block; dispose it if it exists and if the size doesnt match
- SharedMemoryBlock smb = null;
- if (DeallocatedMemoryBlocks.ContainsKey(name))
- {
- smb = DeallocatedMemoryBlocks[name];
- DeallocatedMemoryBlocks.Remove(name);
- if (smb.Size != size)
- {
- smb.Dispose();
- smb = null;
- }
- }
-
- //allocate a new block if we have to
- if(smb == null)
- {
- smb = new SharedMemoryBlock();
- smb.Name = name;
- smb.Size = size;
- smb.BlockName = InstanceName + smb.Name;
- smb.Allocate();
- }
-
- SharedMemoryBlocks[smb.Name] = smb;
- WritePipeString(smb.BlockName);
- break;
- }
- case eMessage.eMessage_snes_freeSharedMemory:
- {
- string name = ReadPipeString();
- var smb = SharedMemoryBlocks[name];
- DeallocatedMemoryBlocks[name] = smb;
- SharedMemoryBlocks.Remove(name);
- break;
- }
-
- case eMessage.eMessage_snes_cb_hook_exec:
- {
- var addr = brPipe.ReadInt32();
- ExecHook((uint)addr);
- break;
- }
- case eMessage.eMessage_snes_cb_hook_read:
- {
- var addr = brPipe.ReadInt32();
- ReadHook((uint)addr);
- break;
- }
- case eMessage.eMessage_snes_cb_hook_write:
- {
- var addr = brPipe.ReadInt32();
- var value = brPipe.ReadByte();
- WriteHook((uint)addr,value);
- break;
- }
- case eMessage.eMessage_snes_cb_hook_nmi:
- break;
- case eMessage.eMessage_snes_cb_hook_irq:
- break;
}
}
- } //WaitForCompletion()
+ }
public Action ReadHook, ExecHook;
public Action WriteHook;
- class SharedMemoryBlock : IDisposable
- {
- public string Name;
- public string BlockName;
- public int Size;
- public MemoryMappedFile mmf;
- public MemoryMappedViewAccessor mmva;
- public byte* Ptr;
-
- public void Allocate()
- {
- //we can't allocate 0 bytes here.. so just allocate 1 byte here if 0 was requested. it should be OK, and we dont have to handle cases where blocks havent been allocated
- int sizeToAlloc = Size;
- if (sizeToAlloc == 0) sizeToAlloc = 1;
- mmf = MemoryMappedFile.CreateNew(BlockName, sizeToAlloc);
- mmva = mmf.CreateViewAccessor();
- mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref Ptr);
- }
-
- public void Dispose()
- {
- if (mmf == null) return;
- mmva.Dispose();
- mmf.Dispose();
- mmf = null;
- }
- }
-
Dictionary SharedMemoryBlocks = new Dictionary();
Dictionary DeallocatedMemoryBlocks = new Dictionary();
@@ -1032,32 +241,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
snes_path_request_t pathRequest;
snes_trace_t traceCallback;
- public void snes_set_video_refresh(snes_video_refresh_t video_refresh) { this.video_refresh = video_refresh; }
- public void snes_set_input_poll(snes_input_poll_t input_poll) { this.input_poll = input_poll; }
- public void snes_set_input_state(snes_input_state_t input_state) { this.input_state = input_state; }
- public void snes_set_input_notify(snes_input_notify_t input_notify) { this.input_notify = input_notify; }
- public void snes_set_audio_sample(snes_audio_sample_t audio_sample)
- {
- this.audio_sample = audio_sample;
- WritePipeMessage(eMessage.eMessage_snes_enable_audio);
- bwPipe.Write(audio_sample != null);
- bwPipe.Flush();
- }
- public void snes_set_path_request(snes_path_request_t pathRequest) { this.pathRequest = pathRequest; }
- public void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart)
- {
- this.scanlineStart = scanlineStart;
- WritePipeMessage(eMessage.eMessage_snes_enable_scanline);
- bwPipe.Write(scanlineStart != null);
- bwPipe.Flush();
- }
- public void snes_set_trace_callback(snes_trace_t callback)
- {
- this.traceCallback = callback;
- WritePipeMessage(eMessage.eMessage_snes_enable_trace);
- bwPipe.Write(callback != null);
- bwPipe.Flush();
- }
+ public void QUERY_set_video_refresh(snes_video_refresh_t video_refresh) { this.video_refresh = video_refresh; }
+ public void QUERY_set_input_poll(snes_input_poll_t input_poll) { this.input_poll = input_poll; }
+ public void QUERY_set_input_state(snes_input_state_t input_state) { this.input_state = input_state; }
+ public void QUERY_set_input_notify(snes_input_notify_t input_notify) { this.input_notify = input_notify; }
+ public void QUERY_set_path_request(snes_path_request_t pathRequest) { this.pathRequest = pathRequest; }
public delegate void snes_video_refresh_t(int* data, int width, int height);
public delegate void snes_input_poll_t();
@@ -1068,147 +256,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public delegate string snes_path_request_t(int slot, string hint);
public delegate void snes_trace_t(string msg);
- public enum SNES_REG : int
+ public void SPECIAL_Resume()
{
- //$2105
- BG_MODE = 0,
- BG3_PRIORITY = 1,
- BG1_TILESIZE = 2,
- BG2_TILESIZE = 3,
- BG3_TILESIZE = 4,
- BG4_TILESIZE = 5,
- //$2107
- BG1_SCADDR = 10,
- BG1_SCSIZE = 11,
- //$2108
- BG2_SCADDR = 12,
- BG2_SCSIZE = 13,
- //$2109
- BG3_SCADDR = 14,
- BG3_SCSIZE = 15,
- //$210A
- BG4_SCADDR = 16,
- BG4_SCSIZE = 17,
- //$210B
- BG1_TDADDR = 20,
- BG2_TDADDR = 21,
- //$210C
- BG3_TDADDR = 22,
- BG4_TDADDR = 23,
- //$2133 SETINI
- SETINI_MODE7_EXTBG = 30,
- SETINI_HIRES = 31,
- SETINI_OVERSCAN = 32,
- SETINI_OBJ_INTERLACE = 33,
- SETINI_SCREEN_INTERLACE = 34,
- //$2130 CGWSEL
- CGWSEL_COLORMASK = 40,
- CGWSEL_COLORSUBMASK = 41,
- CGWSEL_ADDSUBMODE = 42,
- CGWSEL_DIRECTCOLOR = 43,
- //$2101 OBSEL
- OBSEL_NAMEBASE = 50,
- OBSEL_NAMESEL = 51,
- OBSEL_SIZE = 52,
- //$2131 CGADSUB
- CGADSUB_MODE = 60,
- CGADSUB_HALF = 61,
- CGADSUB_BG4 = 62,
- CGADSUB_BG3 = 63,
- CGADSUB_BG2 = 64,
- CGADSUB_BG1 = 65,
- CGADSUB_OBJ = 66,
- CGADSUB_BACKDROP = 67,
- //$212C TM
- TM_BG1 = 70,
- TM_BG2 = 71,
- TM_BG3 = 72,
- TM_BG4 = 73,
- TM_OBJ = 74,
- //$212D TM
- TS_BG1 = 80,
- TS_BG2 = 81,
- TS_BG3 = 82,
- TS_BG4 = 83,
- TS_OBJ = 84,
- //Mode7 regs
- M7SEL_REPEAT = 90,
- M7SEL_HFLIP = 91,
- M7SEL_VFLIP = 92,
- M7A = 93,
- M7B = 94,
- M7C = 95,
- M7D = 96,
- M7X = 97,
- M7Y = 98,
- //BG scroll regs
- BG1HOFS = 100,
- BG1VOFS = 101,
- BG2HOFS = 102,
- BG2VOFS = 103,
- BG3HOFS = 104,
- BG3VOFS = 105,
- BG4HOFS = 106,
- BG4VOFS = 107,
- M7HOFS = 108,
- M7VOFS = 109,
- }
-
- public enum SNES_MEMORY : uint
- {
- CARTRIDGE_RAM = 0,
- CARTRIDGE_RTC = 1,
- BSX_RAM = 2,
- BSX_PRAM = 3,
- SUFAMI_TURBO_A_RAM = 4,
- SUFAMI_TURBO_B_RAM = 5,
- SGB_CARTRAM = 6,
- SGB_RTC = 7,
- SGB_WRAM = 8,
- SGB_HRAM = 9,
-
- WRAM = 100,
- APURAM = 101,
- VRAM = 102,
- OAM = 103,
- CGRAM = 104,
-
- SYSBUS = 200,
- LOGICAL_REGS = 201
- }
-
- public enum SNES_REGION : byte
- {
- NTSC = 0,
- PAL = 1,
- }
-
- public enum SNES_DEVICE : uint
- {
- NONE = 0,
- JOYPAD = 1,
- MULTITAP = 2,
- MOUSE = 3,
- SUPER_SCOPE = 4,
- JUSTIFIER = 5,
- JUSTIFIERS = 6,
- SERIAL_CABLE = 7
- }
-
- public enum SNES_DEVICE_ID : uint
- {
- JOYPAD_B = 0,
- JOYPAD_Y = 1,
- JOYPAD_SELECT = 2,
- JOYPAD_START = 3,
- JOYPAD_UP = 4,
- JOYPAD_DOWN = 5,
- JOYPAD_LEFT = 6,
- JOYPAD_RIGHT = 7,
- JOYPAD_A = 8,
- JOYPAD_X = 9,
- JOYPAD_L = 10,
- JOYPAD_R = 11
+ WritePipeMessage(eMessage.eMessage_CMD_run);
}
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_BRK.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_BRK.cs
new file mode 100644
index 0000000000..c0f9c1271e
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_BRK.cs
@@ -0,0 +1,43 @@
+using System;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ unsafe partial class LibsnesApi
+ {
+ bool Handle_BRK(eMessage msg)
+ {
+ switch (msg)
+ {
+ default:
+ return false;
+
+ case eMessage.eMessage_BRK_hook_exec:
+ {
+ var addr = brPipe.ReadInt32();
+ ExecHook((uint)addr);
+ break;
+ }
+ case eMessage.eMessage_BRK_hook_read:
+ {
+ var addr = brPipe.ReadInt32();
+ ReadHook((uint)addr);
+ break;
+ }
+ case eMessage.eMessage_BRK_hook_write:
+ {
+ var addr = brPipe.ReadInt32();
+ var value = brPipe.ReadByte();
+ WriteHook((uint)addr, value);
+ break;
+ }
+ case eMessage.eMessage_BRK_hook_nmi:
+ break;
+ case eMessage.eMessage_BRK_hook_irq:
+ break;
+ } //switch(msg)
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs
new file mode 100644
index 0000000000..f24d5ea855
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_CMD.cs
@@ -0,0 +1,98 @@
+using System;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ unsafe partial class LibsnesApi
+ {
+ public bool CMD_serialize(IntPtr data, int size)
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_serialize);
+ bwPipe.Write(size);
+ bwPipe.Write(0); //mapped memory location to serialize to
+ bwPipe.Flush();
+ WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
+ bool ret = brPipe.ReadBoolean();
+ if (ret)
+ {
+ CopyMemory(data.ToPointer(), mmvaPtr, (ulong)size);
+ }
+ return ret;
+ }
+
+ public bool CMD_unserialize(IntPtr data, int size)
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_unserialize);
+ CopyMemory(mmvaPtr, data.ToPointer(), (ulong)size);
+ bwPipe.Write(size);
+ bwPipe.Write(0); //mapped memory location to serialize from
+ bwPipe.Flush();
+ WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
+ bool ret = brPipe.ReadBoolean();
+ return ret;
+ }
+
+ public void CMD_init()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_init);
+ WaitForCompletion();
+ }
+ public void CMD_power()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_power);
+ WaitForCompletion();
+ }
+ public void CMD_reset()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_reset);
+ WaitForCompletion();
+ }
+
+ ///
+ /// This is a high-level run command. It runs for one frame (right now) and blocks until the frame is done.
+ /// If any BRK is received, it will be handled before returning from this function.
+ ///
+ public void CMD_run()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_run);
+ WaitForCompletion();
+ }
+
+ public bool CMD_load_cartridge_super_game_boy(string rom_xml, byte[] rom_data, uint rom_size, string dmg_xml, byte[] dmg_data, uint dmg_size)
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_load_cartridge_super_game_boy);
+ WritePipeString(rom_xml ?? "");
+ WritePipeBlob(rom_data);
+ WritePipeString(rom_xml ?? "");
+ WritePipeBlob(dmg_data);
+ //not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
+ WaitForCompletion();
+ bool ret = brPipe.ReadBoolean();
+ return ret;
+ }
+
+ public bool CMD_load_cartridge_normal(byte[] rom_xml, byte[] rom_data)
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_load_cartridge_normal);
+ WritePipeBlob(rom_xml ?? new byte[0]);
+ WritePipeBlob(rom_data ?? new byte[0]);
+ //not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
+ WaitForCompletion();
+ bool ret = brPipe.ReadBoolean();
+ return ret;
+ }
+
+ public void CMD_term()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_term);
+ WaitForCompletion();
+ }
+ public void CMD_unload_cartridge()
+ {
+ WritePipeMessage(eMessage.eMessage_CMD_unload_cartridge);
+ WaitForCompletion();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs
new file mode 100644
index 0000000000..3105d1d8c4
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs
@@ -0,0 +1,216 @@
+using System;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ unsafe partial class LibsnesApi
+ {
+ public enum eMessage : int
+ {
+ eMessage_Complete,
+ eMessage_SetBuffer,
+ eMessage_BeginBufferIO,
+ eMessage_EndBufferIO,
+
+ eMessage_QUERY_library_id,
+ eMessage_QUERY_library_revision_major,
+ eMessage_QUERY_library_revision_minor,
+ eMessage_QUERY_get_region,
+ eMessage_QUERY_get_memory_size,
+ eMessage_QUERY_get_memory_data, //note: this function isnt used and hasnt been tested in a while
+ eMessage_QUERY_peek,
+ eMessage_QUERY_poke,
+ eMessage_QUERY_serialize_size,
+ eMessage_QUERY_poll_message,
+ eMessage_QUERY_dequeue_message,
+ eMessage_QUERY_set_color_lut,
+ eMessage_QUERY_GetMemoryIdName,
+ eMessage_QUERY_state_hook_exec,
+ eMessage_QUERY_state_hook_read,
+ eMessage_QUERY_state_hook_write,
+ eMessage_QUERY_state_hook_nmi,
+ eMessage_QUERY_state_hook_irq,
+ eMessage_QUERY_enable_trace,
+ eMessage_QUERY_enable_scanline,
+ eMessage_QUERY_enable_audio,
+ eMessage_QUERY_set_layer_enable,
+ eMessage_QUERY_set_backdropColor,
+ eMessage_QUERY_peek_logical_register,
+ eMessage_QUERY_peek_cpu_regs,
+
+ eMessage_CMD_init,
+ eMessage_CMD_power,
+ eMessage_CMD_reset,
+ eMessage_CMD_run,
+ eMessage_CMD_serialize,
+ eMessage_CMD_unserialize,
+ eMessage_CMD_load_cartridge_normal,
+ eMessage_CMD_load_cartridge_super_game_boy,
+ eMessage_CMD_term,
+ eMessage_CMD_unload_cartridge,
+
+ eMessage_SIG_video_refresh,
+ eMessage_SIG_input_poll,
+ eMessage_SIG_input_state,
+ eMessage_SIG_input_notify,
+ eMessage_SIG_audio_flush,
+ eMessage_SIG_scanlineStart,
+ eMessage_SIG_path_request,
+ eMessage_SIG_trace_callback,
+ eMessage_SIG_allocSharedMemory, //?
+ eMessage_SIG_freeSharedMemory, //?
+
+ eMessage_BRK_Complete,
+ eMessage_BRK_hook_exec,
+ eMessage_BRK_hook_read,
+ eMessage_BRK_hook_write,
+ eMessage_BRK_hook_nmi,
+ eMessage_BRK_hook_irq,
+ };
+
+
+ public enum SNES_REG : int
+ {
+ //$2105
+ BG_MODE = 0,
+ BG3_PRIORITY = 1,
+ BG1_TILESIZE = 2,
+ BG2_TILESIZE = 3,
+ BG3_TILESIZE = 4,
+ BG4_TILESIZE = 5,
+ //$2107
+ BG1_SCADDR = 10,
+ BG1_SCSIZE = 11,
+ //$2108
+ BG2_SCADDR = 12,
+ BG2_SCSIZE = 13,
+ //$2109
+ BG3_SCADDR = 14,
+ BG3_SCSIZE = 15,
+ //$210A
+ BG4_SCADDR = 16,
+ BG4_SCSIZE = 17,
+ //$210B
+ BG1_TDADDR = 20,
+ BG2_TDADDR = 21,
+ //$210C
+ BG3_TDADDR = 22,
+ BG4_TDADDR = 23,
+ //$2133 SETINI
+ SETINI_MODE7_EXTBG = 30,
+ SETINI_HIRES = 31,
+ SETINI_OVERSCAN = 32,
+ SETINI_OBJ_INTERLACE = 33,
+ SETINI_SCREEN_INTERLACE = 34,
+ //$2130 CGWSEL
+ CGWSEL_COLORMASK = 40,
+ CGWSEL_COLORSUBMASK = 41,
+ CGWSEL_ADDSUBMODE = 42,
+ CGWSEL_DIRECTCOLOR = 43,
+ //$2101 OBSEL
+ OBSEL_NAMEBASE = 50,
+ OBSEL_NAMESEL = 51,
+ OBSEL_SIZE = 52,
+ //$2131 CGADSUB
+ CGADSUB_MODE = 60,
+ CGADSUB_HALF = 61,
+ CGADSUB_BG4 = 62,
+ CGADSUB_BG3 = 63,
+ CGADSUB_BG2 = 64,
+ CGADSUB_BG1 = 65,
+ CGADSUB_OBJ = 66,
+ CGADSUB_BACKDROP = 67,
+ //$212C TM
+ TM_BG1 = 70,
+ TM_BG2 = 71,
+ TM_BG3 = 72,
+ TM_BG4 = 73,
+ TM_OBJ = 74,
+ //$212D TM
+ TS_BG1 = 80,
+ TS_BG2 = 81,
+ TS_BG3 = 82,
+ TS_BG4 = 83,
+ TS_OBJ = 84,
+ //Mode7 regs
+ M7SEL_REPEAT = 90,
+ M7SEL_HFLIP = 91,
+ M7SEL_VFLIP = 92,
+ M7A = 93,
+ M7B = 94,
+ M7C = 95,
+ M7D = 96,
+ M7X = 97,
+ M7Y = 98,
+ //BG scroll regs
+ BG1HOFS = 100,
+ BG1VOFS = 101,
+ BG2HOFS = 102,
+ BG2VOFS = 103,
+ BG3HOFS = 104,
+ BG3VOFS = 105,
+ BG4HOFS = 106,
+ BG4VOFS = 107,
+ M7HOFS = 108,
+ M7VOFS = 109,
+ }
+
+ public enum SNES_MEMORY : uint
+ {
+ CARTRIDGE_RAM = 0,
+ CARTRIDGE_RTC = 1,
+ BSX_RAM = 2,
+ BSX_PRAM = 3,
+ SUFAMI_TURBO_A_RAM = 4,
+ SUFAMI_TURBO_B_RAM = 5,
+ SGB_CARTRAM = 6,
+ SGB_RTC = 7,
+ SGB_WRAM = 8,
+ SGB_HRAM = 9,
+
+ WRAM = 100,
+ APURAM = 101,
+ VRAM = 102,
+ OAM = 103,
+ CGRAM = 104,
+
+ SYSBUS = 200,
+ LOGICAL_REGS = 201
+ }
+
+ public enum SNES_REGION : byte
+ {
+ NTSC = 0,
+ PAL = 1,
+ }
+
+ public enum SNES_DEVICE : uint
+ {
+ NONE = 0,
+ JOYPAD = 1,
+ MULTITAP = 2,
+ MOUSE = 3,
+ SUPER_SCOPE = 4,
+ JUSTIFIER = 5,
+ JUSTIFIERS = 6,
+ SERIAL_CABLE = 7
+ }
+
+ public enum SNES_DEVICE_ID : uint
+ {
+ JOYPAD_B = 0,
+ JOYPAD_Y = 1,
+ JOYPAD_SELECT = 2,
+ JOYPAD_START = 3,
+ JOYPAD_UP = 4,
+ JOYPAD_DOWN = 5,
+ JOYPAD_LEFT = 6,
+ JOYPAD_RIGHT = 7,
+ JOYPAD_A = 8,
+ JOYPAD_X = 9,
+ JOYPAD_L = 10,
+ JOYPAD_R = 11
+ }
+ }
+}
\ 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
new file mode 100644
index 0000000000..ccd11befb0
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_QUERY.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Runtime.InteropServices;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ unsafe partial class LibsnesApi
+ {
+ public string QUERY_library_id()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_library_id);
+ return ReadPipeString();
+ }
+
+ public uint QUERY_library_revision_major()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_library_revision_major);
+ return brPipe.ReadUInt32();
+ }
+
+ public uint QUERY_library_revision_minor()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_library_revision_minor);
+ return brPipe.ReadUInt32();
+ }
+
+ public SNES_REGION QUERY_get_region()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_get_region);
+ return (SNES_REGION)brPipe.ReadByte();
+ }
+
+ public int QUERY_get_memory_size(SNES_MEMORY id)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_get_memory_size);
+ bwPipe.Write((int)id);
+ bwPipe.Flush();
+ return brPipe.ReadInt32();
+ }
+
+ string QUERY_MemoryNameForId(SNES_MEMORY id)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_GetMemoryIdName);
+ bwPipe.Write((uint)id);
+ bwPipe.Flush();
+ return ReadPipeString();
+ }
+
+ public byte* QUERY_get_memory_data(SNES_MEMORY id)
+ {
+ string name = QUERY_MemoryNameForId(id);
+ var smb = SharedMemoryBlocks[name];
+ return (byte*)smb.Ptr;
+ }
+
+ public byte QUERY_peek(SNES_MEMORY id, uint addr)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_peek);
+ bwPipe.Write((uint)id);
+ bwPipe.Write(addr);
+ bwPipe.Flush();
+ return brPipe.ReadByte();
+ }
+ public void QUERY_poke(SNES_MEMORY id, uint addr, byte val)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_poke);
+ bwPipe.Write((uint)id);
+ bwPipe.Write(addr);
+ bwPipe.Write(val);
+ bwPipe.Flush();
+ }
+
+ public int QUERY_serialize_size()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_serialize_size);
+ return brPipe.ReadInt32();
+ }
+
+
+ int QUERY_poll_message()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_poll_message);
+ return brPipe.ReadInt32();
+ }
+
+ public bool QUERY_HasMessage { get { return QUERY_poll_message() != -1; } }
+
+
+ public string QUERY_DequeueMessage()
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_dequeue_message);
+ return ReadPipeString();
+ }
+
+
+ public void QUERY_set_color_lut(IntPtr colors)
+ {
+ int len = 4 * 16 * 32768;
+ byte[] buf = new byte[len];
+ Marshal.Copy(colors, buf, 0, len);
+
+ WritePipeMessage(eMessage.eMessage_QUERY_set_color_lut);
+ WritePipeBlob(buf);
+ }
+
+ public void QUERY_set_state_hook_exec(bool state)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_state_hook_exec);
+ bwPipe.Write(state);
+ }
+
+ public void QUERY_set_state_hook_read(bool state)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_state_hook_read);
+ bwPipe.Write(state);
+ }
+
+ public void QUERY_set_state_hook_write(bool state)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_state_hook_write);
+ bwPipe.Write(state);
+ }
+
+ public void QUERY_set_trace_callback(snes_trace_t callback)
+ {
+ this.traceCallback = callback;
+ WritePipeMessage(eMessage.eMessage_QUERY_enable_scanline);
+ bwPipe.Write(callback != null);
+ bwPipe.Flush();
+ }
+ public void QUERY_set_scanlineStart(snes_scanlineStart_t scanlineStart)
+ {
+ this.scanlineStart = scanlineStart;
+ WritePipeMessage(eMessage.eMessage_QUERY_enable_scanline);
+ bwPipe.Write(scanlineStart != null);
+ bwPipe.Flush();
+ }
+ public void QUERY_set_audio_sample(snes_audio_sample_t audio_sample)
+ {
+ this.audio_sample = audio_sample;
+ WritePipeMessage(eMessage.eMessage_QUERY_enable_audio);
+ bwPipe.Write(audio_sample != null);
+ bwPipe.Flush();
+ }
+
+ public void QUERY_set_layer_enable(int layer, int priority, bool enable)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_set_layer_enable);
+ bwPipe.Write(layer);
+ bwPipe.Write(priority);
+ bwPipe.Write(enable);
+ bwPipe.Flush();
+ }
+
+ public void QUERY_set_backdropColor(int backdropColor)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_set_backdropColor);
+ bwPipe.Write(backdropColor);
+ bwPipe.Flush();
+ }
+
+ public int QUERY_peek_logical_register(SNES_REG reg)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_peek_logical_register);
+ bwPipe.Write((int)reg);
+ bwPipe.Flush();
+ return brPipe.ReadInt32();
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct CpuRegs
+ {
+ public uint pc;
+ public ushort a, x, y, z, s, d, vector; //7x
+ public byte p, nothing;
+ public uint aa, rd;
+ public byte sp, dp, db, mdr;
+ public const int SIZEOF = 32;
+ }
+
+ public unsafe void QUERY_peek_cpu_regs(out CpuRegs ret)
+ {
+ WritePipeMessage(eMessage.eMessage_QUERY_peek_cpu_regs);
+ //bwPipe.Flush();
+ byte[] temp = new byte[CpuRegs.SIZEOF];
+ brPipe.Read(temp, 0, CpuRegs.SIZEOF);
+ fixed(CpuRegs* ptr = &ret)
+ Marshal.Copy(temp, 0, new IntPtr(ptr), CpuRegs.SIZEOF);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs
new file mode 100644
index 0000000000..575fdac155
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_SIG.cs
@@ -0,0 +1,150 @@
+using System;
+
+using BizHawk.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ unsafe partial class LibsnesApi
+ {
+ bool Handle_SIG(eMessage msg)
+ {
+ switch (msg)
+ {
+ default:
+ return false;
+ case eMessage.eMessage_SIG_video_refresh:
+ {
+ int width = brPipe.ReadInt32();
+ int height = brPipe.ReadInt32();
+ bwPipe.Write(0); //offset in mapped memory buffer
+ bwPipe.Flush();
+ brPipe.ReadBoolean(); //dummy synchronization
+ if (video_refresh != null)
+ {
+ video_refresh((int*)mmvaPtr, width, height);
+ }
+ break;
+ }
+ case eMessage.eMessage_SIG_input_poll:
+ break;
+ case eMessage.eMessage_SIG_input_state:
+ {
+ int port = brPipe.ReadInt32();
+ int device = brPipe.ReadInt32();
+ int index = brPipe.ReadInt32();
+ int id = brPipe.ReadInt32();
+ ushort ret = 0;
+ if (input_state != null)
+ ret = input_state(port, device, index, id);
+ bwPipe.Write(ret);
+ bwPipe.Flush();
+ break;
+ }
+ case eMessage.eMessage_SIG_input_notify:
+ {
+ int index = brPipe.ReadInt32();
+ if (input_notify != null)
+ input_notify(index);
+ break;
+ }
+ case eMessage.eMessage_SIG_audio_flush:
+ {
+ int nsamples = brPipe.ReadInt32();
+ bwPipe.Write(0); //location to store audio buffer in
+ bwPipe.Flush();
+ brPipe.ReadInt32(); //dummy synchronization
+
+ if (audio_sample != null)
+ {
+ ushort* audiobuffer = ((ushort*)mmvaPtr);
+ for (int i = 0; i < nsamples; )
+ {
+ ushort left = audiobuffer[i++];
+ ushort right = audiobuffer[i++];
+ audio_sample(left, right);
+ }
+ }
+
+ bwPipe.Write(0); //dummy synchronization
+ bwPipe.Flush();
+ brPipe.ReadInt32(); //dummy synchronization
+ break;
+ }
+ case eMessage.eMessage_SIG_scanlineStart:
+ {
+ int line = brPipe.ReadInt32();
+ if (scanlineStart != null)
+ scanlineStart(line);
+
+ //we have to notify the unmanaged process that we're done peeking thruogh its memory and whatnot so it can proceed with emulation
+ WritePipeMessage(eMessage.eMessage_Complete);
+ break;
+ }
+ case eMessage.eMessage_SIG_path_request:
+ {
+ int slot = brPipe.ReadInt32();
+ string hint = ReadPipeString();
+ string ret = hint;
+ if (pathRequest != null)
+ hint = pathRequest(slot, hint);
+ WritePipeString(hint);
+ break;
+ }
+ case eMessage.eMessage_SIG_trace_callback:
+ {
+ var trace = ReadPipeString();
+ if (traceCallback != null)
+ traceCallback(trace);
+ break;
+ }
+ case eMessage.eMessage_SIG_allocSharedMemory:
+ {
+ var name = ReadPipeString();
+ var size = brPipe.ReadInt32();
+
+ if (SharedMemoryBlocks.ContainsKey(name))
+ {
+ throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name);
+ }
+
+ //try reusing existing block; dispose it if it exists and if the size doesnt match
+ SharedMemoryBlock smb = null;
+ if (DeallocatedMemoryBlocks.ContainsKey(name))
+ {
+ smb = DeallocatedMemoryBlocks[name];
+ DeallocatedMemoryBlocks.Remove(name);
+ if (smb.Size != size)
+ {
+ smb.Dispose();
+ smb = null;
+ }
+ }
+
+ //allocate a new block if we have to
+ if (smb == null)
+ {
+ smb = new SharedMemoryBlock();
+ smb.Name = name;
+ smb.Size = size;
+ smb.BlockName = InstanceName + smb.Name;
+ smb.Allocate();
+ }
+
+ SharedMemoryBlocks[smb.Name] = smb;
+ WritePipeString(smb.BlockName);
+ break;
+ }
+ case eMessage.eMessage_SIG_freeSharedMemory:
+ {
+ string name = ReadPipeString();
+ var smb = SharedMemoryBlocks[name];
+ DeallocatedMemoryBlocks[name] = smb;
+ SharedMemoryBlocks.Remove(name);
+ break;
+ }
+ } //switch(msg)
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
index 11c481d0b9..ed6c626705 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
@@ -71,8 +71,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
if (disposed) return;
disposed = true;
- api.snes_unload_cartridge();
- api.snes_term();
+ api.CMD_unload_cartridge();
+ api.CMD_term();
resampler.Dispose();
api.Dispose();
@@ -80,12 +80,46 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public List> GetCpuFlagsAndRegisters()
{
- var vals = new List>();
- foreach (var blah in Enum.GetValues(typeof(LibsnesApi.SNES_REG)).Cast())
+ var ret = new List>();
+
+ LibsnesApi.CpuRegs regs;
+ api.QUERY_peek_cpu_regs(out regs);
+
+ bool fn = (regs.p & 0x80)!=0;
+ bool fv = (regs.p & 0x40)!=0;
+ bool fm = (regs.p & 0x20)!=0;
+ bool fx = (regs.p & 0x10)!=0;
+ bool fd = (regs.p & 0x08)!=0;
+ bool fi = (regs.p & 0x04)!=0;
+ bool fz = (regs.p & 0x02)!=0;
+ bool fc = (regs.p & 0x01)!=0;
+
+ return new List>
{
- vals.Add(new KeyValuePair(blah.ToString(), api.snes_peek_logical_register(blah)));
- }
- return vals;
+ new KeyValuePair("PC", (int)regs.pc),
+ new KeyValuePair("A", (int)regs.a),
+ new KeyValuePair("X", (int)regs.x),
+ new KeyValuePair("Y", (int)regs.y),
+ new KeyValuePair("Z", (int)regs.z),
+ new KeyValuePair("S", (int)regs.s),
+ new KeyValuePair("D", (int)regs.d),
+ new KeyValuePair("Vector", (int)regs.vector),
+ new KeyValuePair("P", (int)regs.p),
+ new KeyValuePair("AA", (int)regs.aa),
+ new KeyValuePair("RD", (int)regs.rd),
+ new KeyValuePair("SP", (int)regs.sp),
+ new KeyValuePair("DP", (int)regs.dp),
+ new KeyValuePair("DB", (int)regs.db),
+ new KeyValuePair("MDR", (int)regs.mdr),
+ new KeyValuePair("Flag N", fn?1:0),
+ new KeyValuePair("Flag V", fv?1:0),
+ new KeyValuePair("Flag M", fm?1:0),
+ new KeyValuePair("Flag X", fx?1:0),
+ new KeyValuePair("Flag D", fd?1:0),
+ new KeyValuePair("Flag I", fi?1:0),
+ new KeyValuePair("Flag Z", fz?1:0),
+ new KeyValuePair("Flag C", fc?1:0),
+ };
}
public class MyScanlineHookManager : ScanlineHookManager
@@ -105,8 +139,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
void OnScanlineHooksChanged()
{
if (disposed) return;
- if (ScanlineHookManager.HookCount == 0) api.snes_set_scanlineStart(null);
- else api.snes_set_scanlineStart(scanlineStart_cb);
+ if (ScanlineHookManager.HookCount == 0) api.QUERY_set_scanlineStart(null);
+ else api.QUERY_set_scanlineStart(scanlineStart_cb);
}
void snes_scanlineStart(int line)
@@ -175,7 +209,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
CurrPalette = pal;
int[] tmp = SnesColors.GetLUT(pal);
fixed (int* p = &tmp[0])
- api.snes_set_color_lut((IntPtr)p);
+ api.QUERY_set_color_lut((IntPtr)p);
}
public LibsnesApi api;
@@ -185,7 +219,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
CoreComm = comm;
api = new LibsnesApi(CoreComm.SNES_ExePath);
- api.snes_init();
+ api.CMD_init();
api.ReadHook = ReadHook;
api.ExecHook = ExecHook;
api.WriteHook = WriteHook;
@@ -197,6 +231,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
//we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
//EDIT: for now, theres some IPC re-entrancy problem
//RefreshMemoryCallbacks();
+ api.SPECIAL_Resume();
}
void ExecHook(uint addr)
{
@@ -204,6 +239,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
//we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
//EDIT: for now, theres some IPC re-entrancy problem
//RefreshMemoryCallbacks();
+ api.SPECIAL_Resume();
}
void WriteHook(uint addr, byte val)
{
@@ -211,6 +247,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
//we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
//EDIT: for now, theres some IPC re-entrancy problem
//RefreshMemoryCallbacks();
+ api.SPECIAL_Resume();
}
LibsnesApi.snes_scanlineStart_t scanlineStart_cb;
@@ -221,19 +258,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
ScanlineHookManager = new MyScanlineHookManager(this);
- api.snes_init();
+ api.CMD_init();
- api.snes_set_video_refresh(snes_video_refresh);
- api.snes_set_input_poll(snes_input_poll);
- api.snes_set_input_state(snes_input_state);
- api.snes_set_input_notify(snes_input_notify);
- api.snes_set_path_request(snes_path_request);
+ api.QUERY_set_video_refresh(snes_video_refresh);
+ api.QUERY_set_input_poll(snes_input_poll);
+ api.QUERY_set_input_state(snes_input_state);
+ api.QUERY_set_input_notify(snes_input_notify);
+ api.QUERY_set_path_request(snes_path_request);
scanlineStart_cb = new LibsnesApi.snes_scanlineStart_t(snes_scanlineStart);
tracecb = new LibsnesApi.snes_trace_t(snes_trace);
soundcb = new LibsnesApi.snes_audio_sample_t(snes_audio_sample);
- api.snes_set_audio_sample(soundcb);
+ api.QUERY_set_audio_sample(soundcb);
// set default palette. Should be overridden by frontend probably
SetPalette(SnesColors.ColorType.BizHawk);
@@ -255,7 +292,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
IsSGB = true;
SystemId = "SNES";
- if (!api.snes_load_cartridge_super_game_boy(null, sgbRomData, (uint)sgbRomData.Length, null, romData, (uint)romData.Length))
+ if (!api.CMD_load_cartridge_super_game_boy(null, sgbRomData, (uint)sgbRomData.Length, null, romData, (uint)romData.Length))
throw new Exception("snes_load_cartridge_super_game_boy() failed");
}
else
@@ -275,11 +312,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
SystemId = "SNES";
- if (!api.snes_load_cartridge_normal(xmlData, romData))
+ if (!api.CMD_load_cartridge_normal(xmlData, romData))
throw new Exception("snes_load_cartridge_normal() failed");
}
- if (api.snes_get_region() == LibsnesApi.SNES_REGION.NTSC)
+ if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC)
{
//similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure.
CoreComm.VsyncNum = 21477272;
@@ -294,7 +331,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
CoreComm.CpuTraceAvailable = true;
- api.snes_power();
+ api.CMD_power();
SetupMemoryDomains(romData,sgbRomData);
@@ -473,44 +510,44 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
if (!nocallbacks && CoreComm.Tracer.Enabled)
- api.snes_set_trace_callback(tracecb);
+ api.QUERY_set_trace_callback(tracecb);
else
- api.snes_set_trace_callback(null);
+ api.QUERY_set_trace_callback(null);
// speedup when sound rendering is not needed
if (!rendersound)
- api.snes_set_audio_sample(null);
+ api.QUERY_set_audio_sample(null);
else
- api.snes_set_audio_sample(soundcb);
+ api.QUERY_set_audio_sample(soundcb);
bool resetSignal = Controller["Reset"];
- if (resetSignal) api.snes_reset();
+ if (resetSignal) api.CMD_reset();
bool powerSignal = Controller["Power"];
- if (powerSignal) api.snes_power();
+ if (powerSignal) api.CMD_power();
//too many messages
- api.snes_set_layer_enable(0, 0, CoreComm.SNES_ShowBG1_0);
- api.snes_set_layer_enable(0, 1, CoreComm.SNES_ShowBG1_1);
- api.snes_set_layer_enable(1, 0, CoreComm.SNES_ShowBG2_0);
- api.snes_set_layer_enable(1, 1, CoreComm.SNES_ShowBG2_1);
- api.snes_set_layer_enable(2, 0, CoreComm.SNES_ShowBG3_0);
- api.snes_set_layer_enable(2, 1, CoreComm.SNES_ShowBG3_1);
- api.snes_set_layer_enable(3, 0, CoreComm.SNES_ShowBG4_0);
- api.snes_set_layer_enable(3, 1, CoreComm.SNES_ShowBG4_1);
- api.snes_set_layer_enable(4, 0, CoreComm.SNES_ShowOBJ_0);
- api.snes_set_layer_enable(4, 1, CoreComm.SNES_ShowOBJ_1);
- api.snes_set_layer_enable(4, 2, CoreComm.SNES_ShowOBJ_2);
- api.snes_set_layer_enable(4, 3, CoreComm.SNES_ShowOBJ_3);
+ api.QUERY_set_layer_enable(0, 0, CoreComm.SNES_ShowBG1_0);
+ api.QUERY_set_layer_enable(0, 1, CoreComm.SNES_ShowBG1_1);
+ api.QUERY_set_layer_enable(1, 0, CoreComm.SNES_ShowBG2_0);
+ api.QUERY_set_layer_enable(1, 1, CoreComm.SNES_ShowBG2_1);
+ api.QUERY_set_layer_enable(2, 0, CoreComm.SNES_ShowBG3_0);
+ api.QUERY_set_layer_enable(2, 1, CoreComm.SNES_ShowBG3_1);
+ api.QUERY_set_layer_enable(3, 0, CoreComm.SNES_ShowBG4_0);
+ api.QUERY_set_layer_enable(3, 1, CoreComm.SNES_ShowBG4_1);
+ api.QUERY_set_layer_enable(4, 0, CoreComm.SNES_ShowOBJ_0);
+ api.QUERY_set_layer_enable(4, 1, CoreComm.SNES_ShowOBJ_1);
+ api.QUERY_set_layer_enable(4, 2, CoreComm.SNES_ShowOBJ_2);
+ api.QUERY_set_layer_enable(4, 3, CoreComm.SNES_ShowOBJ_3);
RefreshMemoryCallbacks();
//apparently this is one frame?
timeFrameCounter++;
- api.snes_run();
+ api.CMD_run();
- while (api.HasMessage)
- Console.WriteLine(api.DequeueMessage());
+ while (api.QUERY_HasMessage)
+ Console.WriteLine(api.QUERY_DequeueMessage());
if (IsLagFrame)
LagCount++;
@@ -524,16 +561,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
void RefreshMemoryCallbacks()
{
var mcs = CoreComm.MemoryCallbackSystem;
- api.snes_set_state_hook_exec(mcs.HasExecutes);
- api.snes_set_state_hook_read(mcs.HasReads);
- api.snes_set_state_hook_write(mcs.HasWrites);
+ api.QUERY_set_state_hook_exec(mcs.HasExecutes);
+ api.QUERY_set_state_hook_read(mcs.HasReads);
+ api.QUERY_set_state_hook_write(mcs.HasWrites);
}
public DisplayType DisplayType
{
get
{
- if (api.snes_get_region() == LibsnesApi.SNES_REGION.NTSC)
+ if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC)
return DisplayType.NTSC;
else
return DisplayType.PAL;
@@ -593,14 +630,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
set { }
get
{
- return api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0;
+ return api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0;
}
}
public byte[] ReadSaveRam()
{
- byte* buf = api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
- var size = api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
var ret = new byte[size];
Marshal.Copy((IntPtr)buf, ret, 0, size);
return ret;
@@ -616,16 +653,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public void StoreSaveRam(byte[] data)
{
- var size = api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
if (size == 0) return;
if (size != data.Length) throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is");
- byte* buf = api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
Marshal.Copy(data, 0, (IntPtr)buf, size);
}
public void ClearSaveRam()
{
- byte[] cleardata = new byte[(int)api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM)];
+ byte[] cleardata = new byte[(int)api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM)];
StoreSaveRam(cleardata);
}
@@ -786,7 +823,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
public void LoadStateBinary(BinaryReader reader)
{
- int size = api.snes_serialize_size();
+ int size = api.QUERY_serialize_size();
byte[] buf = reader.ReadBytes(size);
CoreLoadState(buf);
@@ -850,22 +887,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
///
void CoreLoadState(byte[] data)
{
- int size = api.snes_serialize_size();
+ int size = api.QUERY_serialize_size();
if (data.Length != size)
throw new Exception("Libsnes internal savestate size mismatch!");
- api.snes_init();
+ api.CMD_init();
fixed (byte* pbuf = &data[0])
- api.snes_unserialize(new IntPtr(pbuf), size);
+ api.CMD_unserialize(new IntPtr(pbuf), size);
}
///
/// handle the unmanaged part of savestating
///
byte[] CoreSaveState()
{
- int size = api.snes_serialize_size();
+ int size = api.QUERY_serialize_size();
byte[] buf = new byte[size];
fixed (byte* pbuf = &buf[0])
- api.snes_serialize(new IntPtr(pbuf), size);
+ api.CMD_serialize(new IntPtr(pbuf), size);
return buf;
}
@@ -881,14 +918,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
// ----- Client Debugging API stuff -----
unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian)
{
- int size = api.snes_get_memory_size(id);
+ int size = api.QUERY_get_memory_size(id);
int mask = size - 1;
//if this type of memory isnt available, dont make the memory domain (most commonly save ram)
if (size == 0)
return null;
- byte* blockptr = api.snes_get_memory_data(id);
+ byte* blockptr = api.QUERY_get_memory_data(id);
MemoryDomain md;
@@ -961,8 +998,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
if (!DeterministicEmulation)
_memoryDomains.Add(new MemoryDomain("BUS", 0x1000000, MemoryDomain.Endian.Little,
- (addr) => api.peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
- (addr, val) => api.poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val)));
+ (addr) => api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
+ (addr, val) => api.QUERY_poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val)));
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/SNESGraphicsDecoder.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/SNESGraphicsDecoder.cs
index 409f1ebf54..840bd28f83 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/SNESGraphicsDecoder.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/SNESGraphicsDecoder.cs
@@ -330,11 +330,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
var si = new ScreenInfo();
- si.Mode1_BG3_Priority = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1;
+ si.Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1;
- si.OBSEL_Size = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE);
- si.OBSEL_NameSel = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
- si.OBSEL_NameBase = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
+ si.OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE);
+ si.OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
+ si.OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1];
int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height);
@@ -344,26 +344,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
si.OBJTable0Addr = si.OBSEL_NameBase << 14;
si.OBJTable1Addr = (si.OBJTable0Addr + ((si.OBSEL_NameSel + 1) << 13)) & 0xFFFF;
- si.SETINI_Mode7ExtBG = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1;
- si.SETINI_HiRes = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1;
- si.SETINI_Overscan = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1;
- si.SETINI_ObjInterlace = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1;
- si.SETINI_ScreenInterlace = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1;
+ si.SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1;
+ si.SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1;
+ si.SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1;
+ si.SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1;
+ si.SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1;
- si.CGWSEL_ColorMask = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK);
- si.CGWSEL_ColorSubMask = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK);
- si.CGWSEL_AddSubMode = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE);
- si.CGWSEL_DirectColor = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1;
+ si.CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK);
+ si.CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK);
+ si.CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE);
+ si.CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1;
- si.CGADSUB_AddSub = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE);
- si.CGADSUB_Half = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1;
+ si.CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE);
+ si.CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1;
- si.OBJ_MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1;
- si.OBJ_SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1;
- si.OBJ_MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1;
- si.BK_MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1;
+ si.OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1;
+ si.OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1;
+ si.OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1;
+ si.BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1;
- si.Mode.MODE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE);
+ si.Mode.MODE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE);
si.BG.BG1.Bpp = ModeBpps[si.Mode.MODE, 0];
si.BG.BG2.Bpp = ModeBpps[si.Mode.MODE, 1];
si.BG.BG3.Bpp = ModeBpps[si.Mode.MODE, 2];
@@ -373,58 +373,58 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
for(int i=1;i<=4;i++)
si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
- si.BG.BG1.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
- si.BG.BG2.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
- si.BG.BG3.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
- si.BG.BG4.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
+ si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
+ si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
+ si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
+ si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
- si.BG.BG1.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
- si.BG.BG2.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
- si.BG.BG3.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
- si.BG.BG4.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
- si.BG.BG1.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
- si.BG.BG2.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
- si.BG.BG3.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
- si.BG.BG4.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
- si.BG.BG1.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
- si.BG.BG2.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
- si.BG.BG3.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
- si.BG.BG4.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
+ si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
+ si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
+ si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
+ si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
+ si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
+ si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
+ si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
+ si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
+ si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
+ si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
+ si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
+ si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
- si.BG.BG1.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
- si.BG.BG2.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
- si.BG.BG3.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
- si.BG.BG4.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
- si.BG.BG1.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
- si.BG.BG2.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
- si.BG.BG3.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
- si.BG.BG4.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
- si.BG.BG1.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
- si.BG.BG2.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
- si.BG.BG3.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
- si.BG.BG4.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
+ si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
+ si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
+ si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
+ si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
+ si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
+ si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
+ si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
+ si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
+ si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
+ si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
+ si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
+ si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
- si.BG.BG1.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
- si.BG.BG1.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
- si.BG.BG2.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
- si.BG.BG2.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
- si.BG.BG3.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
- si.BG.BG3.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
- si.BG.BG4.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
- si.BG.BG4.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
+ si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
+ si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
+ si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
+ si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
+ si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
+ si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
+ si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
+ si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
- si.M7HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS);
- si.M7VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS);
- si.M7A = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7A);
- si.M7B = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7B);
- si.M7C = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7C);
- si.M7D = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7D);
- si.M7X = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7X);
- si.M7Y = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
- si.M7Y = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
- si.M7SEL_REPEAT = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT);
- si.M7SEL_HFLIP = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0;
- si.M7SEL_VFLIP = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0;
+ si.M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS);
+ si.M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS);
+ si.M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A);
+ si.M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B);
+ si.M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C);
+ si.M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D);
+ si.M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X);
+ si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
+ si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
+ si.M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT);
+ si.M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0;
+ si.M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0;
for (int i = 1; i <= 4; i++)
{
@@ -559,12 +559,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
{
this.api = api;
colortable = SnesColors.GetLUT(pal);
- IntPtr block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM);
+ IntPtr block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM);
vram = (byte*)block;
vram16 = (ushort*)block;
- block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM);
+ block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM);
cgram = (ushort*)block;
- block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.OAM);
+ block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.OAM);
oam = (byte*)block;
}
diff --git a/libsnes/bsnes/snes/alt/cpu/cpu.hpp b/libsnes/bsnes/snes/alt/cpu/cpu.hpp
index 8f2b74b061..f3105ad622 100644
--- a/libsnes/bsnes/snes/alt/cpu/cpu.hpp
+++ b/libsnes/bsnes/snes/alt/cpu/cpu.hpp
@@ -146,6 +146,16 @@ private:
uint8 joy3l, joy3h;
uint8 joy4l, joy4h;
} status;
+
+public:
+ struct Debugger {
+ hook op_exec;
+ hook op_read;
+ hook op_write;
+ hook op_nmi;
+ hook op_irq;
+ } debugger;
+
};
extern CPU cpu;
diff --git a/libsnes/bsnes/snes/scheduler/scheduler.hpp b/libsnes/bsnes/snes/scheduler/scheduler.hpp
index 764e179ae5..db67f3727a 100644
--- a/libsnes/bsnes/snes/scheduler/scheduler.hpp
+++ b/libsnes/bsnes/snes/scheduler/scheduler.hpp
@@ -3,6 +3,8 @@ struct Scheduler : property {
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
readonly exit_reason;
+ void clearExitReason() { exit_reason = ExitReason::UnknownEvent; }
+
cothread_t host_thread; //program thread (used to exit emulation)
cothread_t thread; //active emulation thread (used to enter emulation)
diff --git a/libsnes/bsnes/snes/system/video.hpp b/libsnes/bsnes/snes/system/video.hpp
index f01cac65fc..6fbeace0e0 100644
--- a/libsnes/bsnes/snes/system/video.hpp
+++ b/libsnes/bsnes/snes/system/video.hpp
@@ -7,11 +7,12 @@ struct Video {
Video();
~Video();
+ void update();
+
private:
bool hires;
unsigned line_width[240];
- void update();
void scanline();
void init();
diff --git a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp
index 321511c966..bf9393fe3b 100644
--- a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp
+++ b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp
@@ -1,9 +1,30 @@
+//TODO - find out somehow when the parent process is gone (check a pid or some slot on that for existence and match vs logged value?)
+// we cant just check the state of a socket or named pipe or whatever in the case of IPCRingBuffer
+
+//TODO - clean up signaling namespaces.. hooks vs callbacks vs etc. (unify them, at least)
+//maybe turn into signal vs break concept (signals would be synchronous [expecting the frontend to always field them immediately and allow execution to resume immediately]) vs breaks (which the frontend receives control after)
+//also a COMMAND concept (comes from frontend.. run, init, etc.)
+//
+//TODO - consolidate scanline breakpoint into this, with something like a set_break(type,address,size) which could be used in the future for read/write/execute but for now, for scanline (address would be the scanline)
+//
+//TODO - factor out modular components (ringbuffer and the like)
+
+//types of messages:
+//cmd: frontend->core: "command to core" a command from the frontend which causes emulation to proceed. when sending a command, the frontend should wait for an eMessage_brk_complete before proceeding, although a debugger might proceed after any BRK
+//query: frontend->core: "query to core" a query from the frontend which can (and should) be satisfied immediately by the core but which does not result in emulation processes
+//sig: core->frontend: "core signal" a synchronous operation called from the emulation process which the frontend should handle immediately without issuing any calls into the core
+//brk: core->frontend: "core break" the emulation process has suspended. the core is free to do whatever it wishes.
+//(and there are other assorted special messages...)
+
+
#include
#define LIBSNES_IMPORT
#include "snes/snes.hpp"
#include "libsnes.hpp"
+#include
+
#include
#include
#include
@@ -15,6 +36,175 @@
typedef uint8 u8;
typedef int32 s32;
typedef uint32 u32;
+typedef uint16 u16;
+
+enum eMessage : int32
+{
+ eMessage_Complete,
+
+ eMessage_SetBuffer,
+ eMessage_BeginBufferIO,
+ eMessage_EndBufferIO,
+
+ eMessage_QUERY_library_id,
+ eMessage_QUERY_library_revision_major,
+ eMessage_QUERY_library_revision_minor,
+ eMessage_QUERY_get_region,
+ eMessage_QUERY_get_memory_size,
+ eMessage_QUERY_get_memory_data, //note: this function isnt used and hasnt been tested in a while
+ eMessage_QUERY_peek,
+ eMessage_QUERY_poke,
+ eMessage_QUERY_serialize_size,
+ eMessage_QUERY_poll_message,
+ eMessage_QUERY_dequeue_message,
+ eMessage_QUERY_set_color_lut,
+ eMessage_QUERY_GetMemoryIdName,
+ eMessage_QUERY_state_hook_exec,
+ eMessage_QUERY_state_hook_read,
+ eMessage_QUERY_state_hook_write,
+ eMessage_QUERY_state_hook_nmi,
+ eMessage_QUERY_state_hook_irq,
+ eMessage_QUERY_enable_trace,
+ eMessage_QUERY_enable_scanline,
+ eMessage_QUERY_enable_audio,
+ eMessage_QUERY_set_layer_enable,
+ eMessage_QUERY_set_backdropColor,
+ eMessage_QUERY_peek_logical_register,
+ eMessage_QUERY_peek_cpu_regs,
+
+ eMessage_CMD_init,
+ eMessage_CMD_power,
+ eMessage_CMD_reset,
+ eMessage_CMD_run,
+ eMessage_CMD_serialize,
+ eMessage_CMD_unserialize,
+ eMessage_CMD_load_cartridge_normal,
+ eMessage_CMD_load_cartridge_super_game_boy,
+ eMessage_CMD_term,
+ eMessage_CMD_unload_cartridge,
+
+ eMessage_SIG_video_refresh,
+ eMessage_SIG_input_poll,
+ eMessage_SIG_input_state,
+ eMessage_SIG_input_notify,
+ eMessage_SIG_audio_flush,
+ eMessage_SIG_scanlineStart,
+ eMessage_SIG_path_request,
+ eMessage_SIG_trace_callback,
+ eMessage_SIG_allocSharedMemory, //?
+ eMessage_SIG_freeSharedMemory, //?
+
+ eMessage_BRK_Complete,
+ eMessage_BRK_hook_exec,
+ eMessage_BRK_hook_read,
+ eMessage_BRK_hook_write,
+ eMessage_BRK_hook_nmi,
+ eMessage_BRK_hook_irq,
+};
+
+
+enum eEmulationExitReason
+{
+ eEmulationExitReason_NotSet,
+ eEmulationExitReason_BRK,
+ eEmulationExitReason_SIG,
+ eEmulationExitReason_Complete,
+};
+
+enum eEmulationCallback
+{
+ eEmulationCallback_snes_video_refresh,
+ eEmulationCallback_snes_audio_flush,
+ eEmulationCallback_snes_input_poll,
+ eEmulationCallback_snes_input_state,
+ eEmulationCallback_snes_input_notify,
+ eEmulationCallback_snes_path_request,
+
+ eEmulationCallback_snes_allocSharedMemory,
+ eEmulationCallback_snes_freeSharedMemory,
+
+ eEmulationCallback_snes_trace
+};
+
+enum eEmulationCommand
+{
+ eEmulationCommand_NotSet,
+ eEmulationCommand_Init,
+ eEmulationCommand_Power,
+ eEmulationCommand_Reset,
+ eEmulationCommand_FrameAdvance,
+ eEmulationCommand_LoadCartridgeNormal,
+ eEmulationCommand_Step
+};
+
+struct EmulationControl
+{
+ volatile eEmulationCommand command;
+ volatile eEmulationExitReason exitReason;
+
+ union
+ {
+ struct
+ {
+ volatile eMessage hookExitType;
+ uint32 hookAddr;
+ uint8 hookValue;
+ };
+
+ union
+ {
+ struct {
+ bool result;
+ } cmd_loadCartridgeNormal_params;
+ };
+
+ struct
+ {
+ volatile eEmulationCallback exitCallbackType;
+ union
+ {
+ struct
+ {
+ const uint32_t *data;
+ unsigned width;
+ unsigned height;
+ } cb_video_refresh_params;
+ struct
+ {
+ unsigned port, device, index, id;
+ int16_t result;
+ } cb_input_state_params;
+ struct
+ {
+ int index;
+ } cb_input_notify_params;
+ struct
+ {
+ int slot;
+ const char* hint;
+ //yuck
+ char result[MAX_PATH];
+ } cb_path_request_params;
+ struct
+ {
+ const char* memtype;
+ size_t amt;
+ void* result;
+ } cb_allocSharedMemory_params;
+ struct
+ {
+ void* ptr;
+ } cb_freeSharedMemory_params;
+ struct
+ {
+ const char* msg;
+ } cb_trace_params;
+ };
+ };
+ };
+};
+
+static EmulationControl s_EmulationControl;
class IPCRingBuffer
{
@@ -106,6 +296,8 @@ public:
if (available > 0)
return available;
//this is a greedy spinlock.
+ //NOTE: it's annoying right now because libsnes processes die and eat a whole core.
+ //we need to gracefully exit somehow
}
}
@@ -169,78 +361,21 @@ HANDLE hPipe, hMapFile;
void* hMapFilePtr;
static bool running = false;
-enum eMessage : int32
-{
- eMessage_Complete,
+cothread_t co_control, co_emu, co_emu_suspended;
- eMessage_snes_library_id,
- eMessage_snes_library_revision_major,
- eMessage_snes_library_revision_minor,
+#define SETCONTROL \
+{ \
+ co_emu_suspended = co_active(); \
+ co_switch(co_control); \
+}
- eMessage_snes_init,
- eMessage_snes_power,
- eMessage_snes_reset,
- eMessage_snes_run,
- eMessage_snes_term,
- eMessage_snes_unload_cartridge,
+#define SETEMU \
+{ \
+ cothread_t temp = co_emu_suspended; \
+ co_emu_suspended = NULL; \
+ co_switch(temp); \
+}
- //snes_set_cartridge_basename, //not used
-
- eMessage_snes_load_cartridge_normal,
- eMessage_snes_load_cartridge_super_game_boy,
-
- eMessage_snes_cb_video_refresh,
- eMessage_snes_cb_input_poll,
- eMessage_snes_cb_input_state,
- eMessage_snes_cb_input_notify,
- eMessage_snes_cb_audio_sample,
- eMessage_snes_cb_scanlineStart,
- eMessage_snes_cb_path_request,
- eMessage_snes_cb_trace_callback,
-
- eMessage_snes_get_region,
-
- eMessage_snes_get_memory_size,
- eMessage_snes_get_memory_data,
- eMessage_peek,
- eMessage_poke,
-
- eMessage_snes_serialize_size,
-
- eMessage_snes_serialize,
- eMessage_snes_unserialize,
-
- eMessage_snes_poll_message,
- eMessage_snes_dequeue_message,
-
- eMessage_snes_set_color_lut,
-
- eMessage_snes_enable_trace,
- eMessage_snes_enable_scanline,
- eMessage_snes_enable_audio,
- eMessage_snes_set_layer_enable,
- eMessage_snes_set_backdropColor,
- eMessage_snes_peek_logical_register,
-
- eMessage_snes_allocSharedMemory,
- eMessage_snes_freeSharedMemory,
- eMessage_GetMemoryIdName,
-
- eMessage_SetBuffer,
- eMessage_BeginBufferIO,
- eMessage_EndBufferIO,
-
- eMessage_set_state_hook_exec,
- eMessage_set_state_hook_read,
- eMessage_set_state_hook_write,
- eMessage_set_state_hook_nmi,
- eMessage_set_state_hook_irq,
- eMessage_snes_cb_hook_exec,
- eMessage_snes_cb_hook_read,
- eMessage_snes_cb_hook_write,
- eMessage_snes_cb_hook_nmi,
- eMessage_snes_cb_hook_irq,
-};
void ReadPipeBuffer(void* buf, int len)
{
@@ -275,6 +410,10 @@ template<> bool ReadPipe()
void WritePipeBuffer(const void* buf, int len)
{
+ if(co_active() != co_control)
+ {
+ printf("WARNING: WRITING FROM NON-CONTROL THREAD\n");
+ }
//static FILE* outf = NULL;
//if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf);
@@ -290,6 +429,12 @@ void WritePipeBuffer(const void* buf, int len)
exit(1);
}
+//remove volatile qualifier...... crazy?
+template void WritePipe(volatile const T& val)
+{
+ WritePipeBuffer((T*)&val, sizeof(val));
+}
+
template void WritePipe(const T& val)
{
WritePipeBuffer(&val, sizeof(val));
@@ -332,14 +477,13 @@ Blob ReadPipeBlob()
void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height)
{
- WritePipe(eMessage_snes_cb_video_refresh);
- WritePipe(width);
- WritePipe(height);
- int destOfs = ReadPipe();
- char* buf = (char*)hMapFilePtr + destOfs;
- int bufsize = 512 * 480 * 4;
- memcpy(buf,data,bufsize);
- WritePipe((char)0); //dummy synchronization
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_video_refresh;
+ s_EmulationControl.cb_video_refresh_params.data = data;
+ s_EmulationControl.cb_video_refresh_params.width = width;
+ s_EmulationControl.cb_video_refresh_params.height = height;
+
+ SETCONTROL;
}
bool audio_en = false;
@@ -347,83 +491,81 @@ static const int AUDIOBUFFER_SIZE = 44100*2;
uint16_t audiobuffer[AUDIOBUFFER_SIZE];
int audiobuffer_idx = 0;
-void FlushAudio()
+void SIG_FlushAudio()
{
- if(audiobuffer_idx == 0) return;
-
- WritePipe(eMessage_snes_cb_audio_sample);
-
- int nsamples = audiobuffer_idx;
- WritePipe(nsamples);
- char* buf = ReadPipeSharedPtr();
- memcpy(buf,audiobuffer,nsamples*2);
- //extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety.
- WritePipe(0); //dummy synchronization
-
- //wait for frontend to consume data
-
- ReadPipe(); //dummy synchronization
- WritePipe(0); //dummy synchronization
- audiobuffer_idx = 0;
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_audio_flush;
+ SETCONTROL;
}
+//this is the raw callback from the emulator internals when a new audio sample is available
void snes_audio_sample(uint16_t left, uint16_t right)
{
if(!audio_en) return;
+ //if theres no room in the audio buffer, we need to send a flush signal
if(audiobuffer_idx == AUDIOBUFFER_SIZE)
- FlushAudio();
+ SIG_FlushAudio();
+
audiobuffer[audiobuffer_idx++] = left;
- audiobuffer[audiobuffer_idx++] = right;
+ audiobuffer[audiobuffer_idx++] = right;
}
void snes_input_poll(void)
{
- WritePipe(eMessage_snes_cb_input_poll);
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_poll;
+ SETCONTROL;
}
int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id)
{
- WritePipe(eMessage_snes_cb_input_state);
- WritePipe(port);
- WritePipe(device);
- WritePipe(index);
- WritePipe(id);
- return ReadPipe();
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_state;
+ s_EmulationControl.cb_input_state_params.port = port;
+ s_EmulationControl.cb_input_state_params.device = device;
+ s_EmulationControl.cb_input_state_params.index = index;
+ s_EmulationControl.cb_input_state_params.id = id;
+ SETCONTROL;
+ return s_EmulationControl.cb_input_state_params.result;
}
void snes_input_notify(int index)
{
- WritePipe(eMessage_snes_cb_input_notify);
- WritePipe(index);
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_notify;
+ s_EmulationControl.cb_input_notify_params.index = index;
+ SETCONTROL;
}
void snes_trace(const char *msg)
{
- WritePipe(eMessage_snes_cb_trace_callback);
- WritePipeString(msg);
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_trace;
+ s_EmulationControl.cb_trace_params.msg = msg;
+ SETCONTROL;
}
const char* snes_path_request(int slot, const char* hint)
{
- //yuck
- static char ret[MAX_PATH];
- WritePipe(eMessage_snes_cb_path_request);
- WritePipe(slot);
- WritePipeString(hint);
- std::string str = ReadPipeString();
- strcpy(ret,str.c_str());
- return ret;
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_path_request;
+ s_EmulationControl.cb_path_request_params.slot = slot;
+ s_EmulationControl.cb_path_request_params.hint = hint;
+ SETCONTROL;
+
+ return (const char*)s_EmulationControl.cb_path_request_params.result;
}
-void RunMessageLoop();
+void RunControlMessageLoop();
void snes_scanlineStart(int line)
{
- WritePipe(eMessage_snes_cb_scanlineStart);
- WritePipe(line);
+ //TODO
+ //WritePipe(eMessage_snes_cb_scanlineStart);
+ //WritePipe(line);
- //we've got to wait for the frontend to finish processing.
- //in theory we could let emulation proceed after snagging the vram and registers, and do decoding and stuff on another thread...
- //but its too hard for now.
- RunMessageLoop();
+ ////we've got to wait for the frontend to finish processing.
+ ////in theory we could let emulation proceed after snagging the vram and registers, and do decoding and stuff on another thread...
+ ////but its too hard for now.
+ //RunMessageLoop();
}
class SharedMemoryBlock
@@ -435,10 +577,19 @@ public:
static std::map memHandleTable;
-void* snes_allocSharedMemory(const char* memtype, size_t amt)
+void* implementation_snes_allocSharedMemory()
{
- if(!running) return NULL;
- WritePipe(eMessage_snes_allocSharedMemory);
+ const char* memtype = s_EmulationControl.cb_allocSharedMemory_params.memtype;
+ size_t amt = s_EmulationControl.cb_allocSharedMemory_params.amt;
+
+ if(!running)
+ {
+ s_EmulationControl.cb_allocSharedMemory_params.result = NULL;
+ return NULL;
+ }
+
+ //printf("WritePipe(eMessage_SIG_allocSharedMemory)\n");
+ WritePipe(eMessage_SIG_allocSharedMemory);
WritePipeString(memtype);
WritePipe(amt);
@@ -457,18 +608,44 @@ void* snes_allocSharedMemory(const char* memtype, size_t amt)
memHandleTable[ptr] = smb;
- return ptr;
+ s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
+}
+
+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;
+
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_allocSharedMemory;
+ s_EmulationControl.cb_allocSharedMemory_params.memtype = memtype;
+ s_EmulationControl.cb_allocSharedMemory_params.amt = amt;
+ SETCONTROL;
+
+ return s_EmulationControl.cb_allocSharedMemory_params.result;
}
-void snes_freeSharedMemory(void* ptr)
-{
- if(!running) return;
+void implementation_snes_freeSharedMemory()
+{
+ void* ptr = s_EmulationControl.cb_freeSharedMemory_params.ptr;
if(!ptr) return;
auto smb = memHandleTable.find(ptr)->second;
UnmapViewOfFile(ptr);
CloseHandle(smb->handle);
- WritePipe(eMessage_snes_freeSharedMemory);
- WritePipeString(smb->memtype.c_str());
+ //printf("WritePipe(eMessage_SIG_freeSharedMemory);\n");
+ WritePipe(eMessage_SIG_freeSharedMemory);
+ WritePipeString(smb->memtype.c_str());
+}
+
+void snes_freeSharedMemory(void* ptr)
+{
+ //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;
+
+ s_EmulationControl.exitReason = eEmulationExitReason_SIG;
+ s_EmulationControl.exitCallbackType = eEmulationCallback_snes_freeSharedMemory;
+ s_EmulationControl.cb_freeSharedMemory_params.ptr = ptr;
+ SETCONTROL;
}
void InitBsnes()
@@ -488,231 +665,468 @@ void InitBsnes()
static void debug_op_exec(uint24 addr)
{
- WritePipe(eMessage_snes_cb_hook_exec);
- WritePipe((uint32)addr);
+ s_EmulationControl.exitReason = eEmulationExitReason_BRK;
+ s_EmulationControl.hookExitType = eMessage_BRK_hook_exec;
+ s_EmulationControl.hookAddr = (uint32)addr;
+ SETCONTROL;
+ //WritePipe(eMessage_snes_cb_hook_exec);
+ //WritePipe((uint32)addr);
}
static void debug_op_read(uint24 addr)
{
- WritePipe(eMessage_snes_cb_hook_read);
- WritePipe((uint32)addr);
+ s_EmulationControl.exitReason = eEmulationExitReason_BRK;
+ s_EmulationControl.hookExitType = eMessage_BRK_hook_read;
+ s_EmulationControl.hookAddr = (uint32)addr;
+ SETCONTROL;
+ //WritePipe(eMessage_snes_cb_hook_read);
+ //WritePipe((uint32)addr);
}
static void debug_op_write(uint24 addr, uint8 value)
{
- WritePipe(eMessage_snes_cb_hook_write);
- WritePipe((uint32)addr);
- WritePipe(value);
+ s_EmulationControl.exitReason = eEmulationExitReason_BRK;
+ s_EmulationControl.hookExitType = eMessage_BRK_hook_write;
+ s_EmulationControl.hookAddr = (uint32)addr;
+ s_EmulationControl.hookValue = value;
+ SETCONTROL;
+ //WritePipe(eMessage_snes_cb_hook_write);
+ //WritePipe((uint32)addr);
+ //WritePipe(value);
}
static void debug_op_nmi()
{
- WritePipe(eMessage_snes_cb_hook_nmi);
+ WritePipe(eMessage_BRK_hook_nmi);
}
static void debug_op_irq()
{
- WritePipe(eMessage_snes_cb_hook_irq);
+ WritePipe(eMessage_BRK_hook_irq);
}
-void RunMessageLoop()
+void HandleMessage_QUERY(eMessage msg)
+{
+}
+
+bool Handle_QUERY(eMessage msg)
+{
+ switch(msg)
+ {
+ default:
+ return false;
+
+ case eMessage_QUERY_library_id:
+ WritePipeString(snes_library_id());
+ break;
+ case eMessage_QUERY_library_revision_major:
+ WritePipe(snes_library_revision_major());
+ break;
+ case eMessage_QUERY_library_revision_minor:
+ WritePipe(snes_library_revision_minor());
+ break;
+
+ case eMessage_QUERY_get_region:
+ WritePipe((char)snes_get_region());
+ break;
+
+ case eMessage_QUERY_get_memory_size:
+ WritePipe(snes_get_memory_size(ReadPipe()));
+ break;
+
+ case eMessage_QUERY_get_memory_data:
+ {
+ unsigned int id = ReadPipe();
+ char* dstbuf = ReadPipeSharedPtr();
+ uint8_t* srcbuf = snes_get_memory_data(id);
+ memcpy(dstbuf,srcbuf,snes_get_memory_size(id));
+ WritePipe(eMessage_Complete);
+ break;
+ }
+
+ case eMessage_QUERY_peek:
+ {
+ printf("harry");
+ int id = ReadPipe();
+ unsigned int addr = ReadPipe();
+ uint8_t ret;
+ if(id == SNES_MEMORY_SYSBUS)
+ ret = bus_read(addr);
+ else ret = snes_get_memory_data(id)[addr];
+ WritePipe(ret);
+ }
+ break;
+
+ case eMessage_QUERY_poke:
+ {
+ printf("susan");
+ int id = ReadPipe();
+ unsigned int addr = ReadPipe();
+ uint8_t val = ReadPipe();
+ if(id == SNES_MEMORY_SYSBUS)
+ bus_write(addr,val);
+ else snes_get_memory_data(id)[addr] = val;
+ break;
+ }
+ break;
+
+ case eMessage_QUERY_serialize_size:
+ WritePipe(snes_serialize_size());
+ break;
+ case eMessage_QUERY_poll_message:
+ //TBD
+ WritePipe(-1);
+ break;
+ case eMessage_QUERY_dequeue_message:
+ //TBD
+ break;
+
+ case eMessage_QUERY_set_color_lut:
+ {
+ auto blob = ReadPipeBlob();
+ snes_set_color_lut((uint32_t*)&blob[0]);
+ break;
+ }
+ break;
+
+ case eMessage_QUERY_enable_trace:
+ if(!!ReadPipe())
+ snes_set_trace_callback(snes_trace);
+ else snes_set_trace_callback(NULL);
+ break;
+
+ case eMessage_QUERY_enable_scanline:
+ if(ReadPipe())
+ snes_set_scanlineStart(snes_scanlineStart);
+ else snes_set_scanlineStart(NULL);
+ break;
+
+ case eMessage_QUERY_enable_audio:
+ audio_en = ReadPipe();
+ break;
+
+ case eMessage_QUERY_set_layer_enable:
+ {
+ int layer = ReadPipe();
+ int priority = ReadPipe();
+ bool enable = ReadPipe();
+ //TODO - readd this
+ //snes_set_layer_enable(layer,priority,enable);
+ break;
+ }
+
+ case eMessage_QUERY_set_backdropColor:
+ snes_set_backdropColor(ReadPipe());
+ break;
+
+ case eMessage_QUERY_peek_logical_register:
+ WritePipe(snes_peek_logical_register(ReadPipe()));
+ break;
+
+ case eMessage_QUERY_peek_cpu_regs:
+ {
+ //watch it! the size of this struct is important!
+ struct {
+ u32 pc;
+ u16 a,x,y,z,s,d,vector; //7x
+ u8 p, nothing;
+ u32 aa,rd;
+ u8 sp, dp, db, mdr;
+ } __attribute__((__packed__)) cpuregs;
+
+ cpuregs.pc = (u32)SNES::cpu.regs.pc;
+ cpuregs.a = SNES::cpu.regs.a;
+ cpuregs.x = SNES::cpu.regs.x;
+ cpuregs.y = SNES::cpu.regs.y;
+ cpuregs.z = SNES::cpu.regs.z;
+ cpuregs.s = SNES::cpu.regs.s;
+ cpuregs.d = SNES::cpu.regs.d;
+ cpuregs.aa = (u32)SNES::cpu.aa;
+ cpuregs.rd = (u32)SNES::cpu.rd;
+ cpuregs.sp = SNES::cpu.sp;
+ cpuregs.dp = SNES::cpu.dp;
+ cpuregs.db = SNES::cpu.regs.db;
+ cpuregs.mdr = SNES::cpu.regs.mdr;
+ cpuregs.vector = SNES::cpu.regs.vector;
+ cpuregs.p = SNES::cpu.regs.p;
+ cpuregs.nothing = 0;
+
+ WritePipeBuffer(&cpuregs,32); //watch it! the size of this struct is important!
+ }
+ break;
+
+ case eMessage_QUERY_GetMemoryIdName:
+ {
+ uint32 id = ReadPipe();
+ const char* ret = snes_get_memory_id_name(id);
+ if(!ret) ret = "";
+ WritePipeString(ret);
+ break;
+ }
+
+ case eMessage_QUERY_state_hook_exec:
+ SNES::cpu.debugger.op_exec = ReadPipe() ? debug_op_exec : hook();
+ break;
+
+ case eMessage_QUERY_state_hook_read:
+ SNES::cpu.debugger.op_read = ReadPipe() ? debug_op_read : hook();
+ break;
+
+ case eMessage_QUERY_state_hook_write:
+ SNES::cpu.debugger.op_write = ReadPipe() ? debug_op_write : hook();
+ break;
+
+ case eMessage_QUERY_state_hook_nmi:
+ SNES::cpu.debugger.op_nmi = ReadPipe() ? debug_op_nmi : hook();
+ break;
+
+ case eMessage_QUERY_state_hook_irq:
+ SNES::cpu.debugger.op_irq = ReadPipe() ? debug_op_irq : hook();
+ break;
+ }
+ return true;
+}
+
+bool Handle_CMD(eMessage msg)
+{
+ switch(msg)
+ {
+ default:
+ return false;
+
+ case eMessage_CMD_init:
+ s_EmulationControl.command = eEmulationCommand_Init;
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ co_switch(co_emu);
+ break;
+ case eMessage_CMD_power:
+ s_EmulationControl.command = eEmulationCommand_Power;
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ co_switch(co_emu);
+ break;
+ case eMessage_CMD_reset:
+ s_EmulationControl.command = eEmulationCommand_Reset;
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ co_switch(co_emu);
+ break;
+
+ case eMessage_CMD_run:
+ s_EmulationControl.command = eEmulationCommand_FrameAdvance;
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ co_switch(co_emu);
+ break;
+
+ case eMessage_CMD_serialize:
+ {
+ int size = ReadPipe();
+ int destOfs = ReadPipe();
+ char* buf = (char*)hMapFilePtr + destOfs;
+ bool ret = snes_serialize((uint8_t*)buf,size);
+ WritePipe(eMessage_Complete);
+ WritePipe((char)(ret?1:0));
+ break;
+ }
+ case eMessage_CMD_unserialize:
+ {
+ //auto blob = ReadPipeBlob();
+ int size = ReadPipe();
+ int destOfs = ReadPipe();
+ char* buf = (char*)hMapFilePtr + destOfs;
+ bool ret = snes_unserialize((uint8_t*)buf ,size);
+ WritePipe(eMessage_Complete);
+ WritePipe((char)(ret?1:0));
+ break;
+ }
+
+ case eMessage_CMD_load_cartridge_normal:
+ s_EmulationControl.command = eEmulationCommand_LoadCartridgeNormal;
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ co_switch(co_emu);
+ break;
+
+ case eMessage_CMD_load_cartridge_super_game_boy:
+ {
+ printf("skunjk");
+ std::string rom_xml = ReadPipeString();
+ const char* rom_xmlptr = NULL;
+ if(rom_xml != "") rom_xmlptr = rom_xml.c_str();
+ Blob rom_data = ReadPipeBlob();
+ uint32 rom_length = rom_data.size();
+
+ std::string dmg_xml = ReadPipeString();
+ const char* dmg_xmlptr = NULL;
+ if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str();
+ Blob dmg_data = ReadPipeBlob();
+ uint32 dmg_length = dmg_data.size();
+
+ bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length);
+
+ printf("WRITING COMPLETE\n");
+ WritePipe(eMessage_Complete);
+ WritePipe((char)(ret?1:0));
+
+ break;
+ }
+
+ case eMessage_CMD_term:
+ snes_term();
+ WritePipe(eMessage_BRK_Complete);
+ break;
+
+ case eMessage_CMD_unload_cartridge:
+ snes_unload_cartridge();
+ WritePipe(eMessage_BRK_Complete);
+ break;
+ }
+
+ return true;
+}
+
+void Handle_SIG_audio_flush()
+{
+ WritePipe(eMessage_SIG_audio_flush);
+
+ int nsamples = audiobuffer_idx;
+ WritePipe(nsamples);
+ char* buf = ReadPipeSharedPtr();
+ memcpy(buf,audiobuffer,nsamples*2);
+ //extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety.
+ WritePipe(0); //dummy synchronization
+
+ //wait for frontend to consume data
+
+ ReadPipe(); //dummy synchronization
+ WritePipe(0); //dummy synchronization
+ audiobuffer_idx = 0;
+}
+
+void RunControlMessageLoop()
{
for(;;)
{
+TOP:
+ switch(s_EmulationControl.exitReason)
+ {
+ case eEmulationExitReason_NotSet:
+ goto HANDLEMESSAGES;
+
+ case eEmulationExitReason_Complete:
+ //printf("eEmulationExitReason_Complete (command:%d)\n",s_EmulationControl.command);
+ //MessageBox(0,"ZING","ZING",MB_OK);
+ //printf("WRITING COMPLETE\n");
+ WritePipe(eMessage_Complete);
+
+ //special post-completion messages (return values)
+ switch(s_EmulationControl.command)
+ {
+ case eEmulationCommand_LoadCartridgeNormal:
+ WritePipe((char)(s_EmulationControl.cmd_loadCartridgeNormal_params.result?1:0));
+ break;
+ }
+
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ s_EmulationControl.command = eEmulationCommand_NotSet;
+ goto TOP;
+
+ case eEmulationExitReason_SIG:
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ switch(s_EmulationControl.exitCallbackType)
+ {
+ case eEmulationCallback_snes_video_refresh:
+ {
+ WritePipe(eMessage_SIG_video_refresh);
+ WritePipe(s_EmulationControl.cb_video_refresh_params.width);
+ WritePipe(s_EmulationControl.cb_video_refresh_params.height);
+ int destOfs = ReadPipe();
+ char* buf = (char*)hMapFilePtr + destOfs;
+ int bufsize = 512 * 480 * 4;
+ memcpy(buf,s_EmulationControl.cb_video_refresh_params.data,bufsize);
+ WritePipe((char)0); //dummy synchronization
+ break;
+ }
+ case eEmulationCallback_snes_audio_flush:
+ Handle_SIG_audio_flush();
+ break;
+ case eEmulationCallback_snes_input_poll:
+ WritePipe(eMessage_SIG_input_poll);
+ break;
+ case eEmulationCallback_snes_input_state:
+ WritePipe(eMessage_SIG_input_state);
+ WritePipe(s_EmulationControl.cb_input_state_params.port);
+ WritePipe(s_EmulationControl.cb_input_state_params.device);
+ WritePipe(s_EmulationControl.cb_input_state_params.index);
+ WritePipe(s_EmulationControl.cb_input_state_params.id);
+ s_EmulationControl.cb_input_state_params.result = ReadPipe();
+ break;
+ case eEmulationCallback_snes_input_notify:
+ WritePipe(eMessage_SIG_input_notify);
+ WritePipe(s_EmulationControl.cb_input_notify_params.index);
+ break;
+ case eEmulationCallback_snes_path_request:
+ {
+ WritePipe(eMessage_SIG_path_request);
+ WritePipe(s_EmulationControl.cb_path_request_params.slot);
+ WritePipeString(s_EmulationControl.cb_path_request_params.hint);
+ std::string temp = ReadPipeString();
+ //yucky! use strncpy and ARRAY_SIZE or something!
+ strcpy(s_EmulationControl.cb_path_request_params.result,temp.c_str());
+ }
+ break;
+ case eEmulationCallback_snes_allocSharedMemory:
+ implementation_snes_allocSharedMemory();
+ break;
+ case eEmulationCallback_snes_freeSharedMemory:
+ implementation_snes_freeSharedMemory();
+ break;
+ case eEmulationCallback_snes_trace:
+ WritePipe(eMessage_SIG_trace_callback);
+ WritePipeString(s_EmulationControl.cb_trace_params.msg);
+ break;
+ }
+ //when callbacks finish, we automatically resume emulation. be careful to go back to top!!!!!!!!
+ SETEMU;
+ goto TOP;
+
+ case eEmulationExitReason_BRK:
+ s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
+ switch(s_EmulationControl.hookExitType)
+ {
+ case eMessage_BRK_hook_exec:
+ WritePipe(eMessage_BRK_hook_exec);
+ WritePipe((uint32)s_EmulationControl.hookAddr);
+ break;
+ case eMessage_BRK_hook_read:
+ WritePipe(eMessage_BRK_hook_read);
+ WritePipe((uint32)s_EmulationControl.hookAddr);
+ break;
+ case eMessage_BRK_hook_write:
+ WritePipe(eMessage_BRK_hook_write);
+ WritePipe((uint32)s_EmulationControl.hookAddr);
+ WritePipe((uint8)s_EmulationControl.hookValue);
+ break;
+ }
+ goto TOP;
+ }
+
+HANDLEMESSAGES:
+
//printf("Reading message from pipe...\n");
- auto msg = ReadPipe();
- //printf("slam %08X\n",msg);
+ eMessage msg = ReadPipe();
+ //printf("...slam: %08X\n",msg);
+
+ if(Handle_QUERY(msg))
+ goto TOP;
+ if(Handle_CMD(msg))
+ goto TOP;
+
switch(msg)
{
case eMessage_Complete:
return;
-
- case eMessage_snes_library_id: WritePipeString(snes_library_id()); break;
- case eMessage_snes_library_revision_major: WritePipe(snes_library_revision_major()); break;
- case eMessage_snes_library_revision_minor: WritePipe(snes_library_revision_minor()); break;
-
- case eMessage_snes_init:
- snes_init();
- WritePipe(eMessage_Complete);
- break;
- case eMessage_snes_power: snes_power(); break;
- case eMessage_snes_reset: snes_reset(); break;
- case eMessage_snes_run:
- FlushAudio();
- snes_run();
- FlushAudio();
- WritePipe(eMessage_Complete);
- break;
- case eMessage_snes_term: snes_term(); break;
- case eMessage_snes_unload_cartridge: snes_unload_cartridge(); break;
-
- case eMessage_snes_load_cartridge_normal:
- {
- Blob xml = ReadPipeBlob();
- xml.push_back(0); //make sure the xml is null terminated
- const char* xmlptr = NULL;
- if(xml.size() != 1) xmlptr = &xml[0];
-
- Blob rom_data = ReadPipeBlob();
- const unsigned char* rom_ptr = NULL;
- if(rom_data.size() != 0) rom_ptr = (unsigned char*)&rom_data[0];
-
- bool ret = snes_load_cartridge_normal(xmlptr,rom_ptr,rom_data.size());
- WritePipe(eMessage_Complete);
- WritePipe((char)(ret?1:0));
- break;
- }
-
- case eMessage_snes_load_cartridge_super_game_boy:
- {
- std::string rom_xml = ReadPipeString();
- const char* rom_xmlptr = NULL;
- if(rom_xml != "") rom_xmlptr = rom_xml.c_str();
- Blob rom_data = ReadPipeBlob();
- uint32 rom_length = rom_data.size();
-
- std::string dmg_xml = ReadPipeString();
- const char* dmg_xmlptr = NULL;
- if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str();
- Blob dmg_data = ReadPipeBlob();
- uint32 dmg_length = dmg_data.size();
-
- bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length);
-
- WritePipe(eMessage_Complete);
- WritePipe((char)(ret?1:0));
-
- break;
- }
-
- case eMessage_snes_get_region:
- WritePipe((char)snes_get_region());
- break;
-
- case eMessage_snes_get_memory_size:
- WritePipe(snes_get_memory_size(ReadPipe()));
- break;
-
- case eMessage_snes_get_memory_data:
- {
- unsigned int id = ReadPipe();
- char* dstbuf = ReadPipeSharedPtr();
- uint8_t* srcbuf = snes_get_memory_data(id);
- memcpy(dstbuf,srcbuf,snes_get_memory_size(id));
- WritePipe(eMessage_Complete);
- break;
- }
-
- case eMessage_peek:
- {
- int id = ReadPipe();
- unsigned int addr = ReadPipe();
- uint8_t ret;
- if(id == SNES_MEMORY_SYSBUS)
- ret = bus_read(addr);
- else ret = snes_get_memory_data(id)[addr];
- WritePipe(ret);
- }
- break;
-
- case eMessage_poke:
- {
- int id = ReadPipe();
- unsigned int addr = ReadPipe();
- uint8_t val = ReadPipe();
- if(id == SNES_MEMORY_SYSBUS)
- bus_write(addr,val);
- else snes_get_memory_data(id)[addr] = val;
- break;
- }
- break;
-
- case eMessage_snes_serialize_size:
- WritePipe(snes_serialize_size());
- break;
-
- case eMessage_snes_serialize:
- {
- int size = ReadPipe();
- int destOfs = ReadPipe();
- char* buf = (char*)hMapFilePtr + destOfs;
- bool ret = snes_serialize((uint8_t*)buf,size);
- WritePipe(eMessage_Complete);
- WritePipe((char)(ret?1:0));
- break;
- }
- case eMessage_snes_unserialize:
- {
- //auto blob = ReadPipeBlob();
- int size = ReadPipe();
- int destOfs = ReadPipe();
- char* buf = (char*)hMapFilePtr + destOfs;
- bool ret = snes_unserialize((uint8_t*)buf ,size);
- WritePipe(eMessage_Complete);
- WritePipe((char)(ret?1:0));
- break;
- }
-
- case eMessage_snes_poll_message:
- //TBD
- WritePipe(-1);
- break;
- case eMessage_snes_dequeue_message:
- //TBD
- break;
-
- case eMessage_snes_set_color_lut:
- {
- auto blob = ReadPipeBlob();
- snes_set_color_lut((uint32_t*)&blob[0]);
- break;
- }
- break;
-
- case eMessage_snes_enable_trace:
- if(!!ReadPipe())
- snes_set_trace_callback(snes_trace);
- else snes_set_trace_callback(NULL);
- break;
-
- case eMessage_snes_enable_scanline:
- if(ReadPipe())
- snes_set_scanlineStart(snes_scanlineStart);
- else snes_set_scanlineStart(NULL);
- break;
-
- case eMessage_snes_enable_audio:
- audio_en = ReadPipe();
- break;
-
- case eMessage_snes_set_layer_enable:
- {
- int layer = ReadPipe();
- int priority = ReadPipe();
- bool enable = ReadPipe();
- snes_set_layer_enable(layer,priority,enable);
- break;
- }
-
- case eMessage_snes_set_backdropColor:
- snes_set_backdropColor(ReadPipe());
- break;
-
- case eMessage_snes_peek_logical_register:
- WritePipe(snes_peek_logical_register(ReadPipe()));
- break;
-
- case eMessage_GetMemoryIdName:
- {
- uint32 id = ReadPipe();
- const char* ret = snes_get_memory_id_name(id);
- if(!ret) ret = "";
- WritePipeString(ret);
- break;
- }
-
+
case eMessage_SetBuffer:
{
+ printf("eMessage_SetBuffer\n");
int which = ReadPipe();
std::string name = ReadPipeString();
IPCRingBuffer* ipcrb = new IPCRingBuffer();
@@ -730,26 +1144,6 @@ void RunMessageLoop()
bufio = false;
break;
- case eMessage_set_state_hook_exec:
- SNES::cpu.debugger.op_exec = ReadPipe() ? debug_op_exec : hook();
- break;
-
- case eMessage_set_state_hook_read:
- SNES::cpu.debugger.op_read = ReadPipe() ? debug_op_read : hook();
- break;
-
- case eMessage_set_state_hook_write:
- SNES::cpu.debugger.op_write = ReadPipe() ? debug_op_write : hook();
- break;
-
- case eMessage_set_state_hook_nmi:
- SNES::cpu.debugger.op_nmi = ReadPipe() ? debug_op_nmi : hook();
- break;
-
- case eMessage_set_state_hook_irq:
- SNES::cpu.debugger.op_irq = ReadPipe() ? debug_op_irq : hook();
- break;
-
} //switch(msg)
}
}
@@ -763,6 +1157,68 @@ void OpenConsole()
freopen("CONIN$", "r", stdin);
}
+void handleCommand_LoadCartridgeNormal()
+{
+ Blob xml = ReadPipeBlob();
+ xml.push_back(0); //make sure the xml is null terminated
+ const char* xmlptr = NULL;
+ if(xml.size() != 1) xmlptr = &xml[0];
+
+ Blob rom_data = ReadPipeBlob();
+ const unsigned char* rom_ptr = NULL;
+ if(rom_data.size() != 0) rom_ptr = (unsigned char*)&rom_data[0];
+
+ s_EmulationControl.cmd_loadCartridgeNormal_params.result = snes_load_cartridge_normal(xmlptr,rom_ptr,rom_data.size());
+}
+
+void emuthread()
+{
+ for(;;)
+ {
+ switch(s_EmulationControl.command)
+ {
+ case eEmulationCommand_Init:
+ snes_init();
+ break;
+ case eEmulationCommand_Power:
+ snes_power();
+ break;
+ case eEmulationCommand_Reset:
+ snes_reset();
+ break;
+ case eEmulationCommand_FrameAdvance:
+ SIG_FlushAudio();
+
+ //we could avoid this if we saved the current thread before jumping back to co_control, instead of always jumping back to co_emu
+ //in effect, we're scrambling the scheduler
+ //EDIT - well, we changed that, but.. we still want this probably, for debugging and stuff
+ for(;;)
+ {
+ SNES::scheduler.sync = SNES::Scheduler::SynchronizeMode::None;
+ SNES::scheduler.clearExitReason();
+ SNES::scheduler.enter();
+ if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::FrameEvent)
+ {
+ SNES::video.update();
+ break;
+ }
+ //not used yet
+ if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent)
+ break;
+ }
+
+ SIG_FlushAudio();
+ break;
+ case eEmulationCommand_LoadCartridgeNormal:
+ handleCommand_LoadCartridgeNormal();
+ break;
+ }
+
+ s_EmulationControl.exitReason = eEmulationExitReason_Complete;
+ SETCONTROL;
+ }
+}
+
int xmain(int argc, char** argv)
{
if(argc != 2)
@@ -784,7 +1240,7 @@ int xmain(int argc, char** argv)
{
OpenConsole();
}
-
+
printf("pipe: %s\n",pipename);
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
@@ -798,10 +1254,14 @@ int xmain(int argc, char** argv)
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
+ //make a coroutine thread to run the emulation in. we'll switch back to this thread when communicating with the frontend
+ co_control = co_active();
+ co_emu = co_create(65536*sizeof(void*),emuthread);
+
running = true;
printf("running\n");
- RunMessageLoop();
+ RunControlMessageLoop();
return 0;
}
@@ -810,7 +1270,6 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine
{
int argc = __argc;
char** argv = __argv;
-
if(argc != 2)
{
if(IDOK == MessageBox(0,"This program is run from the libsneshawk emulator core. It is useless to you directly. But if you're really, that curious, click OK.","Whatfor my daddy-o",MB_OKCANCEL))
diff --git a/libsnes/upx.exe b/libsnes/upx.exe
index e23a9ed9b0..1c331c703a 100644
Binary files a/libsnes/upx.exe and b/libsnes/upx.exe differ
diff --git a/output/dll/libsneshawk-32-compatibility.exe b/output/dll/libsneshawk-32-compatibility.exe
index a1502704aa..5a6b66d873 100644
Binary files a/output/dll/libsneshawk-32-compatibility.exe and b/output/dll/libsneshawk-32-compatibility.exe differ
diff --git a/output/dll/libsneshawk-32-performance.exe b/output/dll/libsneshawk-32-performance.exe
index 525dd8bc10..e8f115f011 100644
Binary files a/output/dll/libsneshawk-32-performance.exe and b/output/dll/libsneshawk-32-performance.exe differ