snes hooking system and GetCpuFlagsAndRegisters now work normally

This commit is contained in:
zeromus 2013-11-22 09:33:56 +00:00
parent 7a1f28b5a9
commit 4c247672fe
22 changed files with 2186 additions and 1459 deletions

View File

@ -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" />

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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)));
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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

Binary file not shown.