for evaluation:
overhaul snes core -- radically simplify IPC. There were two goals 1. speedup (failed) 2. not break in the old way of becoming unstable when GC happens (uncertain) 3. lay framework for simplified libretro interface (success, provisional on #2) the speedup failed, I think, because a slower libco is needed to coexist with .net. I think I will fix this be incorporating both libcos, one for bsnes internal use, and one for .net<->c++ switching
This commit is contained in:
parent
3bb1d0849d
commit
fbd7601b77
BizHawk.Client.EmuHawk/config/SNES
BizHawk.Common/IPC
BizHawk.Emulation.Cores/Consoles/Nintendo/SNES
LibsnesApi.csLibsnesApi_BRK.csLibsnesApi_CMD.csLibsnesApi_Enums.csLibsnesApi_QUERY.csLibsnesApi_SIG.csLibsnesCore.cs
Building Other Solutions.txtlibsnes
bsnes
vs2015
output/dll
|
@ -35,8 +35,6 @@
|
|||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.rbAccuracy = new System.Windows.Forms.RadioButton();
|
||||
this.rbPerformance = new System.Windows.Forms.RadioButton();
|
||||
this.cbRingbuf = new System.Windows.Forms.CheckBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.cbDoubleSize = new System.Windows.Forms.CheckBox();
|
||||
this.lblDoubleSize = new System.Windows.Forms.Label();
|
||||
this.cbForceDeterminism = new System.Windows.Forms.CheckBox();
|
||||
|
@ -47,7 +45,7 @@
|
|||
// btnOk
|
||||
//
|
||||
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnOk.Location = new System.Drawing.Point(136, 344);
|
||||
this.btnOk.Location = new System.Drawing.Point(136, 286);
|
||||
this.btnOk.Name = "btnOk";
|
||||
this.btnOk.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnOk.TabIndex = 0;
|
||||
|
@ -59,7 +57,7 @@
|
|||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(217, 344);
|
||||
this.btnCancel.Location = new System.Drawing.Point(217, 286);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 1;
|
||||
|
@ -123,28 +121,10 @@
|
|||
this.rbPerformance.Text = "Performance (only for casual gaming!)";
|
||||
this.rbPerformance.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cbRingbuf
|
||||
//
|
||||
this.cbRingbuf.AutoSize = true;
|
||||
this.cbRingbuf.Location = new System.Drawing.Point(19, 134);
|
||||
this.cbRingbuf.Name = "cbRingbuf";
|
||||
this.cbRingbuf.Size = new System.Drawing.Size(115, 17);
|
||||
this.cbRingbuf.TabIndex = 4;
|
||||
this.cbRingbuf.Text = "Use Ring Buffer IO";
|
||||
this.cbRingbuf.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Location = new System.Drawing.Point(35, 154);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(254, 45);
|
||||
this.label1.TabIndex = 5;
|
||||
this.label1.Text = "Performance-affecting option; results differ for different users\' systems.";
|
||||
//
|
||||
// cbDoubleSize
|
||||
//
|
||||
this.cbDoubleSize.AutoSize = true;
|
||||
this.cbDoubleSize.Location = new System.Drawing.Point(18, 189);
|
||||
this.cbDoubleSize.Location = new System.Drawing.Point(18, 130);
|
||||
this.cbDoubleSize.Name = "cbDoubleSize";
|
||||
this.cbDoubleSize.Size = new System.Drawing.Size(178, 17);
|
||||
this.cbDoubleSize.TabIndex = 6;
|
||||
|
@ -154,7 +134,7 @@
|
|||
//
|
||||
// lblDoubleSize
|
||||
//
|
||||
this.lblDoubleSize.Location = new System.Drawing.Point(36, 210);
|
||||
this.lblDoubleSize.Location = new System.Drawing.Point(36, 151);
|
||||
this.lblDoubleSize.Name = "lblDoubleSize";
|
||||
this.lblDoubleSize.Size = new System.Drawing.Size(254, 57);
|
||||
this.lblDoubleSize.TabIndex = 7;
|
||||
|
@ -165,7 +145,7 @@
|
|||
// cbForceDeterminism
|
||||
//
|
||||
this.cbForceDeterminism.AutoSize = true;
|
||||
this.cbForceDeterminism.Location = new System.Drawing.Point(19, 271);
|
||||
this.cbForceDeterminism.Location = new System.Drawing.Point(19, 212);
|
||||
this.cbForceDeterminism.Name = "cbForceDeterminism";
|
||||
this.cbForceDeterminism.Size = new System.Drawing.Size(113, 17);
|
||||
this.cbForceDeterminism.TabIndex = 8;
|
||||
|
@ -175,7 +155,7 @@
|
|||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Location = new System.Drawing.Point(38, 295);
|
||||
this.label3.Location = new System.Drawing.Point(38, 236);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(251, 41);
|
||||
this.label3.TabIndex = 9;
|
||||
|
@ -188,13 +168,11 @@
|
|||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(304, 379);
|
||||
this.ClientSize = new System.Drawing.Size(304, 321);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.cbForceDeterminism);
|
||||
this.Controls.Add(this.lblDoubleSize);
|
||||
this.Controls.Add(this.cbDoubleSize);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.cbRingbuf);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.btnOk);
|
||||
|
@ -220,8 +198,6 @@
|
|||
private System.Windows.Forms.RadioButton rbCompatibility;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.RadioButton rbPerformance;
|
||||
private System.Windows.Forms.CheckBox cbRingbuf;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.CheckBox cbDoubleSize;
|
||||
private System.Windows.Forms.Label lblDoubleSize;
|
||||
private System.Windows.Forms.RadioButton rbAccuracy;
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
var ss = ((LibsnesCore)Global.Emulator).GetSyncSettings();
|
||||
var dlg = new SNESOptions
|
||||
{
|
||||
UseRingBuffer = s.UseRingBuffer,
|
||||
AlwaysDoubleSize = s.AlwaysDoubleSize,
|
||||
ForceDeterminism = s.ForceDeterminism,
|
||||
Profile = ss.Profile
|
||||
|
@ -30,7 +29,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
var result = dlg.ShowDialog(owner);
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
s.UseRingBuffer = dlg.UseRingBuffer;
|
||||
s.AlwaysDoubleSize = dlg.AlwaysDoubleSize;
|
||||
s.ForceDeterminism = dlg.ForceDeterminism;
|
||||
ss.Profile = dlg.Profile;
|
||||
|
@ -62,12 +60,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
public bool UseRingBuffer
|
||||
{
|
||||
get { return cbRingbuf.Checked; }
|
||||
set { cbRingbuf.Checked = value; }
|
||||
}
|
||||
|
||||
public bool AlwaysDoubleSize
|
||||
{
|
||||
get { return UserDoubleSizeOption; }
|
||||
|
|
|
@ -1,286 +0,0 @@
|
|||
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;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// note that a few bytes of the size will be used for a management area
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
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.
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
[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
|
||||
{
|
||||
private readonly Queue<byte> shazam = new Queue<byte>();
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// A stream on top of an IPCRingBuffer
|
||||
/// </summary>
|
||||
public unsafe class IPCRingBufferStream : Stream
|
||||
{
|
||||
private readonly IPCRingBuffer buf;
|
||||
|
||||
public IPCRingBufferStream(IPCRingBuffer buf)
|
||||
{
|
||||
this.buf = 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
|
|
@ -22,34 +22,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
// this is a lot of work, but it will be some decent speedups. who wouldve ever thought to make an emulator this way? I will, from now on...
|
||||
//todo - use a reader/writer ring buffer for communication instead of pipe
|
||||
//todo - when exe wrapper is fully baked, put it into mingw so we can just have libsneshawk.exe without a separate dll. it hardly needs any debugging presently, it should be easy to maintain.
|
||||
|
||||
|
||||
//space optimizations to deploy later (only if people complain about so many files)
|
||||
//todo - put executables in zipfiles and search for them there; dearchive to a .cache folder. check timestamps to know when to freshen. this is weird.....
|
||||
|
||||
//speedups to deploy later:
|
||||
//todo - convey rom data faster than pipe blob (use shared memory) (WARNING: right now our general purpose shared memory is only 1MB. maybe wait until ring buffer IPC)
|
||||
//todo - collapse input messages to one IPC operation. right now theres like 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)
|
||||
|
||||
|
||||
InstanceDll instanceDll;
|
||||
string InstanceName;
|
||||
NamedPipeServerStream pipe;
|
||||
BinaryWriter bwPipe;
|
||||
BinaryReader brPipe;
|
||||
MemoryMappedFile mmf;
|
||||
MemoryMappedViewAccessor mmva;
|
||||
byte* mmvaPtr;
|
||||
IPCRingBuffer rbuf, wbuf;
|
||||
IPCRingBufferStream rbufstr, wbufstr;
|
||||
SwitcherStream rstream, wstream;
|
||||
bool bufio;
|
||||
|
||||
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
||||
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void DllInit(string ipcname);
|
||||
delegate CommStruct* DllInit();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void MessageApi(eMessage msg);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void BufferApi(int id, void* ptr, int size);
|
||||
|
||||
CommStruct* comm;
|
||||
MessageApi Message;
|
||||
BufferApi CopyBuffer; //TODO: consider making private and wrapping
|
||||
BufferApi SetBuffer; //TODO: consider making private and wrapping
|
||||
|
||||
public LibsnesApi(string dllPath)
|
||||
{
|
||||
|
@ -57,147 +56,53 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
var pipeName = InstanceName;
|
||||
|
||||
mmf = MemoryMappedFile.CreateNew(pipeName, 1024 * 1024);
|
||||
mmva = mmf.CreateViewAccessor();
|
||||
mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref mmvaPtr);
|
||||
|
||||
pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024 * 1024, 1024);
|
||||
|
||||
instanceDll = new InstanceDll(dllPath);
|
||||
var dllinit = (DllInit)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("DllInit"), typeof(DllInit));
|
||||
dllinit(pipeName);
|
||||
Message = (MessageApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("Message"), typeof(MessageApi));
|
||||
CopyBuffer = (BufferApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("CopyBuffer"), typeof(BufferApi));
|
||||
SetBuffer = (BufferApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("SetBuffer"), typeof(BufferApi));
|
||||
|
||||
//TODO - start a thread to wait for process to exit and gracefully handle errors? how about the pipe?
|
||||
|
||||
pipe.WaitForConnection();
|
||||
|
||||
rbuf = new IPCRingBuffer();
|
||||
wbuf = new IPCRingBuffer();
|
||||
rbuf.Allocate(1024);
|
||||
wbuf.Allocate(1024);
|
||||
rbufstr = new IPCRingBufferStream(rbuf);
|
||||
wbufstr = new IPCRingBufferStream(wbuf);
|
||||
|
||||
rstream = new SwitcherStream();
|
||||
wstream = new SwitcherStream();
|
||||
|
||||
rstream.SetCurrStream(pipe);
|
||||
wstream.SetCurrStream(pipe);
|
||||
|
||||
brPipe = new BinaryReader(rstream);
|
||||
bwPipe = new BinaryWriter(wstream);
|
||||
|
||||
WritePipeMessage(eMessage.eMessage_SetBuffer);
|
||||
bwPipe.Write(1);
|
||||
WritePipeString(rbuf.Id);
|
||||
WritePipeMessage(eMessage.eMessage_SetBuffer);
|
||||
bwPipe.Write(0);
|
||||
WritePipeString(wbuf.Id);
|
||||
bwPipe.Flush();
|
||||
comm = dllinit();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_Shutdown);
|
||||
WaitForCompletion();
|
||||
instanceDll.Dispose();
|
||||
|
||||
pipe.Dispose();
|
||||
mmva.Dispose();
|
||||
mmf.Dispose();
|
||||
rbuf.Dispose();
|
||||
wbuf.Dispose();
|
||||
foreach (var smb in DeallocatedMemoryBlocks.Values)
|
||||
smb.Dispose();
|
||||
DeallocatedMemoryBlocks.Clear();
|
||||
}
|
||||
|
||||
public void BeginBufferIO()
|
||||
public void CopyString(string str)
|
||||
{
|
||||
bufio = true;
|
||||
WritePipeMessage(eMessage.eMessage_BeginBufferIO);
|
||||
rstream.SetCurrStream(rbufstr);
|
||||
wstream.SetCurrStream(wbufstr);
|
||||
fixed (char* cp = str)
|
||||
CopyBuffer(0, cp, str.Length + 1);
|
||||
}
|
||||
|
||||
public void EndBufferIO()
|
||||
public void CopyBytes(byte[] bytes)
|
||||
{
|
||||
if(!bufio) return;
|
||||
bufio = false;
|
||||
WritePipeMessage(eMessage.eMessage_EndBufferIO);
|
||||
rstream.SetCurrStream(pipe);
|
||||
wstream.SetCurrStream(pipe);
|
||||
fixed (byte* bp = bytes)
|
||||
CopyBuffer(0, bp, bytes.Length);
|
||||
}
|
||||
|
||||
void WritePipeString(string str)
|
||||
public void SetAscii(string str)
|
||||
{
|
||||
WritePipeBlob(System.Text.Encoding.ASCII.GetBytes(str));
|
||||
fixed (char* cp = str)
|
||||
SetBuffer(0, cp, str.Length + 1);
|
||||
}
|
||||
|
||||
byte[] ReadPipeBlob()
|
||||
public void SetBytes(byte[] bytes)
|
||||
{
|
||||
int len = brPipe.ReadInt32();
|
||||
var ret = new byte[len];
|
||||
brPipe.Read(ret, 0, len);
|
||||
return ret;
|
||||
fixed (byte* bp = bytes)
|
||||
SetBuffer(0, bp, bytes.Length);
|
||||
}
|
||||
|
||||
void WritePipeBlob(byte[] blob)
|
||||
public void SetBytes2(byte[] bytes)
|
||||
{
|
||||
bwPipe.Write(blob.Length);
|
||||
bwPipe.Write(blob);
|
||||
bwPipe.Flush();
|
||||
fixed (byte* bp = bytes)
|
||||
SetBuffer(1, bp, bytes.Length);
|
||||
}
|
||||
|
||||
public int MessageCounter;
|
||||
|
||||
void WritePipeInt(int n)
|
||||
{
|
||||
}
|
||||
|
||||
void WritePipePointer(IntPtr ptr, bool flush = true)
|
||||
{
|
||||
bwPipe.Write(ptr.ToInt32());
|
||||
if(flush) bwPipe.Flush();
|
||||
}
|
||||
|
||||
void WritePipeMessage(eMessage msg)
|
||||
{
|
||||
if(!bufio) MessageCounter++;
|
||||
//Console.WriteLine("write pipe message: " + msg);
|
||||
bwPipe.Write((int)msg);
|
||||
bwPipe.Flush();
|
||||
}
|
||||
|
||||
eMessage ReadPipeMessage()
|
||||
{
|
||||
return (eMessage)brPipe.ReadInt32();
|
||||
}
|
||||
|
||||
string ReadPipeString()
|
||||
{
|
||||
int len = brPipe.ReadInt32();
|
||||
var bytes = brPipe.ReadBytes(len);
|
||||
return System.Text.ASCIIEncoding.ASCII.GetString(bytes);
|
||||
}
|
||||
|
||||
void WaitForCompletion()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
var msg = ReadPipeMessage();
|
||||
if (!bufio) MessageCounter++;
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
public Action<uint> ReadHook, ExecHook;
|
||||
public Action<uint, byte> WriteHook;
|
||||
|
||||
|
@ -243,9 +148,98 @@ 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 void SPECIAL_Resume()
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CPURegs
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_ResumeAfterBRK);
|
||||
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;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LayerEnables
|
||||
{
|
||||
byte _BG1_Prio0, _BG1_Prio1;
|
||||
byte _BG2_Prio0, _BG2_Prio1;
|
||||
byte _BG3_Prio0, _BG3_Prio1;
|
||||
byte _BG4_Prio0, _BG4_Prio1;
|
||||
byte _Obj_Prio0, _Obj_Prio1, _Obj_Prio2, _Obj_Prio3;
|
||||
|
||||
public bool BG1_Prio0 { get { return _BG1_Prio0 != 0; } set { _BG1_Prio0 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG1_Prio1 { get { return _BG1_Prio1 != 0; } set { _BG1_Prio1 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG2_Prio0 { get { return _BG2_Prio0 != 0; } set { _BG2_Prio0 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG2_Prio1 { get { return _BG2_Prio1 != 0; } set { _BG2_Prio1 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG3_Prio0 { get { return _BG3_Prio0 != 0; } set { _BG3_Prio0 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG3_Prio1 { get { return _BG3_Prio1 != 0; } set { _BG3_Prio1 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG4_Prio0 { get { return _BG4_Prio0 != 0; } set { _BG4_Prio0 = (byte)(value ? 1 : 0); } }
|
||||
public bool BG4_Prio1 { get { return _BG4_Prio1 != 0; } set { _BG4_Prio1 = (byte)(value ? 1 : 0); } }
|
||||
|
||||
public bool Obj_Prio0 { get { return _Obj_Prio0 != 0; } set { _Obj_Prio0 = (byte)(value ? 1 : 0); } }
|
||||
public bool Obj_Prio1 { get { return _Obj_Prio1 != 0; } set { _Obj_Prio1 = (byte)(value ? 1 : 0); } }
|
||||
public bool Obj_Prio2 { get { return _Obj_Prio2 != 0; } set { _Obj_Prio2 = (byte)(value ? 1 : 0); } }
|
||||
public bool Obj_Prio3 { get { return _Obj_Prio3 != 0; } set { _Obj_Prio3 = (byte)(value ? 1 : 0); } }
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct CommStruct
|
||||
{
|
||||
//the cmd being executed
|
||||
public eMessage cmd;
|
||||
|
||||
//the status of the core
|
||||
public eStatus status;
|
||||
|
||||
//the SIG or BRK that the core is halted in
|
||||
public eMessage reason;
|
||||
|
||||
//flexible in/out parameters
|
||||
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
|
||||
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
|
||||
public sbyte* str;
|
||||
public void* ptr;
|
||||
public uint id, addr, value, size;
|
||||
public int port, device, index, slot;
|
||||
public uint width, height;
|
||||
public int scanline;
|
||||
|
||||
//this should always be used in pairs
|
||||
public void* buf0, buf1;
|
||||
public int buf_size0, buf_size1;
|
||||
|
||||
//bleck. this is a long so that it can be a 32/64bit pointer
|
||||
public fixed long cdl_ptr[4];
|
||||
public fixed uint cdl_size[4];
|
||||
|
||||
public CPURegs cpuregs;
|
||||
public LayerEnables layerEnables;
|
||||
|
||||
//static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command
|
||||
public SNES_REGION region;
|
||||
public SNES_MAPPER mapper;
|
||||
|
||||
//utilities
|
||||
//TODO: make internal, wrap on the API instead of the comm
|
||||
public unsafe string GetAscii() { return _getAscii(str); }
|
||||
public bool GetBool() { return value != 0; }
|
||||
|
||||
private unsafe string _getAscii(sbyte* ptr) {
|
||||
int len = 0;
|
||||
sbyte* junko = (sbyte*)ptr;
|
||||
while(junko[len] != 0) len++;
|
||||
|
||||
return new string((sbyte*)str, 0, len, System.Text.Encoding.ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
public SNES_REGION Region { get { return comm->region; } }
|
||||
public SNES_MAPPER Mapper { get { return comm->mapper; } }
|
||||
public void SetLayerEnables(ref LayerEnables enables)
|
||||
{
|
||||
comm->layerEnables = enables;
|
||||
QUERY_set_layer_enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,21 +15,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
case eMessage.eMessage_BRK_hook_exec:
|
||||
{
|
||||
var addr = brPipe.ReadInt32();
|
||||
ExecHook((uint)addr);
|
||||
ExecHook(comm->addr);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_BRK_hook_read:
|
||||
{
|
||||
var addr = brPipe.ReadInt32();
|
||||
ReadHook((uint)addr);
|
||||
ReadHook(comm->addr);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_BRK_hook_write:
|
||||
{
|
||||
var addr = brPipe.ReadInt32();
|
||||
var value = brPipe.ReadByte();
|
||||
WriteHook((uint)addr, value);
|
||||
WriteHook(comm->addr, (byte)comm->value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -40,13 +36,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
break;
|
||||
|
||||
case eMessage.eMessage_BRK_scanlineStart:
|
||||
int line = brPipe.ReadInt32();
|
||||
if (scanlineStart != null)
|
||||
scanlineStart(line);
|
||||
SPECIAL_Resume();
|
||||
scanlineStart(comm->scanline);
|
||||
break;
|
||||
|
||||
} //switch(msg)
|
||||
|
||||
Message(eMessage.eMessage_ResumeAfterBRK);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,90 +8,92 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
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.ReadInt32() != 0;
|
||||
if (ret)
|
||||
{
|
||||
CopyMemory(data.ToPointer(), mmvaPtr, (ulong)size);
|
||||
}
|
||||
comm->buf0 = data.ToPointer();
|
||||
comm->buf_size0 = size;
|
||||
Message(eMessage.eMessage_CMD_serialize);
|
||||
WaitForCMD();
|
||||
bool ret = comm->GetBool();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WaitForCMD()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
if (comm->status == eStatus.eStatus_Idle)
|
||||
break;
|
||||
if (Handle_SIG(comm->reason)) continue;
|
||||
if (Handle_BRK(comm->reason)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
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.ReadInt32() != 0;
|
||||
comm->buf0 = data.ToPointer();
|
||||
comm->buf_size0 = size;
|
||||
Message(eMessage.eMessage_CMD_unserialize);
|
||||
WaitForCMD();
|
||||
bool ret = comm->GetBool();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void CMD_init()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_init);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_init);
|
||||
WaitForCMD();
|
||||
}
|
||||
public void CMD_power()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_power);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_power);
|
||||
WaitForCMD();
|
||||
}
|
||||
public void CMD_reset()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_reset);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_reset);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public void CMD_run()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_run);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_run);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
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)
|
||||
public bool CMD_load_cartridge_super_game_boy(string rom_xml, byte[] rom_data, uint rom_size, byte[] dmg_data)
|
||||
{
|
||||
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.ReadInt32() != 0;
|
||||
return ret;
|
||||
fixed (char* xmlcp = rom_xml)
|
||||
{
|
||||
comm->str = (sbyte*)xmlcp;
|
||||
SetBytes(rom_data);
|
||||
SetBytes2(dmg_data);
|
||||
Message(eMessage.eMessage_CMD_load_cartridge_sgb);
|
||||
WaitForCMD();
|
||||
return comm->GetBool();
|
||||
}
|
||||
}
|
||||
|
||||
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.ReadInt32() != 0;
|
||||
return ret;
|
||||
string xml = rom_xml==null?null:System.Text.Encoding.ASCII.GetString(rom_xml);
|
||||
fixed (char* xmlcp = xml)
|
||||
{
|
||||
comm->str = (sbyte*)xmlcp;
|
||||
SetBytes(rom_data);
|
||||
Message(eMessage.eMessage_CMD_load_cartridge_normal);
|
||||
WaitForCMD();
|
||||
return comm->GetBool();
|
||||
}
|
||||
}
|
||||
|
||||
public void CMD_term()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_term);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_term);
|
||||
WaitForCMD();
|
||||
}
|
||||
public void CMD_unload_cartridge()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_CMD_unload_cartridge);
|
||||
WaitForCompletion();
|
||||
Message(eMessage.eMessage_CMD_unload_cartridge);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,24 +10,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
eMessage_NotSet,
|
||||
|
||||
eMessage_SetBuffer,
|
||||
eMessage_BeginBufferIO,
|
||||
eMessage_EndBufferIO,
|
||||
eMessage_ResumeAfterBRK,
|
||||
eMessage_Shutdown,
|
||||
|
||||
eMessage_QUERY_library_id,
|
||||
eMessage_QUERY_library_revision_major,
|
||||
eMessage_QUERY_library_revision_minor,
|
||||
eMessage_QUERY_get_region,
|
||||
eMessage_QUERY_get_mapper,
|
||||
eMessage_QUERY_FIRST,
|
||||
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,
|
||||
|
@ -43,6 +33,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
eMessage_QUERY_peek_logical_register,
|
||||
eMessage_QUERY_peek_cpu_regs,
|
||||
eMessage_QUERY_set_cdl,
|
||||
eMessage_QUERY_LAST,
|
||||
|
||||
eMessage_CMD_FIRST,
|
||||
eMessage_CMD_init,
|
||||
|
@ -52,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
eMessage_CMD_serialize,
|
||||
eMessage_CMD_unserialize,
|
||||
eMessage_CMD_load_cartridge_normal,
|
||||
eMessage_CMD_load_cartridge_super_game_boy,
|
||||
eMessage_CMD_load_cartridge_sgb,
|
||||
eMessage_CMD_term,
|
||||
eMessage_CMD_unload_cartridge,
|
||||
eMessage_CMD_LAST,
|
||||
|
@ -76,6 +67,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
eMessage_BRK_scanlineStart,
|
||||
};
|
||||
|
||||
enum eStatus : int
|
||||
{
|
||||
eStatus_Idle,
|
||||
eStatus_CMD,
|
||||
eStatus_BRK
|
||||
};
|
||||
|
||||
public enum SNES_REG : int
|
||||
{
|
||||
|
@ -203,7 +200,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
STROM = 10
|
||||
}
|
||||
|
||||
public enum SNES_REGION : byte
|
||||
public enum SNES_REGION : uint
|
||||
{
|
||||
NTSC = 0,
|
||||
PAL = 1,
|
||||
|
|
|
@ -8,50 +8,18 @@ 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 SNES_MAPPER QUERY_get_mapper()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_get_mapper);
|
||||
return (SNES_MAPPER)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();
|
||||
comm->value = (uint)id;
|
||||
Message(eMessage.eMessage_QUERY_get_memory_size);
|
||||
return (int)comm->value;
|
||||
}
|
||||
|
||||
string QUERY_MemoryNameForId(SNES_MEMORY id)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_GetMemoryIdName);
|
||||
bwPipe.Write((uint)id);
|
||||
bwPipe.Flush();
|
||||
return ReadPipeString();
|
||||
comm->id = (uint)id;
|
||||
Message(eMessage.eMessage_QUERY_GetMemoryIdName);
|
||||
return comm->GetAscii();
|
||||
}
|
||||
|
||||
public byte* QUERY_get_memory_data(SNES_MEMORY id)
|
||||
|
@ -64,177 +32,138 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
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();
|
||||
comm->id = (uint)id;
|
||||
comm->addr = addr;
|
||||
Message(eMessage.eMessage_QUERY_peek);
|
||||
return (byte)comm->value;
|
||||
}
|
||||
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();
|
||||
comm->id = (uint)id;
|
||||
comm->addr = addr;
|
||||
comm->value = (byte)val;
|
||||
Message(eMessage.eMessage_QUERY_poke);
|
||||
}
|
||||
|
||||
public int QUERY_serialize_size()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_serialize_size);
|
||||
int ret = brPipe.ReadInt32();
|
||||
Message(eMessage.eMessage_QUERY_serialize_size);
|
||||
int ret = (int)comm->size;
|
||||
if (ret > 100)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
else Console.WriteLine("WHY????????");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
comm->ptr = colors.ToPointer();
|
||||
Message(eMessage.eMessage_QUERY_set_color_lut);
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_exec(bool state)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_state_hook_exec);
|
||||
bwPipe.Write(state);
|
||||
comm->value = state ? 1u : 0u;
|
||||
Message(eMessage.eMessage_QUERY_state_hook_exec);
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_read(bool state)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_state_hook_read);
|
||||
bwPipe.Write(state);
|
||||
comm->value = state ? 1u : 0u;
|
||||
Message(eMessage.eMessage_QUERY_state_hook_read);
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_write(bool state)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_state_hook_write);
|
||||
bwPipe.Write(state);
|
||||
comm->value = state ? 1u : 0u;
|
||||
Message(eMessage.eMessage_QUERY_state_hook_write);
|
||||
}
|
||||
|
||||
public void QUERY_set_trace_callback(snes_trace_t callback)
|
||||
{
|
||||
this.traceCallback = callback;
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_enable_trace);
|
||||
bwPipe.Write(callback != null);
|
||||
bwPipe.Flush();
|
||||
//TODO
|
||||
//this.traceCallback = callback;
|
||||
//WritePipeMessage(eMessage.eMessage_QUERY_enable_trace);
|
||||
//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();
|
||||
//TODO
|
||||
//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();
|
||||
comm->value = (audio_sample!=null) ? 1u : 0u;
|
||||
Message(eMessage.eMessage_QUERY_enable_audio);
|
||||
}
|
||||
|
||||
public void QUERY_set_layer_enable(int layer, int priority, bool enable)
|
||||
public void QUERY_set_layer_enable()
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_set_layer_enable);
|
||||
bwPipe.Write(layer);
|
||||
bwPipe.Write(priority);
|
||||
bwPipe.Write(enable);
|
||||
bwPipe.Flush();
|
||||
Message(eMessage.eMessage_QUERY_set_layer_enable);
|
||||
}
|
||||
|
||||
public void QUERY_set_backdropColor(int backdropColor)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_set_backdropColor);
|
||||
bwPipe.Write(backdropColor);
|
||||
bwPipe.Flush();
|
||||
comm->value = (uint)backdropColor;
|
||||
Message(eMessage.eMessage_QUERY_set_backdropColor);
|
||||
}
|
||||
|
||||
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();
|
||||
comm->id = (uint)reg;
|
||||
Message(eMessage.eMessage_QUERY_peek_logical_register);
|
||||
return (int)comm->value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct CpuRegs
|
||||
public unsafe void QUERY_peek_cpu_regs(out CPURegs ret)
|
||||
{
|
||||
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);
|
||||
Message(eMessage.eMessage_QUERY_peek_cpu_regs);
|
||||
ret = comm->cpuregs;
|
||||
}
|
||||
|
||||
public void QUERY_set_cdl(ICodeDataLog cdl)
|
||||
{
|
||||
WritePipeMessage(eMessage.eMessage_QUERY_set_cdl);
|
||||
if (cdl == null)
|
||||
{
|
||||
for(int i=0;i<4*2;i++)
|
||||
WritePipePointer(IntPtr.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePipePointer(cdl.GetPin("CARTROM"),false);
|
||||
bwPipe.Write(cdl["CARTROM"].Length);
|
||||
//TODO
|
||||
//WritePipeMessage(eMessage.eMessage_QUERY_set_cdl);
|
||||
//if (cdl == null)
|
||||
//{
|
||||
// for(int i=0;i<4*2;i++)
|
||||
// WritePipePointer(IntPtr.Zero);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// WritePipePointer(cdl.GetPin("CARTROM"),false);
|
||||
// bwPipe.Write(cdl["CARTROM"].Length);
|
||||
|
||||
if (cdl.Has("CARTRAM"))
|
||||
{
|
||||
WritePipePointer(cdl.GetPin("CARTRAM"), false);
|
||||
bwPipe.Write(cdl["CARTRAM"].Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePipePointer(IntPtr.Zero);
|
||||
WritePipePointer(IntPtr.Zero);
|
||||
}
|
||||
// if (cdl.Has("CARTRAM"))
|
||||
// {
|
||||
// WritePipePointer(cdl.GetPin("CARTRAM"), false);
|
||||
// bwPipe.Write(cdl["CARTRAM"].Length);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// WritePipePointer(IntPtr.Zero);
|
||||
// WritePipePointer(IntPtr.Zero);
|
||||
// }
|
||||
|
||||
WritePipePointer(cdl.GetPin("WRAM"));
|
||||
bwPipe.Write(cdl["WRAM"].Length);
|
||||
// WritePipePointer(cdl.GetPin("WRAM"));
|
||||
// bwPipe.Write(cdl["WRAM"].Length);
|
||||
|
||||
WritePipePointer(cdl.GetPin("APURAM"), false);
|
||||
bwPipe.Write(cdl["APURAM"].Length);
|
||||
bwPipe.Flush();
|
||||
}
|
||||
// WritePipePointer(cdl.GetPin("APURAM"), false);
|
||||
// bwPipe.Write(cdl["APURAM"].Length);
|
||||
// bwPipe.Flush();
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,16 +12,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
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
|
||||
int width = (int)comm->width;
|
||||
int height = (int)comm->height;
|
||||
if (video_refresh != null)
|
||||
{
|
||||
video_refresh((int*)mmvaPtr, width, height);
|
||||
video_refresh((int*)comm->ptr, width, height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -29,35 +27,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
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;
|
||||
int port = comm->port;
|
||||
int device = comm->device;
|
||||
int index = comm->index;
|
||||
int id = (int)comm->id;
|
||||
if (input_state != null)
|
||||
ret = input_state(port, device, index, id);
|
||||
bwPipe.Write(ret);
|
||||
bwPipe.Flush();
|
||||
comm->value = input_state(port, device, index, id);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_notify:
|
||||
{
|
||||
int index = brPipe.ReadInt32();
|
||||
if (input_notify != null)
|
||||
input_notify(index);
|
||||
input_notify(comm->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
|
||||
uint nsamples = comm->size;
|
||||
|
||||
if (audio_sample != null)
|
||||
{
|
||||
ushort* audiobuffer = ((ushort*)mmvaPtr);
|
||||
for (int i = 0; i < nsamples; )
|
||||
ushort* audiobuffer = ((ushort*)comm->ptr);
|
||||
for (uint i = 0; i < nsamples; )
|
||||
{
|
||||
ushort left = audiobuffer[i++];
|
||||
ushort right = audiobuffer[i++];
|
||||
|
@ -65,32 +56,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
}
|
||||
}
|
||||
|
||||
bwPipe.Write(0); //dummy synchronization
|
||||
bwPipe.Flush();
|
||||
brPipe.ReadInt32(); //dummy synchronization
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_path_request:
|
||||
{
|
||||
int slot = brPipe.ReadInt32();
|
||||
string hint = ReadPipeString();
|
||||
int slot = comm->slot;
|
||||
string hint = comm->GetAscii();
|
||||
string ret = hint;
|
||||
if (pathRequest != null)
|
||||
hint = pathRequest(slot, hint);
|
||||
WritePipeString(hint);
|
||||
hint = pathRequest(slot, hint);
|
||||
SetAscii(hint);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_trace_callback:
|
||||
{
|
||||
var trace = ReadPipeString();
|
||||
if (traceCallback != null)
|
||||
traceCallback(trace);
|
||||
traceCallback(comm->GetAscii());
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_allocSharedMemory:
|
||||
{
|
||||
var name = ReadPipeString();
|
||||
var size = brPipe.ReadInt32();
|
||||
var name = comm->GetAscii();
|
||||
var size = comm->size;
|
||||
|
||||
if (SharedMemoryBlocks.ContainsKey(name))
|
||||
{
|
||||
|
@ -115,25 +102,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
smb = new SharedMemoryBlock();
|
||||
smb.Name = name;
|
||||
smb.Size = size;
|
||||
smb.Size = (int)size;
|
||||
smb.BlockName = InstanceName + smb.Name;
|
||||
smb.Allocate();
|
||||
}
|
||||
|
||||
comm->ptr = smb.Ptr;
|
||||
SharedMemoryBlocks[smb.Name] = smb;
|
||||
WritePipeString(smb.BlockName);
|
||||
CopyString(smb.BlockName);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_freeSharedMemory:
|
||||
{
|
||||
string name = ReadPipeString();
|
||||
var smb = SharedMemoryBlocks[name];
|
||||
DeallocatedMemoryBlocks[name] = smb;
|
||||
SharedMemoryBlocks.Remove(name);
|
||||
foreach (var block in SharedMemoryBlocks.Values)
|
||||
{
|
||||
if (block.Ptr == comm->ptr)
|
||||
{
|
||||
DeallocatedMemoryBlocks[block.Name] = block;
|
||||
SharedMemoryBlocks.Remove(block.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} //switch(msg)
|
||||
|
||||
|
||||
Message(eMessage.eMessage_ResumeAfterBRK);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,9 +107,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
rom_xml = null,
|
||||
rom_data = sgbRomData,
|
||||
rom_size = (uint)sgbRomData.Length,
|
||||
dmg_xml = null,
|
||||
dmg_data = romData,
|
||||
dmg_size = (uint)romData.Length
|
||||
};
|
||||
|
||||
if (!LoadCurrent())
|
||||
|
@ -143,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
throw new Exception("snes_load_cartridge_normal() failed");
|
||||
}
|
||||
|
||||
if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC)
|
||||
if (api.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;
|
||||
|
@ -245,7 +243,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
LibsnesApi.CpuRegs regs;
|
||||
LibsnesApi.CPURegs regs;
|
||||
api.QUERY_peek_cpu_regs(out regs);
|
||||
|
||||
bool fn = (regs.p & 0x80)!=0;
|
||||
|
@ -446,7 +444,6 @@ 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)
|
||||
{
|
||||
|
@ -454,7 +451,6 @@ 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)
|
||||
{
|
||||
|
@ -462,7 +458,6 @@ 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;
|
||||
|
@ -481,9 +476,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public string rom_xml;
|
||||
public byte[] rom_data;
|
||||
public uint rom_size;
|
||||
public string dmg_xml;
|
||||
public byte[] dmg_data;
|
||||
public uint dmg_size;
|
||||
}
|
||||
|
||||
LoadParams CurrLoadParams;
|
||||
|
@ -493,9 +486,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
bool result = false;
|
||||
if (CurrLoadParams.type == LoadParamType.Normal)
|
||||
result = api.CMD_load_cartridge_normal(CurrLoadParams.xml_data, CurrLoadParams.rom_data);
|
||||
else result = api.CMD_load_cartridge_super_game_boy(CurrLoadParams.rom_xml, CurrLoadParams.rom_data, CurrLoadParams.rom_size, CurrLoadParams.dmg_xml, CurrLoadParams.dmg_data, CurrLoadParams.dmg_size);
|
||||
else result = api.CMD_load_cartridge_super_game_boy(CurrLoadParams.rom_xml, CurrLoadParams.rom_data, CurrLoadParams.rom_size, CurrLoadParams.dmg_data);
|
||||
|
||||
mapper = api.QUERY_get_mapper();
|
||||
mapper = api.Mapper;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -639,11 +632,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
public void FrameAdvance(bool render, bool rendersound)
|
||||
{
|
||||
api.MessageCounter = 0;
|
||||
|
||||
if(Settings.UseRingBuffer)
|
||||
api.BeginBufferIO();
|
||||
|
||||
/* if the input poll callback is called, it will set this to false
|
||||
* this has to be done before we save the per-frame state in deterministic
|
||||
* mode, because in there, the core actually advances, and might advance
|
||||
|
@ -684,19 +672,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
bool powerSignal = Controller.IsPressed("Power");
|
||||
if (powerSignal) api.CMD_power();
|
||||
|
||||
//too many messages
|
||||
api.QUERY_set_layer_enable(0, 0, Settings.ShowBG1_0);
|
||||
api.QUERY_set_layer_enable(0, 1, Settings.ShowBG1_1);
|
||||
api.QUERY_set_layer_enable(1, 0, Settings.ShowBG2_0);
|
||||
api.QUERY_set_layer_enable(1, 1, Settings.ShowBG2_1);
|
||||
api.QUERY_set_layer_enable(2, 0, Settings.ShowBG3_0);
|
||||
api.QUERY_set_layer_enable(2, 1, Settings.ShowBG3_1);
|
||||
api.QUERY_set_layer_enable(3, 0, Settings.ShowBG4_0);
|
||||
api.QUERY_set_layer_enable(3, 1, Settings.ShowBG4_1);
|
||||
api.QUERY_set_layer_enable(4, 0, Settings.ShowOBJ_0);
|
||||
api.QUERY_set_layer_enable(4, 1, Settings.ShowOBJ_1);
|
||||
api.QUERY_set_layer_enable(4, 2, Settings.ShowOBJ_2);
|
||||
api.QUERY_set_layer_enable(4, 3, Settings.ShowOBJ_3);
|
||||
var enables = new LibsnesApi.LayerEnables();
|
||||
enables.BG1_Prio0 = Settings.ShowBG1_0;
|
||||
enables.BG1_Prio1 = Settings.ShowBG1_1;
|
||||
enables.BG2_Prio0 = Settings.ShowBG2_0;
|
||||
enables.BG2_Prio1 = Settings.ShowBG2_1;
|
||||
enables.BG3_Prio0 = Settings.ShowBG3_0;
|
||||
enables.BG3_Prio1 = Settings.ShowBG3_1;
|
||||
enables.BG4_Prio0 = Settings.ShowBG4_0;
|
||||
enables.BG4_Prio1 = Settings.ShowBG4_1;
|
||||
enables.Obj_Prio0 = Settings.ShowOBJ_0;
|
||||
enables.Obj_Prio1 = Settings.ShowOBJ_1;
|
||||
enables.Obj_Prio2 = Settings.ShowOBJ_2;
|
||||
enables.Obj_Prio3 = Settings.ShowOBJ_3;
|
||||
api.SetLayerEnables(ref enables);
|
||||
|
||||
RefreshMemoryCallbacks(false);
|
||||
|
||||
|
@ -704,16 +693,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
timeFrameCounter++;
|
||||
api.CMD_run();
|
||||
|
||||
while (api.QUERY_HasMessage)
|
||||
Console.WriteLine(api.QUERY_DequeueMessage());
|
||||
//once upon a time we forwarded messages frmo bsnes here, by checking for queued text messages, but I don't think it's needed any longer
|
||||
|
||||
if (IsLagFrame)
|
||||
LagCount++;
|
||||
|
||||
//diagnostics for IPC traffic
|
||||
//Console.WriteLine(api.MessageCounter);
|
||||
|
||||
api.EndBufferIO();
|
||||
}
|
||||
|
||||
void RefreshMemoryCallbacks(bool suppress)
|
||||
|
@ -728,7 +711,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
get
|
||||
{
|
||||
if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC)
|
||||
if (api.Region == LibsnesApi.SNES_REGION.NTSC)
|
||||
return DisplayType.NTSC;
|
||||
else
|
||||
return DisplayType.PAL;
|
||||
|
@ -1351,7 +1334,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public bool ShowOBJ_2 = true;
|
||||
public bool ShowOBJ_3 = true;
|
||||
|
||||
public bool UseRingBuffer = true;
|
||||
public bool AlwaysDoubleSize = false;
|
||||
public bool ForceDeterminism = true;
|
||||
public string Palette = "BizHawk";
|
||||
|
|
|
@ -26,30 +26,7 @@ yabause: Load yabause/src/libyabause/libyabause.sln; builds as VC++ (VC10). Out
|
|||
|
||||
libdarm: stock build from https://github.com/jbremer/darm.git 71a027e0ffb2171d01213c6ecc6da1c49e158398
|
||||
|
||||
===========================================
|
||||
|
||||
Here's a guide zeromus developed on 03-feb-2015 to assembly a mingw-w64 + msys capable of building bsnes and maybe other things. The motivation: mingw make stopped working at some point (maybe a windows update) and the compiler is old and has problems.
|
||||
|
||||
Here's my reasoning for this approach:
|
||||
1. People tell me mingw-w64 is superior
|
||||
2. Mingw+msys or whatever will only install mingw
|
||||
3. mingw-w64 doesnt come with msys
|
||||
4. Therefore, we must 'upgrade' a mingw-w64 with msys.
|
||||
|
||||
Here's how:
|
||||
|
||||
install an mingw-w64 toolchain. lets say its at C:\i686-4.9.2-posix-dwarf-rt_v3-rev1
|
||||
download mingw-get-setup.exe to C:\i686-4.9.2-posix-dwarf-rt_v3-rev1\mingw-get-setup.exe.
|
||||
Run it and install to C:\i686-4.9.2-posix-dwarf-rt_v3-rev1. Turn off the shortcuts, but leave on the GUI options. Install it and pick continue.
|
||||
Enable msys-base and pick installation > update catalogue. review changes and apply. then we'll be done. close, close, close.
|
||||
You now have C:\i686-4.9.2-posix-dwarf-rt_v3-rev1\msys\1.0\msys.bat
|
||||
run it, and run /postinstall/pi.sh
|
||||
enter y,y and then C:/i686-4.9.2-posix-dwarf-rt_v3-rev1/mingw32
|
||||
answer Y (is this step MISSING?)
|
||||
Now you've activated the mingw-w64 on this msys install.
|
||||
It will tell you "Oh joy" you dont have a better make.exe. Thats bullshit, their make.exe is terrible. In fact youll want a new one
|
||||
ln -s /mingw/bin/mingw32-make.exe /mingw/bin/make
|
||||
Now, with the way msys sets up its paths, youll be using that make by default
|
||||
libsnes: use libsnes/vs2015/libsnes.sln in vs2015
|
||||
|
||||
=========================================
|
||||
|
||||
|
|
|
@ -22,18 +22,13 @@ extern "C" {
|
|||
typedef void* cothread_t;
|
||||
typedef void (*coentry_t)(void);
|
||||
|
||||
#if defined(LIBCO_IMPORT)
|
||||
#define LIBCO_IMPORTDECL __declspec(dllimport)
|
||||
#elif defined(LIBCO_EXPORT)
|
||||
#define LIBCO_IMPORTDECL __declspec(dllexport)
|
||||
#else
|
||||
#define LIBCO_IMPORTDECL
|
||||
#endif
|
||||
|
||||
LIBCO_IMPORTDECL cothread_t co_active();
|
||||
LIBCO_IMPORTDECL cothread_t co_create(unsigned int, coentry_t);
|
||||
LIBCO_IMPORTDECL void co_delete(cothread_t);
|
||||
LIBCO_IMPORTDECL void co_switch(cothread_t);
|
||||
void* co_getstack(cothread_t);
|
||||
cothread_t co_active();
|
||||
cothread_t co_create_withstack(void* stack, int stacksize, coentry_t);
|
||||
cothread_t co_create(unsigned int, coentry_t);
|
||||
void co_delete(cothread_t);
|
||||
void co_switch(cothread_t);
|
||||
cothread_t co_primary();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
original author: Nach
|
||||
license: public domain
|
||||
|
||||
additional work: zeromus
|
||||
note: more ARM compilers are supported here (check the ifdefs in _JUMP_BUFFER)
|
||||
and: work has been done to make this coexist more peaceably with .net
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -14,17 +27,107 @@ typedef struct
|
|||
void *stack;
|
||||
unsigned long seh_frame, stack_top, stack_bottom;
|
||||
cothread_t caller;
|
||||
int ownstack;
|
||||
} cothread_struct;
|
||||
|
||||
static thread_local cothread_struct co_primary;
|
||||
static thread_local cothread_struct _co_primary;
|
||||
static thread_local cothread_struct *co_running = 0;
|
||||
|
||||
//-------------------
|
||||
#if defined(_MSC_VER) || defined(MINGW32)
|
||||
cothread_t co_primary() { return (cothread_t)&_co_primary; }
|
||||
|
||||
__declspec(dllimport) cothread_t os_co_create();
|
||||
__declspec(dllimport) void os_pre_setjmp(cothread_t target);
|
||||
__declspec(dllimport) void os_pre_longjmp(cothread_struct* rec);
|
||||
|
||||
//-------------------
|
||||
#ifdef _MSC_VER
|
||||
|
||||
//links of interest
|
||||
//http://connect.microsoft.com/VisualStudio/feedback/details/100319/really-wierd-behaviour-in-crt-io-coupled-with-some-inline-assembly
|
||||
//http://en.wikipedia.org/wiki/Thread_Information_Block
|
||||
//http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/72093e46-4524-4f54-9f36-c7e8a309d1db/ //FS warning
|
||||
|
||||
|
||||
#define WINVER 0x0400
|
||||
#define _WIN32_WINNT 0x0400
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning(disable:4733)
|
||||
#pragma warning(disable:4311)
|
||||
|
||||
static void capture_fs(cothread_struct* rec)
|
||||
{
|
||||
int temp;
|
||||
__asm mov eax, dword ptr fs:[0];
|
||||
__asm mov temp, eax;
|
||||
rec->seh_frame = temp;
|
||||
__asm mov eax, dword ptr fs:[4];
|
||||
__asm mov temp, eax;
|
||||
rec->stack_top = temp;
|
||||
__asm mov eax, dword ptr fs:[8];
|
||||
__asm mov temp, eax;
|
||||
rec->stack_bottom = temp;
|
||||
}
|
||||
|
||||
static void restore_fs(cothread_struct* rec)
|
||||
{
|
||||
int temp;
|
||||
temp = rec->seh_frame;
|
||||
__asm mov eax, temp;
|
||||
__asm mov dword ptr fs:[0], eax
|
||||
temp = rec->stack_top;
|
||||
__asm mov eax, temp;
|
||||
__asm mov dword ptr fs:[4], eax
|
||||
temp = rec->stack_bottom;
|
||||
__asm mov eax, temp;
|
||||
__asm mov dword ptr fs:[8], eax
|
||||
}
|
||||
|
||||
static void os_co_wrapper()
|
||||
{
|
||||
cothread_struct* rec = (cothread_struct*)co_active();
|
||||
__try
|
||||
{
|
||||
rec->coentry();
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
//unhandled win32 exception in coroutine.
|
||||
//this coroutine will now be suspended permanently and control will be yielded to caller, for lack of anything better to do.
|
||||
//perhaps the process should just terminate.
|
||||
for(;;)
|
||||
{
|
||||
//dead coroutine
|
||||
co_switch(rec->caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void os_co_create(cothread_struct* rec, unsigned int size, coentry_t coentry)
|
||||
{
|
||||
_JUMP_BUFFER* jb = (_JUMP_BUFFER*)&rec->context;
|
||||
cothread_struct temp;
|
||||
|
||||
jb->Esp = (unsigned long)rec->stack + size - 4;
|
||||
jb->Eip = (unsigned long)os_co_wrapper;
|
||||
|
||||
rec->stack_top = jb->Esp + 4;
|
||||
rec->stack_bottom = (unsigned long)rec->stack;
|
||||
|
||||
//wild assumption about SEH frame.. seems to work
|
||||
capture_fs(&temp);
|
||||
rec->seh_frame = temp.seh_frame;
|
||||
}
|
||||
|
||||
static void os_pre_setjmp(cothread_t target)
|
||||
{
|
||||
cothread_struct* rec = (cothread_struct*)target;
|
||||
capture_fs(co_running);
|
||||
rec->caller = co_running;
|
||||
}
|
||||
|
||||
static void os_pre_longjmp(cothread_struct* rec)
|
||||
{
|
||||
restore_fs(rec);
|
||||
}
|
||||
|
||||
#elif defined(__ARM_EABI__) || defined(__ARMCC_VERSION)
|
||||
|
||||
|
@ -68,25 +171,40 @@ static void os_pre_longjmp(cothread_struct* rec)
|
|||
|
||||
cothread_t co_active()
|
||||
{
|
||||
if(!co_running) co_running = &co_primary;
|
||||
if(!co_running) co_running = &_co_primary;
|
||||
return (cothread_t)co_running;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*coentry)(void))
|
||||
void* co_getstack(cothread_t cothread)
|
||||
{
|
||||
return ((cothread_struct*)cothread)->stack;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int stacksize, coentry_t coentry)
|
||||
{
|
||||
cothread_struct* ret = (cothread_struct*)co_create_withstack(malloc(stacksize), stacksize, coentry);
|
||||
if(ret)
|
||||
ret->ownstack = 1;
|
||||
return (cothread_t)ret;
|
||||
}
|
||||
|
||||
cothread_t co_create_withstack(void* stack, int stacksize, coentry_t coentry)
|
||||
{
|
||||
cothread_struct *thread;
|
||||
|
||||
if(!co_running) co_running = &co_primary;
|
||||
if(!co_running) co_running = &_co_primary;
|
||||
|
||||
thread = (cothread_struct*)malloc(sizeof(cothread_struct));
|
||||
if(thread)
|
||||
{
|
||||
thread->coentry = coentry;
|
||||
thread->stack = malloc(size);
|
||||
thread->stack = stack;
|
||||
|
||||
{
|
||||
setjmp(thread->context);
|
||||
os_co_create(thread,size,coentry);
|
||||
os_co_create(thread,stacksize,coentry);
|
||||
}
|
||||
thread->ownstack = 0;
|
||||
}
|
||||
|
||||
return (cothread_t)thread;
|
||||
|
@ -96,10 +214,9 @@ void co_delete(cothread_t cothread)
|
|||
{
|
||||
if(cothread)
|
||||
{
|
||||
if(((cothread_struct*)cothread)->stack)
|
||||
{
|
||||
free(((cothread_struct*)cothread)->stack);
|
||||
}
|
||||
cothread_struct* thread = (cothread_struct*)cothread;
|
||||
if (thread->ownstack)
|
||||
free(thread->stack);
|
||||
free(cothread);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,93 +1,117 @@
|
|||
/*
|
||||
libco.x86 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define fastcall __fastcall
|
||||
#elif defined(__GNUC__)
|
||||
#define fastcall __attribute__((fastcall))
|
||||
#else
|
||||
#error "libco: please define fastcall macro"
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
//ABI: fastcall
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A,
|
||||
0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
void co_init() {
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init() {
|
||||
unsigned long addr = (unsigned long)co_swap_function;
|
||||
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
if(!co_swap) {
|
||||
co_init();
|
||||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 256; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle) {
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
libco.x86 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define fastcall __fastcall
|
||||
#elif defined(__GNUC__)
|
||||
#define fastcall __attribute__((fastcall))
|
||||
#else
|
||||
#error "libco: please define fastcall macro"
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
//ABI: fastcall
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x89, 0x22, /* mov [edx],esp */
|
||||
0x8b, 0x21, /* mov esp,[ecx] */
|
||||
0x58, /* pop eax */
|
||||
0x89, 0x6a, 0x04, /* mov [edx+0x04],ebp */
|
||||
0x89, 0x72, 0x08, /* mov [edx+0x08],esi */
|
||||
0x89, 0x7a, 0x0c, /* mov [edx+0x0c],edi */
|
||||
0x89, 0x5a, 0x10, /* mov [edx+0x10],ebx */
|
||||
0x8b, 0x69, 0x04, /* mov ebp,[ecx+0x04] */
|
||||
0x8b, 0x71, 0x08, /* mov esi,[ecx+0x08] */
|
||||
0x8b, 0x79, 0x0c, /* mov edi,[ecx+0x0c] */
|
||||
0x8b, 0x59, 0x10, /* mov ebx,[ecx+0x10] */
|
||||
0xff, 0xe0, /* jmp eax */
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
void co_init(void)
|
||||
{
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function,
|
||||
sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init(void)
|
||||
{
|
||||
unsigned long addr = (unsigned long)co_swap_function;
|
||||
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash(void)
|
||||
{
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active(void)
|
||||
{
|
||||
if(!co_active_handle)
|
||||
co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void))
|
||||
{
|
||||
cothread_t handle;
|
||||
if(!co_swap)
|
||||
{
|
||||
co_init();
|
||||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
|
||||
if(!co_active_handle)
|
||||
co_active_handle = &co_active_buffer;
|
||||
|
||||
size += 256; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if((handle = (cothread_t)malloc(size)))
|
||||
{
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle)
|
||||
{
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle)
|
||||
{
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -327,7 +327,7 @@
|
|||
<ClCompile Include="..\bsnes\gameboy\scheduler\scheduler.cpp" />
|
||||
<ClCompile Include="..\bsnes\gameboy\system\system.cpp" />
|
||||
<ClCompile Include="..\bsnes\gameboy\video\video.cpp" />
|
||||
<ClCompile Include="..\bsnes\libco\x86.c" />
|
||||
<ClCompile Include="..\bsnes\libco\sjlj-multi.c" />
|
||||
<ClCompile Include="..\bsnes\snes\alt\cpu\cpu.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Compatibility|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Compatibility|Win32'">true</ExcludedFromBuild>
|
||||
|
|
|
@ -566,7 +566,7 @@
|
|||
<ClCompile Include="..\bsnes\snes\dsp\counter.cpp">
|
||||
<Filter>snes\dsp</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\bsnes\libco\x86.c">
|
||||
<ClCompile Include="..\bsnes\libco\sjlj-multi.c">
|
||||
<Filter>libco</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue