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