snes: waterboxification phase 2
This commit is contained in:
parent
9975a05695
commit
36be9e9fc7
|
@ -659,9 +659,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
SNESGraphicsDecoder NewDecoder()
|
||||
{
|
||||
//wtf to do? now we need an api all the time
|
||||
throw new NotImplementedException("TODO");
|
||||
/*
|
||||
if (currentSnesCore != null)
|
||||
return new SNESGraphicsDecoder(currentSnesCore.Api, currentSnesCore.CurrPalette);
|
||||
else return new SNESGraphicsDecoder(currentSnesCore.Api, SnesColors.ColorType.BizHawk);
|
||||
else return new SNESGraphicsDecoder(currentSnesCore.Api, SnesColors.ColorType.BizHawk);*/
|
||||
}
|
||||
|
||||
void RenderPalette()
|
||||
|
|
|
@ -10,28 +10,44 @@ using BizHawk.Common.BizInvoke;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public unsafe partial class LibsnesApi : IDisposable
|
||||
public abstract unsafe class CoreImpl
|
||||
{
|
||||
/*[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
||||
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);*/
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract IntPtr DllInit();
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void Message(LibsnesApi.eMessage msg);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void CopyBuffer(int id, void* ptr, int size);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void SetBuffer(int id, void* ptr, int size);
|
||||
}
|
||||
|
||||
public unsafe partial class LibsnesApi : IDisposable, IMonitor
|
||||
{
|
||||
static LibsnesApi()
|
||||
{
|
||||
if (sizeof(CommStruct) != 232)
|
||||
{
|
||||
throw new InvalidOperationException("sizeof(comm)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private PeRunner _exe;
|
||||
private CoreImpl _core;
|
||||
private bool _disposed;
|
||||
private CommStruct* _comm;
|
||||
private readonly Dictionary<string, IntPtr> _sharedMemoryBlocks = new Dictionary<string, IntPtr>();
|
||||
|
||||
private abstract class CoreImpl
|
||||
public void Enter()
|
||||
{
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract IntPtr DllInit();
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void Message(eMessage msg);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void CopyBuffer(int id, void* ptr, int size);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void SetBuffer(int id, void* ptr, int size);
|
||||
_exe.Enter();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_exe.Exit();
|
||||
}
|
||||
|
||||
public LibsnesApi(string dllPath)
|
||||
{
|
||||
|
@ -41,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
Path = dllPath,
|
||||
SbrkHeapSizeKB = 32 * 1024,
|
||||
InvisibleHeapSizeKB = 32 * 1024,
|
||||
MmapHeapSizeKB = 32 * 1024,
|
||||
MmapHeapSizeKB = 256 * 1024,
|
||||
PlainHeapSizeKB = 32 * 1024,
|
||||
SealedHeapSizeKB = 32 * 1024
|
||||
});
|
||||
|
@ -54,6 +70,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
_exe.Dispose();
|
||||
_exe = null;
|
||||
_core = null;
|
||||
|
@ -137,9 +154,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
BRR = 0x80,
|
||||
};
|
||||
|
||||
//Dictionary<string, SharedMemoryBlock> SharedMemoryBlocks = new Dictionary<string, SharedMemoryBlock>();
|
||||
//Dictionary<string, SharedMemoryBlock> DeallocatedMemoryBlocks = new Dictionary<string, SharedMemoryBlock>();
|
||||
|
||||
snes_video_refresh_t video_refresh;
|
||||
snes_input_poll_t input_poll;
|
||||
snes_input_state_t input_state;
|
||||
|
@ -165,15 +179,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public delegate void snes_trace_t(uint which, string msg);
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct CPURegs
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public uint pc;
|
||||
[FieldOffset(4)]
|
||||
public ushort a, x, y, z, s, d, vector; //7x
|
||||
[FieldOffset(18)]
|
||||
public byte p, nothing;
|
||||
[FieldOffset(20)]
|
||||
public uint aa, rd;
|
||||
[FieldOffset(28)]
|
||||
public byte sp, dp, db, mdr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LayerEnables
|
||||
{
|
||||
byte _BG1_Prio0, _BG1_Prio1;
|
||||
|
@ -197,43 +218,63 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public bool Obj_Prio3 { get { return _Obj_Prio3 != 0; } set { _Obj_Prio3 = (byte)(value ? 1 : 0); } }
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
struct CommStruct
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
//the cmd being executed
|
||||
public eMessage cmd;
|
||||
|
||||
[FieldOffset(4)]
|
||||
//the status of the core
|
||||
public eStatus status;
|
||||
|
||||
[FieldOffset(8)]
|
||||
//the SIG or BRK that the core is halted in
|
||||
public eMessage reason;
|
||||
|
||||
//flexible in/out parameters
|
||||
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
|
||||
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
|
||||
[FieldOffset(16)]
|
||||
public sbyte* str;
|
||||
[FieldOffset(24)]
|
||||
public void* ptr;
|
||||
[FieldOffset(32)]
|
||||
public uint id, addr, value, size;
|
||||
[FieldOffset(48)]
|
||||
public int port, device, index, slot;
|
||||
[FieldOffset(64)]
|
||||
public int width, height;
|
||||
[FieldOffset(72)]
|
||||
public int scanline;
|
||||
[FieldOffset(76)]
|
||||
public fixed int inports[2];
|
||||
|
||||
[FieldOffset(88)]
|
||||
//this should always be used in pairs
|
||||
public fixed uint buf[3]; //ACTUALLY A POINTER but can't marshal it :(
|
||||
public fixed long buf[3]; //ACTUALLY A POINTER but can't marshal it :(
|
||||
[FieldOffset(112)]
|
||||
public fixed int buf_size[3];
|
||||
|
||||
[FieldOffset(128)]
|
||||
//bleck. this is a long so that it can be a 32/64bit pointer
|
||||
public fixed long cdl_ptr[4];
|
||||
[FieldOffset(160)]
|
||||
public fixed int cdl_size[4];
|
||||
|
||||
[FieldOffset(176)]
|
||||
public CPURegs cpuregs;
|
||||
[FieldOffset(208)]
|
||||
public LayerEnables layerEnables;
|
||||
|
||||
[FieldOffset(220)]
|
||||
//static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command
|
||||
public SNES_REGION region;
|
||||
[FieldOffset(224)]
|
||||
public SNES_MAPPER mapper;
|
||||
|
||||
[FieldOffset(228)]
|
||||
public int unused;
|
||||
|
||||
//utilities
|
||||
//TODO: make internal, wrap on the API instead of the comm
|
||||
public unsafe string GetAscii() { return _getAscii(str); }
|
||||
|
|
|
@ -76,16 +76,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
return _comm->GetBool();
|
||||
}
|
||||
}
|
||||
|
||||
public void CMD_term()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_term);
|
||||
WaitForCMD();
|
||||
}
|
||||
public void CMD_unload_cartridge()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_unload_cartridge);
|
||||
WaitForCMD();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,9 +31,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public byte* QUERY_get_memory_data(SNES_MEMORY id)
|
||||
{
|
||||
string name = QUERY_MemoryNameForId(id);
|
||||
if (!SharedMemoryBlocks.ContainsKey(name)) return null;
|
||||
var smb = SharedMemoryBlocks[name];
|
||||
return (byte*)smb.Ptr;
|
||||
IntPtr ret;
|
||||
_sharedMemoryBlocks.TryGetValue(name, out ret);
|
||||
return (byte*)ret;
|
||||
}
|
||||
|
||||
public byte QUERY_peek(SNES_MEMORY id, uint addr)
|
||||
|
|
|
@ -8,127 +8,89 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
bool Handle_SIG(eMessage msg)
|
||||
{
|
||||
switch (msg)
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
switch (msg)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
|
||||
case eMessage.eMessage_SIG_video_refresh:
|
||||
{
|
||||
int width = comm->width;
|
||||
int height = comm->height;
|
||||
if (video_refresh != null)
|
||||
case eMessage.eMessage_SIG_video_refresh:
|
||||
{
|
||||
video_refresh((int*)comm->ptr, width, height);
|
||||
int width = _comm->width;
|
||||
int height = _comm->height;
|
||||
video_refresh?.Invoke((int*)_comm->ptr, width, height);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_poll:
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_poll:
|
||||
break;
|
||||
case eMessage.eMessage_SIG_input_state:
|
||||
{
|
||||
int port = comm->port;
|
||||
int device = comm->device;
|
||||
int index = comm->index;
|
||||
int id = (int)comm->id;
|
||||
if (input_state != null)
|
||||
comm->value = (uint)input_state(port, device, index, id);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_notify:
|
||||
{
|
||||
if (input_notify != null)
|
||||
input_notify(comm->index);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_audio_flush:
|
||||
{
|
||||
uint nsamples = comm->size;
|
||||
|
||||
if (audio_sample != null)
|
||||
case eMessage.eMessage_SIG_input_state:
|
||||
{
|
||||
ushort* audiobuffer = ((ushort*)comm->ptr);
|
||||
for (uint i = 0; i < nsamples; )
|
||||
int port = _comm->port;
|
||||
int device = _comm->device;
|
||||
int index = _comm->index;
|
||||
int id = (int)_comm->id;
|
||||
if (input_state != null)
|
||||
_comm->value = (uint)input_state(port, device, index, id);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_notify:
|
||||
{
|
||||
input_notify?.Invoke(_comm->index);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_audio_flush:
|
||||
{
|
||||
uint nsamples = _comm->size;
|
||||
|
||||
if (audio_sample != null)
|
||||
{
|
||||
ushort left = audiobuffer[i++];
|
||||
ushort right = audiobuffer[i++];
|
||||
audio_sample(left, right);
|
||||
ushort* audiobuffer = ((ushort*)_comm->ptr);
|
||||
for (uint i = 0; i < nsamples;)
|
||||
{
|
||||
ushort left = audiobuffer[i++];
|
||||
ushort right = audiobuffer[i++];
|
||||
audio_sample(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_path_request:
|
||||
{
|
||||
int slot = comm->slot;
|
||||
string hint = comm->GetAscii();
|
||||
string ret = hint;
|
||||
if (pathRequest != null)
|
||||
hint = pathRequest(slot, hint);
|
||||
CopyAscii(0, hint);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_trace_callback:
|
||||
{
|
||||
if (traceCallback != null)
|
||||
traceCallback(comm->value, comm->GetAscii());
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_allocSharedMemory:
|
||||
{
|
||||
var name = comm->GetAscii();
|
||||
var size = comm->size;
|
||||
|
||||
if (SharedMemoryBlocks.ContainsKey(name))
|
||||
case eMessage.eMessage_SIG_path_request:
|
||||
{
|
||||
throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name);
|
||||
int slot = _comm->slot;
|
||||
string hint = _comm->GetAscii();
|
||||
string ret = hint;
|
||||
if (pathRequest != null)
|
||||
hint = pathRequest(slot, hint);
|
||||
CopyAscii(0, hint);
|
||||
break;
|
||||
}
|
||||
|
||||
//try reusing existing block; dispose it if it exists and if the size doesnt match
|
||||
SharedMemoryBlock smb = null;
|
||||
if (DeallocatedMemoryBlocks.ContainsKey(name))
|
||||
case eMessage.eMessage_SIG_trace_callback:
|
||||
{
|
||||
smb = DeallocatedMemoryBlocks[name];
|
||||
DeallocatedMemoryBlocks.Remove(name);
|
||||
if (smb.Size != size)
|
||||
{
|
||||
smb.Dispose();
|
||||
smb = null;
|
||||
}
|
||||
traceCallback?.Invoke(_comm->value, _comm->GetAscii());
|
||||
break;
|
||||
}
|
||||
|
||||
//allocate a new block if we have to
|
||||
if (smb == null)
|
||||
case eMessage.eMessage_SIG_allocSharedMemory:
|
||||
{
|
||||
smb = new SharedMemoryBlock();
|
||||
smb.Name = name;
|
||||
smb.Size = (int)size;
|
||||
smb.BlockName = InstanceName + smb.Name;
|
||||
smb.Allocate();
|
||||
}
|
||||
// NB: shared memory blocks are allocated on the unmanaged side
|
||||
var name = _comm->GetAscii();
|
||||
var size = _comm->size;
|
||||
var ptr = _comm->ptr;
|
||||
|
||||
comm->ptr = smb.Ptr;
|
||||
SharedMemoryBlocks[smb.Name] = smb;
|
||||
CopyAscii(0, smb.BlockName);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_freeSharedMemory:
|
||||
{
|
||||
foreach (var block in SharedMemoryBlocks.Values)
|
||||
{
|
||||
if (block.Ptr == comm->ptr)
|
||||
{
|
||||
DeallocatedMemoryBlocks[block.Name] = block;
|
||||
SharedMemoryBlocks.Remove(block.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} //switch(msg)
|
||||
if (_sharedMemoryBlocks.ContainsKey(name))
|
||||
throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name);
|
||||
|
||||
Message(eMessage.eMessage_Resume);
|
||||
return true;
|
||||
_sharedMemoryBlocks.Add(name, (IntPtr)ptr);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_freeSharedMemory:
|
||||
throw new InvalidOperationException("Unexpected call: SIG_freeSharedMemory");
|
||||
} //switch(msg)
|
||||
|
||||
_core.Message(eMessage.eMessage_Resume);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
IsLagFrame = true;
|
||||
|
||||
if (!_nocallbacks && _tracer.Enabled)
|
||||
if (_tracer.Enabled)
|
||||
{
|
||||
//Api.QUERY_set_trace_callback(1<<(int)LibsnesApi.eTRACE.SMP, _tracecb); //TEST -- it works but theres no way to control it from the frontend now
|
||||
|
||||
|
@ -36,21 +36,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
Api.QUERY_set_trace_callback(0,null);
|
||||
}
|
||||
|
||||
// for deterministic emulation, save the state we're going to use before frame advance
|
||||
// don't do this during nocallbacks though, since it's already been done
|
||||
if (!_nocallbacks && DeterministicEmulation)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
bw.Write(CoreSaveState());
|
||||
bw.Write(false); // not framezero
|
||||
var ssc = new SaveController();
|
||||
ssc.CopyFrom(controller);
|
||||
ssc.Serialize(bw);
|
||||
bw.Close();
|
||||
_savestatebuff = ms.ToArray();
|
||||
}
|
||||
|
||||
// speedup when sound rendering is not needed
|
||||
Api.QUERY_set_audio_sample(rendersound ? _soundcb : null);
|
||||
|
||||
|
@ -104,14 +89,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
}
|
||||
|
||||
public string SystemId { get; }
|
||||
|
||||
// adelikat: Nasty hack to force new business logic. Compatibility (and Accuracy when fully supported) will ALWAYS be in deterministic mode,
|
||||
// a consequence is a permanent performance hit to the compatibility core
|
||||
// Perormance will NEVER be in deterministic mode (and the client side logic will prohibit movie recording on it)
|
||||
// feos: Nasty hack to a nasty hack. Allow user disable it with a strong warning.
|
||||
public bool DeterministicEmulation =>
|
||||
_settings.ForceDeterminism
|
||||
&& (CurrentProfile == "Compatibility" || CurrentProfile == "Accuracy");
|
||||
public bool DeterministicEmulation => true;
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
|
@ -129,15 +107,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
Api.CMD_unload_cartridge();
|
||||
Api.CMD_term();
|
||||
|
||||
_resampler.Dispose();
|
||||
Api.Dispose();
|
||||
_resampler.Dispose();
|
||||
|
||||
_currCdl?.Unpin();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,8 +84,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
private unsafe void MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian, int byteSize = 1)
|
||||
{
|
||||
int size = Api.QUERY_get_memory_size(id);
|
||||
int mask = size - 1;
|
||||
bool pow2 = Util.IsPowerOfTwo(size);
|
||||
|
||||
// if this type of memory isnt available, dont make the memory domain (most commonly save ram)
|
||||
if (size == 0)
|
||||
|
@ -95,46 +93,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
byte* blockptr = Api.QUERY_get_memory_data(id);
|
||||
|
||||
MemoryDomain md;
|
||||
|
||||
if (id == LibsnesApi.SNES_MEMORY.OAM)
|
||||
{
|
||||
// OAM is actually two differently sized banks of memory which arent truly considered adjacent.
|
||||
// maybe a better way to visualize it is with an empty bus and adjacent banks
|
||||
// so, we just throw away everything above its size of 544 bytes
|
||||
if (size != 544)
|
||||
{
|
||||
throw new InvalidOperationException("oam size isnt 544 bytes.. wtf?");
|
||||
}
|
||||
|
||||
md = new MemoryDomainDelegate(
|
||||
name,
|
||||
size,
|
||||
endian,
|
||||
addr => addr < 544 ? blockptr[addr] : (byte)0x00,
|
||||
(addr, value) => { if (addr < 544) { blockptr[addr] = value; } },
|
||||
byteSize);
|
||||
}
|
||||
else if (pow2)
|
||||
{
|
||||
md = new MemoryDomainDelegate(
|
||||
name,
|
||||
size,
|
||||
endian,
|
||||
addr => blockptr[addr & mask],
|
||||
(addr, value) => blockptr[addr & mask] = value,
|
||||
byteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
md = new MemoryDomainDelegate(
|
||||
name,
|
||||
size,
|
||||
endian,
|
||||
addr => blockptr[addr % size],
|
||||
(addr, value) => blockptr[addr % size] = value,
|
||||
byteSize);
|
||||
}
|
||||
var md = new MemoryDomainIntPtrMonitor(name, MemoryDomain.Endian.Little, (IntPtr)blockptr, size, true, byteSize, Api);
|
||||
|
||||
_memoryDomainList.Add(md);
|
||||
}
|
||||
|
@ -152,19 +111,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
var md = new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little,
|
||||
addr =>
|
||||
{
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
return blockptr[a.Value];
|
||||
}
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
{
|
||||
return blockptr[a.Value];
|
||||
}
|
||||
|
||||
return FakeBusRead((int)addr);
|
||||
return FakeBusRead((int)addr);
|
||||
}
|
||||
},
|
||||
(addr, val) =>
|
||||
{
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
blockptr[a.Value] = val;
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
blockptr[a.Value] = val;
|
||||
}
|
||||
}, wordSize: 2);
|
||||
_memoryDomainList.Add(md);
|
||||
}
|
||||
|
|
|
@ -12,26 +12,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
var temp = SaveStateBinary();
|
||||
/*var temp = SaveStateBinary();
|
||||
temp.SaveAsHexFast(writer);
|
||||
writer.WriteLine("Frame {0}", Frame); // we don't parse this, it's only for the client to use
|
||||
writer.WriteLine("Profile {0}", CurrentProfile);
|
||||
writer.WriteLine("Profile {0}", CurrentProfile);*/
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
string hex = reader.ReadLine();
|
||||
/*string hex = reader.ReadLine();
|
||||
byte[] state = new byte[hex.Length / 2];
|
||||
state.ReadFromHexFast(hex);
|
||||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
reader.ReadLine(); // Frame #
|
||||
var profile = reader.ReadLine().Split(' ')[1];
|
||||
ValidateLoadstateProfile(profile);
|
||||
ValidateLoadstateProfile(profile);*/
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState());
|
||||
/*writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState());
|
||||
|
||||
// other variables
|
||||
writer.Write(IsLagFrame);
|
||||
|
@ -39,12 +39,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
writer.Write(Frame);
|
||||
writer.Write(CurrentProfile);
|
||||
|
||||
writer.Flush();
|
||||
writer.Flush();*/
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
int size = Api.QUERY_serialize_size();
|
||||
/*int size = Api.QUERY_serialize_size();
|
||||
byte[] buf = reader.ReadBytes(size);
|
||||
CoreLoadState(buf);
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
LagCount = reader.ReadInt32();
|
||||
Frame = reader.ReadInt32();
|
||||
var profile = reader.ReadString();
|
||||
ValidateLoadstateProfile(profile);
|
||||
ValidateLoadstateProfile(profile);*/
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
|
@ -90,34 +90,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
return ms.ToArray();
|
||||
}
|
||||
|
||||
// handle the unmanaged part of loadstating
|
||||
private void CoreLoadState(byte[] data)
|
||||
{
|
||||
int size = Api.QUERY_serialize_size();
|
||||
if (data.Length != size)
|
||||
{
|
||||
throw new Exception("Libsnes internal savestate size mismatch!");
|
||||
}
|
||||
|
||||
Api.CMD_init();
|
||||
|
||||
// zero 01-sep-2014 - this approach isn't being used anymore, it's too slow!
|
||||
// LoadCurrent(); //need to make sure chip roms are reloaded
|
||||
fixed (byte* pbuf = &data[0])
|
||||
Api.CMD_unserialize(new IntPtr(pbuf), size);
|
||||
}
|
||||
|
||||
|
||||
// handle the unmanaged part of savestating
|
||||
private byte[] CoreSaveState()
|
||||
{
|
||||
int size = Api.QUERY_serialize_size();
|
||||
byte[] buf = new byte[size];
|
||||
fixed (byte* pbuf = &buf[0])
|
||||
Api.CMD_serialize(new IntPtr(pbuf), size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void ValidateLoadstateProfile(string profile)
|
||||
{
|
||||
if (profile != CurrentProfile)
|
||||
|
@ -125,8 +97,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
throw new InvalidOperationException($"You've attempted to load a savestate made using a different SNES profile ({profile}) than your current configuration ({CurrentProfile}). We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now.");
|
||||
}
|
||||
}
|
||||
|
||||
// most recent internal savestate, for deterministic mode ONLY
|
||||
private byte[] _savestatebuff;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
_settings = (SnesSettings)settings ?? new SnesSettings();
|
||||
_syncSettings = (SnesSyncSettings)syncSettings ?? new SnesSyncSettings();
|
||||
|
||||
Api = new LibsnesApi(GetDllPath())
|
||||
// TODO: pass profile here
|
||||
Api = new LibsnesApi(CoreComm.CoreFileProvider.DllPath())
|
||||
{
|
||||
ReadHook = ReadHook,
|
||||
ExecHook = ExecHook,
|
||||
|
@ -169,17 +170,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
|
||||
SetupMemoryDomains(romData, sgbRomData);
|
||||
|
||||
// DeterministicEmulation = deterministicEmulation; // Note we don't respect the value coming in and force it instead
|
||||
if (DeterministicEmulation) // save frame-0 savestate now
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
bw.Write(CoreSaveState());
|
||||
bw.Write(true); // framezero, so no controller follows and don't frameadvance on load
|
||||
bw.Close();
|
||||
_savestatebuff = ms.ToArray();
|
||||
}
|
||||
|
||||
if (CurrentProfile == "Compatibility")
|
||||
{
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
|
@ -198,7 +188,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
private LoadParams _currLoadParams;
|
||||
private SpeexResampler _resampler;
|
||||
private int _timeFrameCounter;
|
||||
private bool _nocallbacks; // disable all external callbacks. the front end should not even know the core is frame advancing
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsSGB { get; }
|
||||
|
@ -222,7 +211,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
}
|
||||
}
|
||||
|
||||
public LibsnesApi Api { get; }
|
||||
private LibsnesApi Api { get; }
|
||||
|
||||
public SnesColors.ColorType CurrPalette { get; private set; }
|
||||
|
||||
|
@ -374,20 +363,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
Api.QUERY_set_color_lut((IntPtr)p);
|
||||
}
|
||||
|
||||
private string GetDllPath()
|
||||
{
|
||||
var exename = "libsneshawk-32-" + CurrentProfile.ToLower() + ".dll";
|
||||
|
||||
string dllPath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), exename);
|
||||
|
||||
if (!File.Exists(dllPath))
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't locate the DLL for SNES emulation for profile: " + CurrentProfile + ". Please make sure you're using a fresh dearchive of a BizHawk distribution.");
|
||||
}
|
||||
|
||||
return dllPath;
|
||||
}
|
||||
|
||||
private void ReadHook(uint addr)
|
||||
{
|
||||
MemoryCallbacks.CallReads(addr);
|
||||
|
|
|
@ -2,9 +2,13 @@ section .text
|
|||
global co_swap
|
||||
global __imp_co_swap
|
||||
|
||||
; TODO: how to tell GCC it doesn't need this?
|
||||
align 16
|
||||
__imp_co_swap:
|
||||
dq co_swap
|
||||
|
||||
align 16
|
||||
co_swap:
|
||||
__imp_co_swap:
|
||||
mov [rdx],rsp
|
||||
mov rsp,[rcx]
|
||||
pop rax
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define LIBSNES_IMPORT
|
||||
#include "snes/snes.hpp"
|
||||
#include "libsnes.hpp"
|
||||
#include <emulibc.h>
|
||||
|
||||
#include <libco.h>
|
||||
|
||||
|
@ -136,6 +137,8 @@ struct CommStruct
|
|||
//the SIG or BRK that the core is halted in
|
||||
eMessage reason;
|
||||
|
||||
int32 padding1;
|
||||
|
||||
//flexible in/out parameters
|
||||
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
|
||||
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
|
||||
|
@ -147,10 +150,14 @@ struct CommStruct
|
|||
int32 scanline;
|
||||
SNES::Input::Device inports[2];
|
||||
|
||||
int32 padding2;
|
||||
|
||||
//always used in pairs
|
||||
void* buf[3];
|
||||
int32 buf_size[3];
|
||||
|
||||
int32 padding3;
|
||||
|
||||
int64 cdl_ptr[4];
|
||||
int32 cdl_size[4];
|
||||
|
||||
|
@ -161,6 +168,8 @@ struct CommStruct
|
|||
uint32 region;
|
||||
uint32 mapper;
|
||||
|
||||
int32 padding4;
|
||||
|
||||
//===========================================================
|
||||
|
||||
//private stuff
|
||||
|
@ -282,8 +291,11 @@ void* snes_allocSharedMemory(const char* memtype, size_t amt)
|
|||
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
|
||||
//if(!running) return NULL;
|
||||
|
||||
auto ret = alloc_plain(amt);
|
||||
|
||||
comm.str = (char*)memtype;
|
||||
comm.size = amt;
|
||||
comm.ptr = ret;
|
||||
|
||||
BREAK(eMessage_SIG_allocSharedMemory);
|
||||
|
||||
|
@ -573,6 +585,29 @@ void new_emuthread()
|
|||
|
||||
EXPORT void* DllInit()
|
||||
{
|
||||
#define T(s,n) static_assert(offsetof(CommStruct,s)==n,#n)
|
||||
T(cmd, 0);
|
||||
T(status, 4);
|
||||
T(reason, 8);
|
||||
T(str, 16);
|
||||
T(ptr, 24);
|
||||
T(id, 32);
|
||||
T(port, 48);
|
||||
T(width, 64);
|
||||
T(scanline, 72);
|
||||
T(inports, 76);
|
||||
T(buf, 88);
|
||||
T(buf_size, 112);
|
||||
T(cdl_ptr, 128);
|
||||
T(cdl_size, 160);
|
||||
T(cpuregs, 176);
|
||||
T(layerEnables, 208);
|
||||
T(region, 220);
|
||||
T(mapper, 224);
|
||||
// start of private stuff
|
||||
T(privbuf, 232);
|
||||
#undef T
|
||||
|
||||
memset(&comm,0,sizeof(comm));
|
||||
|
||||
//make a coroutine thread to run the emulation in. we'll switch back to this cothread when communicating with the frontend
|
||||
|
|
Loading…
Reference in New Issue