diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj index 961f21bbb3..0f0f5c46dc 100644 --- a/BizHawk.Common/BizHawk.Common.csproj +++ b/BizHawk.Common/BizHawk.Common.csproj @@ -101,6 +101,7 @@ + diff --git a/BizHawk.Common/IImportResolver.cs b/BizHawk.Common/IImportResolver.cs index db1b5d388d..ed0463ffe7 100644 --- a/BizHawk.Common/IImportResolver.cs +++ b/BizHawk.Common/IImportResolver.cs @@ -5,6 +5,9 @@ using System.Text; namespace BizHawk.Common { + /// + /// interface for a dynamic link library or similar + /// public interface IImportResolver { IntPtr Resolve(string entryPoint); diff --git a/BizHawk.Common/IMonitor.cs b/BizHawk.Common/IMonitor.cs new file mode 100644 index 0000000000..88e08f16e6 --- /dev/null +++ b/BizHawk.Common/IMonitor.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Common +{ + public interface IMonitor + { + void Enter(); + void Exit(); + } +} diff --git a/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs b/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs index eeb64a3da7..f20e5585d6 100644 --- a/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs +++ b/BizHawk.Emulation.Common/BizInvoke/BizInvoker.cs @@ -18,12 +18,15 @@ namespace BizHawk.Emulation.Common.BizInvoke { public Type ImplType; public List> Hooks; + public Action ConnectMonitor; - public object Create(IImportResolver dll) + public object Create(IImportResolver dll, IMonitor monitor) { var ret = Activator.CreateInstance(ImplType); foreach (var f in Hooks) f(ret, dll); + if (ConnectMonitor != null) + ConnectMonitor(ret, monitor); return ret; } } @@ -61,14 +64,34 @@ namespace BizHawk.Emulation.Common.BizInvoke var baseType = typeof(T); if (!Impls.TryGetValue(baseType, out impl)) { - impl = CreateProxy(baseType); + impl = CreateProxy(baseType, false); Impls.Add(baseType, impl); } } - return (T)impl.Create(dll); + if (impl.ConnectMonitor != null) + throw new InvalidOperationException("Class was previously proxied with a monitor!"); + return (T)impl.Create(dll, null); } - private static InvokerImpl CreateProxy(Type baseType) + public static T GetInvoker(IImportResolver dll, IMonitor monitor) + where T : class + { + InvokerImpl impl; + lock (Impls) + { + var baseType = typeof(T); + if (!Impls.TryGetValue(baseType, out impl)) + { + impl = CreateProxy(baseType, true); + Impls.Add(baseType, impl); + } + } + if (impl.ConnectMonitor == null) + throw new InvalidOperationException("Class was previously proxied without a monitor!"); + return (T)impl.Create(dll, monitor); + } + + private static InvokerImpl CreateProxy(Type baseType, bool monitor) { if (baseType.IsSealed) throw new InvalidOperationException("Can't proxy a sealed type"); @@ -109,28 +132,34 @@ namespace BizHawk.Emulation.Common.BizInvoke var type = ImplModuleBilder.DefineType("Bizhawk.BizInvokeProxy" + baseType.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, baseType); + var monitorField = monitor ? type.DefineField("MonitorField", typeof(IMonitor), FieldAttributes.Public) : null; + foreach (var mi in baseMethods) { var entryPointName = mi.Attr.EntryPoint ?? mi.Info.Name; var hook = mi.Attr.Compatibility - ? ImplementMethodDelegate(type, mi.Info, mi.Attr.CallingConvention, entryPointName) - : ImplementMethodCalli(type, mi.Info, mi.Attr.CallingConvention, entryPointName); + ? ImplementMethodDelegate(type, mi.Info, mi.Attr.CallingConvention, entryPointName, monitorField) + : ImplementMethodCalli(type, mi.Info, mi.Attr.CallingConvention, entryPointName, monitorField); postCreateHooks.Add(hook); } - return new InvokerImpl + var ret = new InvokerImpl { Hooks = postCreateHooks, ImplType = type.CreateType() }; + if (monitor) + ret.ConnectMonitor = (o, m) => o.GetType().GetField(monitorField.Name).SetValue(o, m); + + return ret; } /// /// create a method implementation that uses GetDelegateForFunctionPointer internally /// - private static Action ImplementMethodDelegate(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName) + private static Action ImplementMethodDelegate(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField) { var paramInfos = baseMethod.GetParameters(); var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray(); @@ -172,12 +201,45 @@ namespace BizHawk.Emulation.Common.BizInvoke var method = type.DefineMethod(baseMethod.Name, MethodAttributes.Virtual | MethodAttributes.Public, CallingConventions.HasThis, returnType, paramTypes); var il = method.GetILGenerator(); + + Label exc = new Label(); + if (monitorField != null) // monitor: enter and then begin try + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, monitorField); + il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter")); + exc = il.BeginExceptionBlock(); + } + il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); for (int i = 0; i < paramTypes.Length; i++) il.Emit(OpCodes.Ldarg, (short)(i + 1)); + il.Emit(OpCodes.Callvirt, delegateInvoke); + if (monitorField != null) // monitor: finally exit + { + LocalBuilder loc = null; + if (returnType != typeof(void)) + { + loc = il.DeclareLocal(returnType); + il.Emit(OpCodes.Stloc, loc); + } + + il.Emit(OpCodes.Leave, exc); + il.BeginFinallyBlock(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, monitorField); + il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit")); + il.EndExceptionBlock(); + + if (returnType != typeof(void)) + { + il.Emit(OpCodes.Ldloc, loc); + } + } + il.Emit(OpCodes.Ret); type.DefineMethodOverride(method, baseMethod); @@ -193,7 +255,7 @@ namespace BizHawk.Emulation.Common.BizInvoke /// /// create a method implementation that uses calli internally /// - private static Action ImplementMethodCalli(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName) + private static Action ImplementMethodCalli(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField) { var paramInfos = baseMethod.GetParameters(); var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray(); @@ -211,6 +273,16 @@ namespace BizHawk.Emulation.Common.BizInvoke var il = method.GetILGenerator(); + + Label exc = new Label(); + if (monitorField != null) // monitor: enter and then begin try + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, monitorField); + il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter")); + exc = il.BeginExceptionBlock(); + } + for (int i = 0; i < paramTypes.Length; i++) { // arg 0 is this, so + 1 @@ -220,6 +292,28 @@ namespace BizHawk.Emulation.Common.BizInvoke il.Emit(OpCodes.Ldfld, field); il.EmitCalli(OpCodes.Calli, nativeCall, returnType, nativeParamTypes.ToArray()); + if (monitorField != null) // monitor: finally exit + { + LocalBuilder loc = null; + if (returnType != typeof(void)) + { + loc = il.DeclareLocal(returnType); + il.Emit(OpCodes.Stloc, loc); + } + + il.Emit(OpCodes.Leave, exc); + il.BeginFinallyBlock(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, monitorField); + il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit")); + il.EndExceptionBlock(); + + if (returnType != typeof(void)) + { + il.Emit(OpCodes.Ldloc, loc); + } + } + // 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); diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs index 3b3aeff2f8..8660469aac 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx )] public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable, IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable - { + { LibGPGX Core; IDisposable NativeData;