snes hooking system and GetCpuFlagsAndRegisters now work normally
This commit is contained in:
parent
7a1f28b5a9
commit
4c247672fe
|
@ -50,11 +50,14 @@
|
|||
<Compile Include="Buffer.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="HawkFile.cs" />
|
||||
<Compile Include="IPC\IPCRingBuffer.cs" />
|
||||
<Compile Include="IPC\SharedMemoryBlock.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="MruStack.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\svnrev.cs" />
|
||||
<Compile Include="QuickCollections.cs" />
|
||||
<Compile Include="SwitcherStream.cs" />
|
||||
<Compile Include="Types.cs" />
|
||||
<Compile Include="UndoHistory.cs" />
|
||||
<Compile Include="Util.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
|
||||
{
|
||||
/// <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 and degrade entirely if there is less than one processor.
|
||||
/// </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.
|
||||
}
|
||||
}
|
||||
|
||||
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<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
|
||||
{
|
||||
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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<DebugEvent, Bag<uint, Reference>> database = new WorkingDictionary<DebugEvent, Bag<uint, Reference>>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -389,6 +389,11 @@
|
|||
<Compile Include="Consoles\Nintendo\NES\PPU.run.cs" />
|
||||
<Compile Include="Consoles\Nintendo\NES\Unif.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi_BRK.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi_CMD.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi_Enums.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi_QUERY.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi_SIG.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\SnesColors.cs" />
|
||||
<Compile Include="Consoles\Nintendo\SNES\SNESGraphicsDecoder.cs" />
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<KeyValuePair<string, int>> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
var vals = new List<KeyValuePair<string, int>>();
|
||||
foreach (var blah in Enum.GetValues(typeof(LibsnesApi.SNES_REG)).Cast<LibsnesApi.SNES_REG>())
|
||||
var ret = new List<KeyValuePair<string, int>>();
|
||||
|
||||
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<KeyValuePair<string, int>>
|
||||
{
|
||||
vals.Add(new KeyValuePair<string, int>(blah.ToString(), api.snes_peek_logical_register(blah)));
|
||||
}
|
||||
return vals;
|
||||
new KeyValuePair<string, int>("PC", (int)regs.pc),
|
||||
new KeyValuePair<string, int>("A", (int)regs.a),
|
||||
new KeyValuePair<string, int>("X", (int)regs.x),
|
||||
new KeyValuePair<string, int>("Y", (int)regs.y),
|
||||
new KeyValuePair<string, int>("Z", (int)regs.z),
|
||||
new KeyValuePair<string, int>("S", (int)regs.s),
|
||||
new KeyValuePair<string, int>("D", (int)regs.d),
|
||||
new KeyValuePair<string, int>("Vector", (int)regs.vector),
|
||||
new KeyValuePair<string, int>("P", (int)regs.p),
|
||||
new KeyValuePair<string, int>("AA", (int)regs.aa),
|
||||
new KeyValuePair<string, int>("RD", (int)regs.rd),
|
||||
new KeyValuePair<string, int>("SP", (int)regs.sp),
|
||||
new KeyValuePair<string, int>("DP", (int)regs.dp),
|
||||
new KeyValuePair<string, int>("DB", (int)regs.db),
|
||||
new KeyValuePair<string, int>("MDR", (int)regs.mdr),
|
||||
new KeyValuePair<string, int>("Flag N", fn?1:0),
|
||||
new KeyValuePair<string, int>("Flag V", fv?1:0),
|
||||
new KeyValuePair<string, int>("Flag M", fm?1:0),
|
||||
new KeyValuePair<string, int>("Flag X", fx?1:0),
|
||||
new KeyValuePair<string, int>("Flag D", fd?1:0),
|
||||
new KeyValuePair<string, int>("Flag I", fi?1:0),
|
||||
new KeyValuePair<string, int>("Flag Z", fz?1:0),
|
||||
new KeyValuePair<string, int>("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
|
|||
/// </summary>
|
||||
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);
|
||||
}
|
||||
/// <summary>
|
||||
/// handle the unmanaged part of savestating
|
||||
/// </summary>
|
||||
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)));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,16 @@ private:
|
|||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
} status;
|
||||
|
||||
public:
|
||||
struct Debugger {
|
||||
hook<void (uint24)> op_exec;
|
||||
hook<void (uint24)> op_read;
|
||||
hook<void (uint24, uint8)> op_write;
|
||||
hook<void ()> op_nmi;
|
||||
hook<void ()> op_irq;
|
||||
} debugger;
|
||||
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -3,6 +3,8 @@ struct Scheduler : property<Scheduler> {
|
|||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
|
||||
readonly<ExitReason> 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)
|
||||
|
||||
|
|
|
@ -7,11 +7,12 @@ struct Video {
|
|||
Video();
|
||||
~Video();
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
bool hires;
|
||||
unsigned line_width[240];
|
||||
|
||||
void update();
|
||||
void scanline();
|
||||
void init();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
BIN
libsnes/upx.exe
BIN
libsnes/upx.exe
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue