diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs
index c7abea2154..1c7078cfb0 100644
--- a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs
+++ b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs
@@ -407,7 +407,7 @@ namespace BizHawk.Client.EmuHawk
we.SetWatch(SelectedWatches.First().Domain, SelectedWatches, duplicate ? WatchEditor.Mode.Duplicate : WatchEditor.Mode.Edit);
- var result = we.ShowHawkDialog();
+ var result = we.ShowHawkDialog(this);
if (result == DialogResult.OK)
{
Changes();
@@ -777,7 +777,7 @@ namespace BizHawk.Client.EmuHawk
poke.SetWatch(SelectedWatches);
- if (poke.ShowHawkDialog() == DialogResult.OK)
+ if (poke.ShowHawkDialog(this) == DialogResult.OK)
{
UpdateValues();
}
diff --git a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
index 416ba033db..17510a2b30 100644
--- a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
+++ b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
@@ -64,6 +64,9 @@
+
+
+
diff --git a/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs b/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs
new file mode 100644
index 0000000000..d5498c2d1e
--- /dev/null
+++ b/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Common.BizInvoke
+{
+ public static class BizInvoker
+ {
+ public static T GetInvoker(IImportResolver dll)
+ where T : class
+ {
+ var baseType = typeof(T);
+ if (baseType.IsSealed)
+ throw new InvalidOperationException("Can't proxy a sealed type");
+ if (!baseType.IsPublic)
+ // the proxy type will be in a new assembly, so public is required here
+ throw new InvalidOperationException("Type must be public");
+
+ var baseConstructor = baseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
+ if (baseConstructor == null)
+ throw new InvalidOperationException("Base type must have a zero arg constructor");
+
+ var baseMethods = baseType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+ .Select(m => new
+ {
+ Info = m,
+ Attr = m.GetCustomAttributes(true).OfType().FirstOrDefault()
+ })
+ .Where(a => a.Attr != null)
+ .ToList();
+
+ if (baseMethods.Count == 0)
+ throw new InvalidOperationException("Couldn't find any [BizImport] methods to proxy");
+
+ {
+ var uo = baseMethods.FirstOrDefault(a => !a.Info.IsVirtual || a.Info.IsFinal);
+ if (uo != null)
+ throw new InvalidOperationException("Method " + uo.Info.Name + " cannot be overriden!");
+
+ // there's no technical reason to disallow this, but we wouldn't be doing anything
+ // with the base implementation, so it's probably a user error
+ var na = baseMethods.FirstOrDefault(a => !a.Info.IsAbstract);
+ if (na != null)
+ throw new InvalidOperationException("Method " + na.Info.Name + " is not abstract!");
+ }
+
+ var aname = new AssemblyName(baseType.Name + Guid.NewGuid().ToString("N"));
+ var assy = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
+ var module = assy.DefineDynamicModule("BizInvoker");
+ var type = module.DefineType("Bizhawk.BizInvokeProxy", TypeAttributes.Class | TypeAttributes.Public, baseType);
+
+ foreach (var mi in baseMethods)
+ {
+ var paramInfos = mi.Info.GetParameters();
+ var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
+ var nativeParamTypes = new List();
+ var returnType = mi.Info.ReturnType;
+ var method = type.DefineMethod(mi.Info.Name, MethodAttributes.Virtual | MethodAttributes.Public,
+ CallingConventions.HasThis, returnType, paramTypes);
+
+ var entryPointName = mi.Attr.EntryPoint ?? mi.Info.Name;
+ var entryPtr = dll.Resolve(entryPointName);
+ if (entryPtr == IntPtr.Zero)
+ throw new InvalidOperationException("Resolver returned NULL for entry point " + entryPointName);
+
+ if (returnType != typeof(void) && !returnType.IsPrimitive)
+ throw new InvalidOperationException("Only primitive return types are supported");
+
+ var il = method.GetILGenerator();
+ for (int i = 0; i < paramTypes.Length; i++)
+ {
+ // arg 0 is this, so + 1
+ nativeParamTypes.Add(EmitParamterLoad(il, i + 1, paramTypes[i]));
+ }
+ LoadConstant(il, entryPtr);
+ il.EmitCalli(OpCodes.Calli, mi.Attr.CallingConvention, returnType, nativeParamTypes.ToArray());
+
+ // either there's a primitive on the stack and we're expected to return that primitive,
+ // or there's nothing on the stack and we're expected to return nothing
+ il.Emit(OpCodes.Ret);
+
+ type.DefineMethodOverride(method, mi.Info);
+ }
+
+ return (T)Activator.CreateInstance(type.CreateType());
+ }
+
+ private static void LoadConstant(ILGenerator il, IntPtr p)
+ {
+ if (p == IntPtr.Zero)
+ il.Emit(OpCodes.Ldc_I4_0);
+ else if (IntPtr.Size == 4)
+ il.Emit(OpCodes.Ldc_I4, (int)p);
+ else
+ il.Emit(OpCodes.Ldc_I8, (long)p);
+ il.Emit(OpCodes.Conv_I);
+ }
+
+ private static void LoadConstant(ILGenerator il, UIntPtr p)
+ {
+ if (p == UIntPtr.Zero)
+ il.Emit(OpCodes.Ldc_I4_0);
+ else if (UIntPtr.Size == 4)
+ il.Emit(OpCodes.Ldc_I4, (int)p);
+ else
+ il.Emit(OpCodes.Ldc_I8, (long)p);
+ il.Emit(OpCodes.Conv_U);
+ }
+
+ private static Type EmitParamterLoad(ILGenerator il, int idx, Type type)
+ {
+ if (type.IsGenericType)
+ throw new InvalidOperationException("Generic types not supported");
+ if (type.IsByRef)
+ {
+ var et = type.GetElementType();
+ if (!et.IsPrimitive)
+ throw new InvalidOperationException("Only refs of primitive types are supported!");
+ var loc = il.DeclareLocal(type, true);
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Stloc, loc);
+ il.Emit(OpCodes.Conv_I);
+ return typeof(IntPtr);
+ }
+ else if (type.IsArray)
+ {
+ var et = type.GetElementType();
+ if (!et.IsPrimitive)
+ throw new InvalidOperationException("Only arrays of primitive types are supported!");
+
+ // these two cases aren't too hard to add
+ if (type.GetArrayRank() > 1)
+ throw new InvalidOperationException("Multidimensional arrays are not supported!");
+ if (type.Name.Contains('*'))
+ throw new InvalidOperationException("Only 0-based 1-dimensional arrays are supported!");
+
+ var loc = il.DeclareLocal(type, true);
+ var end = il.DefineLabel();
+ var isNull = il.DefineLabel();
+
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ il.Emit(OpCodes.Brfalse, isNull);
+
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Stloc, loc);
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Ldelema, et);
+ il.Emit(OpCodes.Conv_I);
+ il.Emit(OpCodes.Br, end);
+
+ il.MarkLabel(isNull);
+ LoadConstant(il, IntPtr.Zero);
+ il.MarkLabel(end);
+
+ return typeof(IntPtr);
+ }
+ else if (typeof(Delegate).IsAssignableFrom(type))
+ {
+ var mi = typeof(Marshal).GetMethod("GetFunctionPointerForDelegate", new[] { typeof(Delegate) });
+ var end = il.DefineLabel();
+ var isNull = il.DefineLabel();
+
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ il.Emit(OpCodes.Brfalse, isNull);
+
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ il.Emit(OpCodes.Call, mi);
+ il.Emit(OpCodes.Br, end);
+
+ il.MarkLabel(isNull);
+ LoadConstant(il, IntPtr.Zero);
+ il.MarkLabel(end);
+ return typeof(IntPtr);
+ }
+ else if (type.IsPrimitive)
+ {
+ il.Emit(OpCodes.Ldarg, (short)idx);
+ return type;
+ }
+ else
+ {
+ throw new InvalidOperationException("Unrecognized parameter type!");
+ }
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class BizImportAttribute : Attribute
+ {
+ public CallingConvention CallingConvention
+ {
+ get { return _callingConvention; }
+ }
+ private readonly CallingConvention _callingConvention;
+
+ public string EntryPoint { get; set; }
+
+ public BizImportAttribute(CallingConvention c)
+ {
+ _callingConvention = c;
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Common/BizInvoke/IImportResolver.cs b/BizHawk.Emulation.Common/BizInvoke/IImportResolver.cs
new file mode 100644
index 0000000000..bb33bd06cf
--- /dev/null
+++ b/BizHawk.Emulation.Common/BizInvoke/IImportResolver.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.Emulation.Common.BizInvoke
+{
+ public interface IImportResolver
+ {
+ IntPtr Resolve(string entryPoint);
+ }
+}
diff --git a/BizHawk.Emulation.Common/BizInvoke/Win32LibraryImportResolver.cs b/BizHawk.Emulation.Common/BizInvoke/Win32LibraryImportResolver.cs
new file mode 100644
index 0000000000..5bc21d8763
--- /dev/null
+++ b/BizHawk.Emulation.Common/BizInvoke/Win32LibraryImportResolver.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Common.BizInvoke
+{
+ public class Win32LibraryImportResolver : IImportResolver, IDisposable
+ {
+ private IntPtr _p;
+
+ public Win32LibraryImportResolver(string dllName)
+ {
+ _p = Win32.LoadLibrary(dllName);
+ if (_p == IntPtr.Zero)
+ throw new InvalidOperationException("LoadLibrary returned NULL");
+ }
+
+ public IntPtr Resolve(string entryPoint)
+ {
+ return Win32.GetProcAddress(_p, entryPoint);
+ }
+
+ private void Free()
+ {
+ if (_p != IntPtr.Zero)
+ {
+ Win32.FreeLibrary(_p);
+ _p = IntPtr.Zero;
+ }
+ }
+
+ public void Dispose()
+ {
+ Free();
+ GC.SuppressFinalize(this);
+ }
+
+ ~Win32LibraryImportResolver()
+ {
+ Free();
+ }
+
+ private static class Win32
+ {
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr LoadLibrary(string dllToLoad);
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
+ [DllImport("kernel32.dll")]
+ public static extern bool FreeLibrary(IntPtr hModule);
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs
index 4d3f40a22f..82e573126e 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs
@@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
+using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
- public static class LibQuickNES
+ public static class LibQuickNESOld
{
public const string dllname = "libquicknes.dll";
@@ -258,4 +259,257 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
}
}
}
+
+ public abstract class LibQuickNES
+ {
+ public const string dllname = "libquicknes.dll";
+
+ ///
+ /// setup extra mappers. should be done before anything else
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_setup_mappers();
+ ///
+ /// create a new quicknes context
+ ///
+ /// NULL on failure
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_new();
+ ///
+ /// destroy a quicknes context
+ ///
+ /// context previously returned from qn_new()
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_delete(IntPtr e);
+ ///
+ /// load an ines file
+ ///
+ /// context
+ /// file
+ /// length of file
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_loadines(IntPtr e, byte[] data, int length);
+ ///
+ /// set audio sample rate
+ ///
+ /// context
+ /// hz
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_set_sample_rate(IntPtr e, int rate);
+ ///
+ /// emulate a single frame
+ ///
+ /// context
+ /// pad 1 input
+ /// pad 2 input
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_emulate_frame(IntPtr e, int pad1, int pad2);
+ ///
+ /// blit to rgb32
+ ///
+ /// Context
+ /// rgb32 256x240 packed
+ /// rgb32 colors, 512 of them
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_blit(IntPtr e, int[] dest, int[] colors, int cropleft, int croptop, int cropright, int cropbottom);
+ ///
+ /// get quicknes's default palette
+ ///
+ /// 1536 bytes suitable for qn_blit
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_get_default_colors();
+ ///
+ /// get number of times joypad was read in most recent frame
+ ///
+ /// context
+ /// 0 means lag
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract int qn_get_joypad_read_count(IntPtr e);
+ ///
+ /// get audio info for most recent frame
+ ///
+ /// context
+ /// number of samples actually created
+ /// 1 for mono, 2 for stereo
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_get_audio_info(IntPtr e, ref int sample_count, ref int chan_count);
+ ///
+ /// get audio for most recent frame. must not be called more than once per frame!
+ ///
+ /// context
+ /// sample buffer
+ /// length to read into sample buffer
+ /// length actually read
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract int qn_read_audio(IntPtr e, short[] dest, int max_samples);
+ ///
+ /// reset the console
+ ///
+ /// context
+ /// true for powercycle, false for reset button
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_reset(IntPtr e, bool hard);
+ ///
+ /// get the required byte size of a savestate
+ ///
+ /// context
+ /// size is returned
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_state_size(IntPtr e, ref int size);
+ ///
+ /// save state to buffer
+ ///
+ /// context
+ /// buffer
+ /// length of buffer
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_state_save(IntPtr e, byte[] dest, int size);
+ ///
+ /// load state from buffer
+ ///
+ /// context
+ /// buffer
+ /// length of buffer
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_state_load(IntPtr e, byte[] src, int size);
+ ///
+ /// query battery ram state
+ ///
+ /// context
+ /// true if battery backup sram exists
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract bool qn_has_battery_ram(IntPtr e);
+ ///
+ /// query battery ram size
+ ///
+ /// context
+ /// size is returned
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_battery_ram_size(IntPtr e, ref int size);
+ ///
+ /// save battery ram to buffer
+ ///
+ /// context
+ /// buffer
+ /// size
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_battery_ram_save(IntPtr e, byte[] dest, int size);
+ ///
+ /// load battery ram from buffer
+ ///
+ /// context
+ /// buffer
+ /// size
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_battery_ram_load(IntPtr e, byte[] src, int size);
+ ///
+ /// clear battery ram
+ ///
+ /// context
+ /// string error
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_battery_ram_clear(IntPtr e);
+ ///
+ /// set sprite limit; does not affect emulation
+ ///
+ /// context
+ /// 0 to hide, 8 for normal, 64 for all
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_set_sprite_limit(IntPtr e, int n);
+ ///
+ /// get memory area for debugging
+ ///
+ /// Context
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract bool qn_get_memory_area(IntPtr e, int which, ref IntPtr data, ref int size, ref bool writable, ref IntPtr name);
+ ///
+ /// peek the system bus
+ ///
+ /// Context
+ /// 0000:ffff, but non-ram/rom addresses won't work
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract byte qn_peek_prgbus(IntPtr e, int addr);
+ ///
+ /// poke the system bus
+ ///
+ /// Context
+ /// 0000:ffff, but non-ram/rom addresses won't work
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_poke_prgbus(IntPtr e, int addr, byte val);
+ ///
+ /// get internal registers
+ ///
+ /// Context
+ /// a, x, y, sp, pc, p
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_get_cpuregs(IntPtr e, [Out] int[] dest);
+ ///
+ /// get the mapper that's loaded
+ ///
+ /// Context
+ /// recieves mapper number
+ /// mapper name
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_get_mapper(IntPtr e, ref int number);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void TraceCallback(IntPtr data);
+
+ ///
+ /// set a trace callback to be run on each cycle
+ ///
+ /// Context
+ ///
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_set_tracecb(IntPtr e, TraceCallback cb);
+
+
+
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract byte qn_get_reg2000(IntPtr e);
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_get_palmem(IntPtr e);
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract IntPtr qn_get_oammem(IntPtr e);
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract byte qn_peek_ppu(IntPtr e, int addr);
+ [BizImport(CallingConvention.Cdecl)]
+ public abstract void qn_peek_ppubus(IntPtr e, byte[] dest);
+
+ ///
+ /// handle "string error" as returned by some quicknes functions
+ ///
+ ///
+ public static void ThrowStringError(IntPtr p)
+ {
+ if (p == IntPtr.Zero)
+ return;
+ string s = Marshal.PtrToStringAnsi(p);
+ if (s == "Unsupported mapper" || s == "Not an iNES file") // Not worth making a new exception for the iNES error, they ultimately are the same problem
+ {
+ throw new Emulation.Common.UnsupportedGameException("Quicknes unsupported mapper");
+ }
+ else
+ {
+ throw new InvalidOperationException("LibQuickNES error: " + s);
+ }
+ }
+ }
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs
index fe05914eb0..e5d5d12b63 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs
@@ -11,7 +11,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
int[] regs = new int[6];
var ret = new Dictionary();
- LibQuickNES.qn_get_cpuregs(Context, regs);
+ QN.qn_get_cpuregs(Context, regs);
ret["A"] = (byte)regs[0];
ret["X"] = (byte)regs[1];
ret["Y"] = (byte)regs[2];
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs
index 56dc157515..cc998d3c51 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs
@@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
bool writable = false;
IntPtr name = IntPtr.Zero;
- if (!LibQuickNES.qn_get_memory_area(Context, i, ref data, ref size, ref writable, ref name))
+ if (!QN.qn_get_memory_area(Context, i, ref data, ref size, ref writable, ref name))
break;
if (data != IntPtr.Zero && size > 0 && name != IntPtr.Zero)
@@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
throw new ArgumentOutOfRangeException();
}
- return LibQuickNES.qn_peek_prgbus(Context, (int)addr);
+ return QN.qn_peek_prgbus(Context, (int)addr);
},
delegate(long addr, byte val)
{
@@ -48,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
throw new ArgumentOutOfRangeException();
}
- LibQuickNES.qn_poke_prgbus(Context, (int)addr, val);
+ QN.qn_poke_prgbus(Context, (int)addr, val);
}
));
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.INESPPUViewable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.INESPPUViewable.cs
index de9382909c..40251ec9be 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.INESPPUViewable.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.INESPPUViewable.cs
@@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
return VideoPalette;
}
- private byte R2000 { get { return LibQuickNES.qn_get_reg2000(Context); } }
+ private byte R2000 { get { return QN.qn_get_reg2000(Context); } }
public bool BGBaseHigh
{
@@ -39,27 +39,27 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private byte[] ppubusbuf = new byte[0x3000];
public byte[] GetPPUBus()
{
- LibQuickNES.qn_peek_ppubus(Context, ppubusbuf);
+ QN.qn_peek_ppubus(Context, ppubusbuf);
return ppubusbuf;
}
private byte[] palrambuf = new byte[0x20];
public byte[] GetPalRam()
{
- Marshal.Copy(LibQuickNES.qn_get_palmem(Context), palrambuf, 0, 0x20);
+ Marshal.Copy(QN.qn_get_palmem(Context), palrambuf, 0, 0x20);
return palrambuf;
}
byte[] oambuf = new byte[0x100];
public byte[] GetOam()
{
- Marshal.Copy(LibQuickNES.qn_get_oammem(Context), oambuf, 0, 0x100);
+ Marshal.Copy(QN.qn_get_oammem(Context), oambuf, 0, 0x100);
return oambuf;
}
public byte PeekPPU(int addr)
{
- return LibQuickNES.qn_peek_ppu(Context, addr);
+ return QN.qn_peek_ppu(Context, addr);
}
// we don't use quicknes's MMC5 at all, so these three methods are just stubs
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs
index afbd2d0f04..3c58707f43 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs
@@ -6,20 +6,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
public byte[] CloneSaveRam()
{
- LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_save(Context, SaveRamBuff, SaveRamBuff.Length));
+ LibQuickNES.ThrowStringError(QN.qn_battery_ram_save(Context, SaveRamBuff, SaveRamBuff.Length));
return (byte[])SaveRamBuff.Clone();
}
public void StoreSaveRam(byte[] data)
{
- LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_load(Context, data, data.Length));
+ LibQuickNES.ThrowStringError(QN.qn_battery_ram_load(Context, data, data.Length));
}
public bool SaveRamModified
{
get
{
- return LibQuickNES.qn_has_battery_ram(Context);
+ return QN.qn_has_battery_ram(Context);
}
}
@@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private void InitSaveRamBuff()
{
int size = 0;
- LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_size(Context, ref size));
+ LibQuickNES.ThrowStringError(QN.qn_battery_ram_size(Context, ref size));
SaveRamBuff = new byte[size];
}
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
index 110ae38c60..0d50e5ae0a 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
@@ -24,7 +24,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
public bool PutSettings(QuickNESSettings o)
{
_settings = o;
- LibQuickNES.qn_set_sprite_limit(Context, _settings.NumSprites);
+ QN.qn_set_sprite_limit(Context, _settings.NumSprites);
RecalculateCrops();
CalculatePalette();
@@ -146,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private static byte[] GetDefaultColors()
{
- IntPtr src = LibQuickNES.qn_get_default_colors();
+ IntPtr src = QN.qn_get_default_colors();
byte[] ret = new byte[1536];
Marshal.Copy(src, ret, 0, 1536);
return ret;
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IStatable.cs
index 1a1fbbe0d6..2f834515b9 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IStatable.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IStatable.cs
@@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
public void SaveStateBinary(System.IO.BinaryWriter writer)
{
CheckDisposed();
- LibQuickNES.ThrowStringError(LibQuickNES.qn_state_save(Context, SaveStateBuff, SaveStateBuff.Length));
+ LibQuickNES.ThrowStringError(QN.qn_state_save(Context, SaveStateBuff, SaveStateBuff.Length));
writer.Write(SaveStateBuff.Length);
writer.Write(SaveStateBuff);
// other variables
@@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
if (len != SaveStateBuff.Length)
throw new InvalidOperationException("Unexpected savestate buffer length!");
reader.Read(SaveStateBuff, 0, SaveStateBuff.Length);
- LibQuickNES.ThrowStringError(LibQuickNES.qn_state_load(Context, SaveStateBuff, SaveStateBuff.Length));
+ LibQuickNES.ThrowStringError(QN.qn_state_load(Context, SaveStateBuff, SaveStateBuff.Length));
// other variables
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
@@ -73,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private void InitSaveStateBuff()
{
int size = 0;
- LibQuickNES.ThrowStringError(LibQuickNES.qn_state_size(Context, ref size));
+ LibQuickNES.ThrowStringError(QN.qn_state_size(Context, ref size));
SaveStateBuff = new byte[size];
SaveStateBuff2 = new byte[size + 13];
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IVideoProvider.cs
index ac87d84b9c..630ed11220 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IVideoProvider.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IVideoProvider.cs
@@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
private void Blit()
{
- LibQuickNES.qn_blit(Context, VideoOutput, VideoPalette, cropleft, croptop, cropright, cropbottom);
+ QN.qn_blit(Context, VideoOutput, VideoPalette, cropleft, croptop, cropright, cropbottom);
}
}
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
index 0567aa96a1..c52fc7934b 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs
@@ -11,6 +11,7 @@ using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
+using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
@@ -26,9 +27,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
public partial class QuickNES : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IInputPollable,
IStatable, IDebuggable, ISettable, Cores.Nintendo.NES.INESPPUViewable
{
+ static readonly LibQuickNES QN;
+ static readonly Win32LibraryImportResolver Resolver;
+
+
static QuickNES()
{
- LibQuickNES.qn_setup_mappers();
+ Resolver = new Win32LibraryImportResolver(LibQuickNES.dllname);
+ QN = BizInvoker.GetInvoker(Resolver);
}
[CoreConstructor("NES")]
@@ -39,12 +45,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
ServiceProvider = new BasicServiceProvider(this);
CoreComm = comm;
- Context = LibQuickNES.qn_new();
+ Context = QN.qn_new();
if (Context == IntPtr.Zero)
throw new InvalidOperationException("qn_new() returned NULL");
try
{
- LibQuickNES.ThrowStringError(LibQuickNES.qn_loadines(Context, file, file.Length));
+ unsafe
+ {
+ fixed (byte* p = file)
+ {
+ Console.WriteLine((IntPtr)p);
+ LibQuickNES.ThrowStringError(QN.qn_loadines(Context, file, file.Length));
+ }
+ }
InitSaveRamBuff();
InitSaveStateBuff();
@@ -52,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
InitMemoryDomains();
int mapper = 0;
- string mappername = Marshal.PtrToStringAnsi(LibQuickNES.qn_get_mapper(Context, ref mapper));
+ string mappername = Marshal.PtrToStringAnsi(QN.qn_get_mapper(Context, ref mapper));
Console.WriteLine("QuickNES: Booted with Mapper #{0} \"{1}\"", mapper, mappername);
BoardName = mappername;
CoreComm.VsyncNum = 39375000;
@@ -185,21 +198,21 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
using (FP.Save())
{
if (Controller["Power"])
- LibQuickNES.qn_reset(Context, true);
+ QN.qn_reset(Context, true);
if (Controller["Reset"])
- LibQuickNES.qn_reset(Context, false);
+ QN.qn_reset(Context, false);
int j1, j2;
SetPads(out j1, out j2);
if (Tracer.Enabled)
- LibQuickNES.qn_set_tracecb(Context, _tracecb);
+ QN.qn_set_tracecb(Context, _tracecb);
else
- LibQuickNES.qn_set_tracecb(Context, null);
+ QN.qn_set_tracecb(Context, null);
Frame++;
- LibQuickNES.ThrowStringError(LibQuickNES.qn_emulate_frame(Context, j1, j2));
- IsLagFrame = LibQuickNES.qn_get_joypad_read_count(Context) == 0;
+ LibQuickNES.ThrowStringError(QN.qn_emulate_frame(Context, j1, j2));
+ IsLagFrame = QN.qn_get_joypad_read_count(Context) == 0;
if (IsLagFrame)
LagCount++;
@@ -214,7 +227,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
}
IntPtr Context;
-
public int Frame { get; private set; }
public string SystemId { get { return "NES"; } }
@@ -304,7 +316,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
if (Context != IntPtr.Zero)
{
- LibQuickNES.qn_delete(Context);
+ QN.qn_delete(Context);
Context = IntPtr.Zero;
}
}
@@ -324,12 +336,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
void InitAudio()
{
- LibQuickNES.ThrowStringError(LibQuickNES.qn_set_sample_rate(Context, 44100));
+ LibQuickNES.ThrowStringError(QN.qn_set_sample_rate(Context, 44100));
}
void DrainAudio()
{
- NumSamples = LibQuickNES.qn_read_audio(Context, MonoBuff, MonoBuff.Length);
+ NumSamples = QN.qn_read_audio(Context, MonoBuff, MonoBuff.Length);
unsafe
{
fixed (short* _src = &MonoBuff[0], _dst = &StereoBuff[0])
diff --git a/quicknes/bizinterface.cpp b/quicknes/bizinterface.cpp
index 74e9ab3ac1..5304124e87 100644
--- a/quicknes/bizinterface.cpp
+++ b/quicknes/bizinterface.cpp
@@ -31,7 +31,11 @@ void operator delete(void *p)
std::free(p);
}
+#ifdef _MSC_VER
+#define EXPORT extern "C" __declspec(dllexport)
+#else
#define EXPORT extern "C" __declspec(dllexport) __attribute__((force_align_arg_pointer))
+#endif
EXPORT void qn_setup_mappers()
{