Add some draft work for supporting sysv <-> msabi interop. I wonder if we'll ever use this? I'd put it on a separate branch but it would be merge hell.
This commit is contained in:
parent
d7423d45a0
commit
1292b27163
|
@ -73,7 +73,10 @@
|
|||
<Compile Include="BizInvoke\BizExvoker.cs" />
|
||||
<Compile Include="BizInvoke\BizInvoker.cs" />
|
||||
<Compile Include="BizInvoke\BizInvokeUtilities.cs" />
|
||||
<Compile Include="BizInvoke\CallingConventionAdapter.cs" />
|
||||
<Compile Include="BizInvoke\DynamicLibraryImportResolver.cs" />
|
||||
<Compile Include="BizInvoke\MemoryBlock.cs" />
|
||||
<Compile Include="BizInvoke\WaterboxUtils.cs" />
|
||||
<Compile Include="Buffer.cs" />
|
||||
<Compile Include="Colors.cs" />
|
||||
<Compile Include="CustomCollections.cs" />
|
||||
|
|
|
@ -1,150 +1,150 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public static class BizExvoker
|
||||
{
|
||||
/// <summary>
|
||||
/// the assembly that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly AssemblyBuilder ImplAssemblyBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// the module that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly ModuleBuilder ImplModuleBuilder;
|
||||
|
||||
static BizExvoker()
|
||||
{
|
||||
var aname = new AssemblyName("BizExvokeProxyAssembly");
|
||||
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
|
||||
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizExvokerModule");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// holds the delegate types for a type
|
||||
/// </summary>
|
||||
private class DelegateStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// the type that this storage was made for
|
||||
/// </summary>
|
||||
public Type OriginalType { get; }
|
||||
/// <summary>
|
||||
/// the type that the delegate types reside in
|
||||
/// </summary>
|
||||
public Type StorageType { get; }
|
||||
|
||||
public List<StoredDelegateInfo> DelegateTypes { get; } = new List<StoredDelegateInfo>();
|
||||
|
||||
public class StoredDelegateInfo
|
||||
{
|
||||
public MethodInfo Method { get; }
|
||||
public Type DelegateType { get; }
|
||||
public string EntryPointName { get; }
|
||||
public StoredDelegateInfo(MethodInfo method, Type delegateType, string entryPointName)
|
||||
{
|
||||
Method = method;
|
||||
DelegateType = delegateType;
|
||||
EntryPointName = entryPointName;
|
||||
}
|
||||
}
|
||||
|
||||
public DelegateStorage(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Select(m => new
|
||||
{
|
||||
Info = m,
|
||||
Attr = m.GetCustomAttributes(true).OfType<BizExportAttribute>().FirstOrDefault()
|
||||
})
|
||||
.Where(a => a.Attr != null)
|
||||
.ToList();
|
||||
|
||||
var typeBuilder = ImplModuleBuilder.DefineType(
|
||||
"Bizhawk.BizExvokeHolder" + type.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed);
|
||||
|
||||
foreach (var a in methods)
|
||||
{
|
||||
MethodBuilder unused;
|
||||
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr.CallingConvention, typeBuilder, out unused).CreateType();
|
||||
DelegateTypes.Add(new StoredDelegateInfo(a.Info, delegateType, a.Attr.EntryPoint ?? a.Info.Name));
|
||||
}
|
||||
StorageType = typeBuilder.CreateType();
|
||||
OriginalType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExvokerImpl : IImportResolver
|
||||
{
|
||||
private readonly Dictionary<string, IntPtr> EntryPoints = new Dictionary<string, IntPtr>();
|
||||
|
||||
private readonly List<Delegate> Delegates = new List<Delegate>();
|
||||
|
||||
public ExvokerImpl(object o, DelegateStorage d)
|
||||
{
|
||||
foreach (var sdt in d.DelegateTypes)
|
||||
{
|
||||
var del = Delegate.CreateDelegate(sdt.DelegateType, o, sdt.Method);
|
||||
Delegates.Add(del); // prevent garbage collection of the delegate, which would invalidate the pointer
|
||||
EntryPoints.Add(sdt.EntryPointName, Marshal.GetFunctionPointerForDelegate(del));
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
IntPtr ret;
|
||||
EntryPoints.TryGetValue(entryPoint, out ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<Type, DelegateStorage> Impls = new Dictionary<Type, DelegateStorage>();
|
||||
|
||||
|
||||
public static IImportResolver GetExvoker(object o)
|
||||
{
|
||||
DelegateStorage ds;
|
||||
lock (Impls)
|
||||
{
|
||||
var type = o.GetType();
|
||||
if (!Impls.TryGetValue(type, out ds))
|
||||
{
|
||||
ds = new DelegateStorage(type);
|
||||
Impls.Add(type, ds);
|
||||
}
|
||||
}
|
||||
|
||||
return new ExvokerImpl(o, ds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// mark an instance method to be exported by BizExvoker
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class BizExportAttribute : Attribute
|
||||
{
|
||||
public CallingConvention CallingConvention { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of entry point; if not given, the method's name is used
|
||||
/// </summary>
|
||||
public string EntryPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BizImportAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="c">unmanaged calling convention</param>
|
||||
public BizExportAttribute(CallingConvention c)
|
||||
{
|
||||
CallingConvention = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public static class BizExvoker
|
||||
{
|
||||
/// <summary>
|
||||
/// the assembly that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly AssemblyBuilder ImplAssemblyBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// the module that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly ModuleBuilder ImplModuleBuilder;
|
||||
|
||||
static BizExvoker()
|
||||
{
|
||||
var aname = new AssemblyName("BizExvokeProxyAssembly");
|
||||
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
|
||||
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizExvokerModule");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// holds the delegate types for a type
|
||||
/// </summary>
|
||||
private class DelegateStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// the type that this storage was made for
|
||||
/// </summary>
|
||||
public Type OriginalType { get; }
|
||||
/// <summary>
|
||||
/// the type that the delegate types reside in
|
||||
/// </summary>
|
||||
public Type StorageType { get; }
|
||||
|
||||
public List<StoredDelegateInfo> DelegateTypes { get; } = new List<StoredDelegateInfo>();
|
||||
|
||||
public class StoredDelegateInfo
|
||||
{
|
||||
public MethodInfo Method { get; }
|
||||
public Type DelegateType { get; }
|
||||
public string EntryPointName { get; }
|
||||
public StoredDelegateInfo(MethodInfo method, Type delegateType, string entryPointName)
|
||||
{
|
||||
Method = method;
|
||||
DelegateType = delegateType;
|
||||
EntryPointName = entryPointName;
|
||||
}
|
||||
}
|
||||
|
||||
public DelegateStorage(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Select(m => new
|
||||
{
|
||||
Info = m,
|
||||
Attr = m.GetCustomAttributes(true).OfType<BizExportAttribute>().FirstOrDefault()
|
||||
})
|
||||
.Where(a => a.Attr != null)
|
||||
.ToList();
|
||||
|
||||
var typeBuilder = ImplModuleBuilder.DefineType(
|
||||
"Bizhawk.BizExvokeHolder" + type.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed);
|
||||
|
||||
foreach (var a in methods)
|
||||
{
|
||||
MethodBuilder unused;
|
||||
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr.CallingConvention, typeBuilder, out unused).CreateType();
|
||||
DelegateTypes.Add(new StoredDelegateInfo(a.Info, delegateType, a.Attr.EntryPoint ?? a.Info.Name));
|
||||
}
|
||||
StorageType = typeBuilder.CreateType();
|
||||
OriginalType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExvokerImpl : IImportResolver
|
||||
{
|
||||
private readonly Dictionary<string, IntPtr> EntryPoints = new Dictionary<string, IntPtr>();
|
||||
|
||||
private readonly List<Delegate> Delegates = new List<Delegate>();
|
||||
|
||||
public ExvokerImpl(object o, DelegateStorage d, ICallingConventionAdapter a)
|
||||
{
|
||||
foreach (var sdt in d.DelegateTypes)
|
||||
{
|
||||
var del = Delegate.CreateDelegate(sdt.DelegateType, o, sdt.Method);
|
||||
Delegates.Add(del); // prevent garbage collection of the delegate, which would invalidate the pointer
|
||||
EntryPoints.Add(sdt.EntryPointName, a.GetFunctionPointerForDelegate(del));
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
IntPtr ret;
|
||||
EntryPoints.TryGetValue(entryPoint, out ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<Type, DelegateStorage> Impls = new Dictionary<Type, DelegateStorage>();
|
||||
|
||||
|
||||
public static IImportResolver GetExvoker(object o, ICallingConventionAdapter a)
|
||||
{
|
||||
DelegateStorage ds;
|
||||
lock (Impls)
|
||||
{
|
||||
var type = o.GetType();
|
||||
if (!Impls.TryGetValue(type, out ds))
|
||||
{
|
||||
ds = new DelegateStorage(type);
|
||||
Impls.Add(type, ds);
|
||||
}
|
||||
}
|
||||
|
||||
return new ExvokerImpl(o, ds, a);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// mark an instance method to be exported by BizExvoker
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class BizExportAttribute : Attribute
|
||||
{
|
||||
public CallingConvention CallingConvention { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of entry point; if not given, the method's name is used
|
||||
/// </summary>
|
||||
public string EntryPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BizImportAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="c">unmanaged calling convention</param>
|
||||
public BizExportAttribute(CallingConvention c)
|
||||
{
|
||||
CallingConvention = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,18 @@ namespace BizHawk.Common.BizInvoke
|
|||
private class InvokerImpl
|
||||
{
|
||||
public Type ImplType;
|
||||
public List<Action<object, IImportResolver>> Hooks;
|
||||
public List<Action<object, IImportResolver, ICallingConventionAdapter>> Hooks;
|
||||
public Action<object, IMonitor> ConnectMonitor;
|
||||
public Action<object, ICallingConventionAdapter> ConnectCallingConventionAdapter;
|
||||
|
||||
public object Create(IImportResolver dll, IMonitor monitor)
|
||||
public object Create(IImportResolver dll, IMonitor monitor, ICallingConventionAdapter adapter)
|
||||
{
|
||||
var ret = Activator.CreateInstance(ImplType);
|
||||
ConnectCallingConventionAdapter(ret, adapter);
|
||||
foreach (var f in Hooks)
|
||||
{
|
||||
f(ret, dll);
|
||||
f(ret, dll, adapter);
|
||||
}
|
||||
|
||||
ConnectMonitor?.Invoke(ret, monitor);
|
||||
return ret;
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// get an implementation proxy for an interop class
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class type that represents the DLL</typeparam>
|
||||
public static T GetInvoker<T>(IImportResolver dll)
|
||||
public static T GetInvoker<T>(IImportResolver dll, ICallingConventionAdapter adapter)
|
||||
where T : class
|
||||
{
|
||||
InvokerImpl impl;
|
||||
|
@ -78,10 +79,10 @@ namespace BizHawk.Common.BizInvoke
|
|||
throw new InvalidOperationException("Class was previously proxied with a monitor!");
|
||||
}
|
||||
|
||||
return (T)impl.Create(dll, null);
|
||||
return (T)impl.Create(dll, null, adapter);
|
||||
}
|
||||
|
||||
public static T GetInvoker<T>(IImportResolver dll, IMonitor monitor)
|
||||
public static T GetInvoker<T>(IImportResolver dll, IMonitor monitor, ICallingConventionAdapter adapter)
|
||||
where T : class
|
||||
{
|
||||
InvokerImpl impl;
|
||||
|
@ -100,7 +101,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
throw new InvalidOperationException("Class was previously proxied without a monitor!");
|
||||
}
|
||||
|
||||
return (T)impl.Create(dll, monitor);
|
||||
return (T)impl.Create(dll, monitor, adapter);
|
||||
}
|
||||
|
||||
private static InvokerImpl CreateProxy(Type baseType, bool monitor)
|
||||
|
@ -153,19 +154,21 @@ namespace BizHawk.Common.BizInvoke
|
|||
}
|
||||
|
||||
// hooks that will be run on the created proxy object
|
||||
var postCreateHooks = new List<Action<object, IImportResolver>>();
|
||||
var postCreateHooks = new List<Action<object, IImportResolver, ICallingConventionAdapter>>();
|
||||
|
||||
var type = ImplModuleBuilder.DefineType("Bizhawk.BizInvokeProxy" + baseType.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, baseType);
|
||||
|
||||
var monitorField = monitor ? type.DefineField("MonitorField", typeof(IMonitor), FieldAttributes.Public) : null;
|
||||
|
||||
var adapterField = type.DefineField("CallingConvention", typeof(ICallingConventionAdapter), FieldAttributes.Public);
|
||||
|
||||
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, monitorField)
|
||||
: ImplementMethodCalli(type, mi.Info, mi.Attr.CallingConvention, entryPointName, monitorField);
|
||||
: ImplementMethodCalli(type, mi.Info, mi.Attr.CallingConvention, entryPointName, monitorField, adapterField);
|
||||
|
||||
postCreateHooks.Add(hook);
|
||||
}
|
||||
|
@ -179,6 +182,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
{
|
||||
ret.ConnectMonitor = (o, m) => o.GetType().GetField(monitorField.Name).SetValue(o, m);
|
||||
}
|
||||
ret.ConnectCallingConventionAdapter = (o, a) => o.GetType().GetField(adapterField.Name).SetValue(o, a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -186,7 +190,8 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// <summary>
|
||||
/// create a method implementation that uses GetDelegateForFunctionPointer internally
|
||||
/// </summary>
|
||||
private static Action<object, IImportResolver> ImplementMethodDelegate(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField)
|
||||
private static Action<object, IImportResolver, ICallingConventionAdapter> ImplementMethodDelegate(
|
||||
TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField)
|
||||
{
|
||||
// create the delegate type
|
||||
MethodBuilder delegateInvoke;
|
||||
|
@ -196,6 +201,13 @@ namespace BizHawk.Common.BizInvoke
|
|||
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
|
||||
var returnType = baseMethod.ReturnType;
|
||||
|
||||
if (paramTypes.Concat(new[] { returnType }).Any(typeof(Delegate).IsAssignableFrom))
|
||||
{
|
||||
// this isn't a problem if CallingConventionAdapters.Waterbox is a no-op
|
||||
if (CallingConventionAdapters.Waterbox.GetType() != CallingConventionAdapters.Native.GetType())
|
||||
throw new InvalidOperationException("Compatibility call mode cannot use ICallingConventionAdapters!");
|
||||
}
|
||||
|
||||
// define a field on the class to hold the delegate
|
||||
var field = type.DefineField(
|
||||
"DelegateField" + baseMethod.Name,
|
||||
|
@ -255,10 +267,10 @@ namespace BizHawk.Common.BizInvoke
|
|||
|
||||
type.DefineMethodOverride(method, baseMethod);
|
||||
|
||||
return (o, dll) =>
|
||||
return (o, dll, adapter) =>
|
||||
{
|
||||
var entryPtr = dll.SafeResolve(entryPointName);
|
||||
var interopDelegate = Marshal.GetDelegateForFunctionPointer(entryPtr, delegateType.CreateType());
|
||||
var interopDelegate = adapter.GetDelegateForFunctionPointer(entryPtr, delegateType.CreateType());
|
||||
o.GetType().GetField(field.Name).SetValue(o, interopDelegate);
|
||||
};
|
||||
}
|
||||
|
@ -266,7 +278,9 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// <summary>
|
||||
/// create a method implementation that uses calli internally
|
||||
/// </summary>
|
||||
private static Action<object, IImportResolver> ImplementMethodCalli(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField)
|
||||
private static Action<object, IImportResolver, ICallingConventionAdapter> ImplementMethodCalli(
|
||||
TypeBuilder type, MethodInfo baseMethod,
|
||||
CallingConvention nativeCall, string entryPointName, FieldInfo monitorField, FieldInfo adapterField)
|
||||
{
|
||||
var paramInfos = baseMethod.GetParameters();
|
||||
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
|
||||
|
@ -304,7 +318,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
for (int i = 0; i < paramTypes.Length; i++)
|
||||
{
|
||||
// arg 0 is this, so + 1
|
||||
nativeParamTypes.Add(EmitParamterLoad(il, i + 1, paramTypes[i]));
|
||||
nativeParamTypes.Add(EmitParamterLoad(il, i + 1, paramTypes[i], adapterField));
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
|
@ -342,10 +356,11 @@ namespace BizHawk.Common.BizInvoke
|
|||
|
||||
type.DefineMethodOverride(method, baseMethod);
|
||||
|
||||
return (o, dll) =>
|
||||
return (o, dll, adapter) =>
|
||||
{
|
||||
var entryPtr = dll.SafeResolve(entryPointName);
|
||||
o.GetType().GetField(field.Name).SetValue(o, entryPtr);
|
||||
o.GetType().GetField(field.Name).SetValue(
|
||||
o, adapter.GetDepartureFunctionPointer(entryPtr, new ParameterInfo(returnType, paramTypes), o));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -394,7 +409,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// <summary>
|
||||
/// emit a single parameter load with unmanaged conversions
|
||||
/// </summary>
|
||||
private static Type EmitParamterLoad(ILGenerator il, int idx, Type type)
|
||||
private static Type EmitParamterLoad(ILGenerator il, int idx, Type type, FieldInfo adapterField)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
|
@ -460,13 +475,15 @@ namespace BizHawk.Common.BizInvoke
|
|||
|
||||
if (typeof(Delegate).IsAssignableFrom(type))
|
||||
{
|
||||
var mi = typeof(Marshal).GetMethod("GetFunctionPointerForDelegate", new[] { typeof(Delegate) });
|
||||
var mi = typeof(ICallingConventionAdapter).GetMethod("GetFunctionPointerForDelegate");
|
||||
var end = il.DefineLabel();
|
||||
var isNull = il.DefineLabel();
|
||||
|
||||
il.Emit(OpCodes.Ldarg, (short)idx);
|
||||
il.Emit(OpCodes.Brfalse, isNull);
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, adapterField);
|
||||
il.Emit(OpCodes.Ldarg, (short)idx);
|
||||
il.Emit(OpCodes.Call, mi);
|
||||
il.Emit(OpCodes.Br, end);
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
/// <summary>
|
||||
/// create interop delegates and function pointers for a particular calling convention
|
||||
/// </summary>
|
||||
public interface ICallingConventionAdapter
|
||||
{
|
||||
IntPtr GetFunctionPointerForDelegate(Delegate d);
|
||||
IntPtr GetArrivalFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime);
|
||||
|
||||
Delegate GetDelegateForFunctionPointer(IntPtr p, Type delegateType);
|
||||
IntPtr GetDepartureFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime);
|
||||
}
|
||||
|
||||
public static class CallingConventionAdapterExtensions
|
||||
{
|
||||
public static T GetDelegateForFunctionPointer<T>(this ICallingConventionAdapter a, IntPtr p)
|
||||
where T: class
|
||||
{
|
||||
return (T)(object)a.GetDelegateForFunctionPointer(p, typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public class ParameterInfo
|
||||
{
|
||||
public Type ReturnType { get; }
|
||||
public IReadOnlyList<Type> ParameterTypes { get; }
|
||||
|
||||
public ParameterInfo(Type returnType, IEnumerable<Type> parameterTypes)
|
||||
{
|
||||
ReturnType = returnType;
|
||||
ParameterTypes = parameterTypes.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public ParameterInfo(Type delegateType)
|
||||
{
|
||||
if (!typeof(Delegate).IsAssignableFrom(delegateType))
|
||||
throw new InvalidOperationException("Must be a delegate type!");
|
||||
var invoke = delegateType.GetMethod("Invoke");
|
||||
ReturnType = invoke.ReturnType;
|
||||
ParameterTypes = invoke.GetParameters().Select(p => p.ParameterType).ToList().AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallingConventionAdapters
|
||||
{
|
||||
private class NativeConvention : ICallingConventionAdapter
|
||||
{
|
||||
public IntPtr GetArrivalFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
public Delegate GetDelegateForFunctionPointer(IntPtr p, Type delegateType)
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer(p, delegateType);
|
||||
}
|
||||
|
||||
public IntPtr GetDepartureFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
public IntPtr GetFunctionPointerForDelegate(Delegate d)
|
||||
{
|
||||
return Marshal.GetFunctionPointerForDelegate(d);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// native (pass-through) calling convention
|
||||
/// </summary>
|
||||
public static ICallingConventionAdapter Native { get; } = new NativeConvention();
|
||||
|
||||
/// <summary>
|
||||
/// convention appropriate for waterbox guests
|
||||
/// </summary>
|
||||
public static ICallingConventionAdapter Waterbox { get; } =
|
||||
#if true
|
||||
new NativeConvention();
|
||||
#else
|
||||
new SysVHostMsGuest();
|
||||
#endif
|
||||
|
||||
private class SysVHostMsGuest : ICallingConventionAdapter
|
||||
{
|
||||
private const ulong Placeholder = 0xdeadbeeffeedface;
|
||||
private const byte Padding = 0x06;
|
||||
private const int BlockSize = 256;
|
||||
private static readonly byte[][] Depart =
|
||||
{
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x55, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x48, 0x89, 0xd1, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x4c, 0x89, 0x4d, 0xd0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd0, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
};
|
||||
private static readonly byte[][] Arrive =
|
||||
{
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x55, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x48, 0x89, 0xd1, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x4c, 0x89, 0x4d, 0xd0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd0, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
};
|
||||
|
||||
private static readonly int[] DepartPlaceholderIndices;
|
||||
private static readonly int[] ArrivePlaceholderIndices;
|
||||
|
||||
private static int FindPlaceholderIndex(byte[] data)
|
||||
{
|
||||
return Enumerable.Range(0, data.Length - 7)
|
||||
.Single(i => BitConverter.ToUInt64(data, i) == Placeholder);
|
||||
}
|
||||
|
||||
static SysVHostMsGuest()
|
||||
{
|
||||
DepartPlaceholderIndices = Depart.Select(FindPlaceholderIndex).ToArray();
|
||||
ArrivePlaceholderIndices = Arrive.Select(FindPlaceholderIndex).ToArray();
|
||||
if (Depart.Any(b => b.Length > BlockSize) || Arrive.Any(b => b.Length > BlockSize))
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private readonly MemoryBlock _memory;
|
||||
private readonly object _sync = new object();
|
||||
private readonly WeakReference[] _refs;
|
||||
|
||||
public SysVHostMsGuest()
|
||||
{
|
||||
int size = 4 * 1024 * 1024;
|
||||
_memory = new MemoryBlock((ulong)size);
|
||||
_memory.Activate();
|
||||
_refs = new WeakReference[size / BlockSize];
|
||||
}
|
||||
|
||||
private int FindFreeIndex()
|
||||
{
|
||||
for (int i = 0; i < _refs.Length; i++)
|
||||
{
|
||||
if (_refs[i] == null || !_refs[i].IsAlive)
|
||||
return i;
|
||||
}
|
||||
throw new InvalidOperationException("Out of Thunk memory");
|
||||
}
|
||||
|
||||
private static void VerifyParameter(Type type)
|
||||
{
|
||||
if (type == typeof(float) || type == typeof(double))
|
||||
throw new NotSupportedException("floating point not supported");
|
||||
if (type == typeof(void) || type.IsPrimitive)
|
||||
return;
|
||||
if (type.IsByRef || type.IsClass)
|
||||
return;
|
||||
throw new NotSupportedException("Unknown type. Possibly supported?");
|
||||
}
|
||||
|
||||
private static int VerifyDelegateSignature(ParameterInfo pp)
|
||||
{
|
||||
VerifyParameter(pp.ReturnType);
|
||||
foreach (var ppp in pp.ParameterTypes)
|
||||
VerifyParameter(ppp);
|
||||
return pp.ParameterTypes.Count;
|
||||
}
|
||||
|
||||
private void WriteThunk(byte[] data, int placeholderIndex, IntPtr p, int index)
|
||||
{
|
||||
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RW);
|
||||
var ss = _memory.GetStream(_memory.Start + (ulong)index * BlockSize, BlockSize, true);
|
||||
ss.Write(data, 0, data.Length);
|
||||
for (int i = data.Length; i < BlockSize; i++)
|
||||
ss.WriteByte(Padding);
|
||||
ss.Position = placeholderIndex;
|
||||
var bw = new BinaryWriter(ss);
|
||||
bw.Write((long)p);
|
||||
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RX);
|
||||
}
|
||||
|
||||
private IntPtr GetThunkAddress(int index)
|
||||
{
|
||||
return Z.US(_memory.Start + (ulong)index * BlockSize);
|
||||
}
|
||||
|
||||
private void SetLifetime(int index, object lifetime)
|
||||
{
|
||||
if (_refs[index] == null)
|
||||
_refs[index] = new WeakReference(lifetime);
|
||||
else
|
||||
_refs[index].Target = lifetime;
|
||||
}
|
||||
|
||||
public IntPtr GetFunctionPointerForDelegate(Delegate d)
|
||||
{
|
||||
return GetArrivalFunctionPointer(
|
||||
Marshal.GetFunctionPointerForDelegate(d), new ParameterInfo(d.GetType()), d);
|
||||
}
|
||||
|
||||
public IntPtr GetArrivalFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
var index = FindFreeIndex();
|
||||
var count = VerifyDelegateSignature(pp);
|
||||
WriteThunk(Arrive[count], ArrivePlaceholderIndices[count], p, index);
|
||||
SetLifetime(index, lifetime);
|
||||
return GetThunkAddress(index);
|
||||
}
|
||||
}
|
||||
|
||||
public Delegate GetDelegateForFunctionPointer(IntPtr p, Type delegateType)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
var index = FindFreeIndex();
|
||||
var count = VerifyDelegateSignature(new ParameterInfo(delegateType));
|
||||
WriteThunk(Depart[count], DepartPlaceholderIndices[count], p, index);
|
||||
var ret = Marshal.GetDelegateForFunctionPointer(GetThunkAddress(index), delegateType);
|
||||
SetLifetime(index, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr GetDepartureFunctionPointer(IntPtr p, ParameterInfo pp, object lifetime)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
var index = FindFreeIndex();
|
||||
var count = VerifyDelegateSignature(pp);
|
||||
WriteThunk(Depart[count], DepartPlaceholderIndices[count], p, index);
|
||||
SetLifetime(index, lifetime);
|
||||
return GetThunkAddress(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,45 +5,45 @@ using System.Text;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public sealed class MemoryBlock : IDisposable
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// starting address of the memory block
|
||||
/// </summary>
|
||||
public ulong Start { get; private set; }
|
||||
public ulong Start { get; private set; }
|
||||
/// <summary>
|
||||
/// total size of the memory block
|
||||
/// </summary>
|
||||
public ulong Size { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
/// <summary>
|
||||
/// ending address of the memory block; equal to start + size
|
||||
/// </summary>
|
||||
public ulong End { get; private set; }
|
||||
|
||||
public ulong End { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// handle returned by CreateFileMapping
|
||||
/// </summary>
|
||||
private IntPtr _handle;
|
||||
|
||||
private IntPtr _handle;
|
||||
|
||||
/// <summary>
|
||||
/// true if this is currently swapped in
|
||||
/// </summary>
|
||||
public bool Active { get; private set; }
|
||||
|
||||
public bool Active { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// stores last set memory protection value for each page
|
||||
/// </summary>
|
||||
private readonly Protection[] _pageData;
|
||||
|
||||
private readonly Protection[] _pageData;
|
||||
|
||||
/// <summary>
|
||||
/// snapshot for XOR buffer
|
||||
/// </summary>
|
||||
private byte[] _snapshot;
|
||||
|
||||
public byte[] XorHash { get; private set; }
|
||||
|
||||
public byte[] XorHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// get a page index within the block
|
||||
/// </summary>
|
||||
|
@ -53,16 +53,16 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
return (int)((addr - Start) >> WaterboxUtils.PageShift);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get a start address for a page index within the block
|
||||
/// </summary>
|
||||
private ulong GetStartAddr(int page)
|
||||
{
|
||||
return ((ulong)page << WaterboxUtils.PageShift) + Start;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// allocate size bytes at any address
|
||||
/// </summary>
|
||||
|
@ -70,8 +70,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
public MemoryBlock(ulong size)
|
||||
: this(0, size)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// allocate size bytes starting at a particular address
|
||||
/// </summary>
|
||||
|
@ -94,8 +94,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
End = start + size;
|
||||
Size = size;
|
||||
_pageData = new Protection[GetPage(End - 1) + 1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// activate the memory block, swapping it in at the specified address
|
||||
/// </summary>
|
||||
|
@ -110,8 +110,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
ProtectAll();
|
||||
Active = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// deactivate the memory block, removing it from RAM but leaving it immediately available to swap back in
|
||||
/// </summary>
|
||||
|
@ -122,16 +122,16 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (!Kernel32.UnmapViewOfFile(Z.US(Start)))
|
||||
throw new InvalidOperationException("UnmapViewOfFile() returned NULL");
|
||||
Active = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Memory protection constant
|
||||
/// </summary>
|
||||
public enum Protection : byte
|
||||
{
|
||||
None, R, RW, RX
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a stream that can be used to read or write from part of the block. Does not check for or change Protect()!
|
||||
/// </summary>
|
||||
|
@ -143,8 +143,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
return new MemoryViewStream(!writer, writer, (long)start, (long)length, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get a stream that can be used to read or write from part of the block.
|
||||
/// both reads and writes will be XORed against an earlier recorded snapshot
|
||||
|
@ -159,8 +159,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new InvalidOperationException("No snapshot taken!");
|
||||
|
||||
return new MemoryViewXorStream(!writer, writer, (long)start, (long)length, this, _snapshot, (long)(start - Start));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// take a snapshot of the entire memory block's contents, for use in GetXorStream
|
||||
/// </summary>
|
||||
|
@ -169,10 +169,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (_snapshot != null)
|
||||
throw new InvalidOperationException("Snapshot already taken");
|
||||
if (!Active)
|
||||
throw new InvalidOperationException("Not active");
|
||||
|
||||
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
|
||||
// that to complicate things
|
||||
throw new InvalidOperationException("Not active");
|
||||
|
||||
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
|
||||
// that to complicate things
|
||||
Kernel32.MemoryProtection old;
|
||||
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
||||
throw new InvalidOperationException("VirtualProtect() returned FALSE!");
|
||||
|
@ -198,8 +198,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
default: throw new ArgumentOutOfRangeException(nameof(prot));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// restore all recorded protections
|
||||
/// </summary>
|
||||
|
@ -219,8 +219,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
ps = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// set r/w/x protection on a portion of memory. rounded to encompassing pages
|
||||
/// </summary>
|
||||
|
@ -233,9 +233,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
|
||||
var p = GetKernelMemoryProtectionValue(prot);
|
||||
for (int i = pstart; i <= pend; i++)
|
||||
_pageData[i] = prot; // also store the value for later use
|
||||
|
||||
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
||||
_pageData[i] = prot; // also store the value for later use
|
||||
|
||||
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
||||
{
|
||||
var computedStart = WaterboxUtils.AlignDown(start);
|
||||
var computedEnd = WaterboxUtils.AlignUp(start + length);
|
||||
|
@ -374,12 +374,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
_initial = initial;
|
||||
_offset = (int)offset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the initial data to XOR against for both reading and writing
|
||||
/// </summary>
|
||||
private readonly byte[] _initial;
|
||||
private readonly byte[] _initial;
|
||||
/// <summary>
|
||||
/// offset into the XOR data that this stream is representing
|
||||
/// </summary>
|
||||
|
@ -399,17 +399,17 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (count < 0 || count + offset > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
if (count > Length - pos)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
// is mutating the buffer passed to Stream.Write kosher?
|
||||
throw new ArgumentOutOfRangeException();
|
||||
// is mutating the buffer passed to Stream.Write kosher?
|
||||
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
||||
base.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
private static unsafe void XorTransform(byte[] source, int sourceOffset, byte[] dest, int destOffset, int length)
|
||||
{
|
||||
// we don't do any bounds check because MemoryViewStream.Read and MemoryViewXorStream.Write already did it
|
||||
|
||||
// TODO: C compilers can make this pretty snappy, but can the C# jitter? Or do we need intrinsics
|
||||
{
|
||||
// we don't do any bounds check because MemoryViewStream.Read and MemoryViewXorStream.Write already did it
|
||||
|
||||
// TODO: C compilers can make this pretty snappy, but can the C# jitter? Or do we need intrinsics
|
||||
fixed (byte* _s = source, _d = dest)
|
||||
{
|
||||
byte* s = _s + sourceOffset;
|
|
@ -0,0 +1,156 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public static class WaterboxUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// copy `len` bytes from `src` to `dest`
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="dst"></param>
|
||||
/// <param name="len"></param>
|
||||
public static void CopySome(Stream src, Stream dst, long len)
|
||||
{
|
||||
var buff = new byte[4096];
|
||||
while (len > 0)
|
||||
{
|
||||
int r = src.Read(buff, 0, (int)Math.Min(len, 4096));
|
||||
dst.Write(buff, 0, r);
|
||||
len -= r;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
using (var h = SHA1.Create())
|
||||
{
|
||||
return h.ComputeHash(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Hash(Stream s)
|
||||
{
|
||||
using (var h = SHA1.Create())
|
||||
{
|
||||
return h.ComputeHash(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void ZeroMemory(IntPtr mem, long length)
|
||||
{
|
||||
byte* p = (byte*)mem;
|
||||
byte* end = p + length;
|
||||
while (p < end)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static long Timestamp()
|
||||
{
|
||||
return DateTime.UtcNow.Ticks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// system page size
|
||||
/// </summary>
|
||||
public static int PageSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// bitshift corresponding to PageSize
|
||||
/// </summary>
|
||||
public static int PageShift { get; private set; }
|
||||
/// <summary>
|
||||
/// bitmask corresponding to PageSize
|
||||
/// </summary>
|
||||
public static ulong PageMask { get; private set; }
|
||||
|
||||
static WaterboxUtils()
|
||||
{
|
||||
int p = PageSize = Environment.SystemPageSize;
|
||||
while (p != 1)
|
||||
{
|
||||
p >>= 1;
|
||||
PageShift++;
|
||||
}
|
||||
PageMask = (ulong)(PageSize - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if addr is aligned
|
||||
/// </summary>
|
||||
public static bool Aligned(ulong addr)
|
||||
{
|
||||
return (addr & PageMask) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// align address down to previous page boundary
|
||||
/// </summary>
|
||||
public static ulong AlignDown(ulong addr)
|
||||
{
|
||||
return addr & ~PageMask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// align address up to next page boundary
|
||||
/// </summary>
|
||||
public static ulong AlignUp(ulong addr)
|
||||
{
|
||||
return ((addr - 1) | PageMask) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// return the minimum number of pages needed to hold size
|
||||
/// </summary>
|
||||
public static int PagesNeeded(ulong size)
|
||||
{
|
||||
return (int)((size + PageMask) >> PageShift);
|
||||
}
|
||||
}
|
||||
|
||||
// C# is annoying: arithmetic operators for native ints are not exposed.
|
||||
// So we store them as long/ulong instead in many places, and use these helpers
|
||||
// to convert to IntPtr when needed
|
||||
|
||||
public static class Z
|
||||
{
|
||||
public static IntPtr US(ulong l)
|
||||
{
|
||||
if (IntPtr.Size == 8)
|
||||
return (IntPtr)(long)l;
|
||||
else
|
||||
return (IntPtr)(int)l;
|
||||
}
|
||||
|
||||
public static UIntPtr UU(ulong l)
|
||||
{
|
||||
if (UIntPtr.Size == 8)
|
||||
return (UIntPtr)l;
|
||||
else
|
||||
return (UIntPtr)(uint)l;
|
||||
}
|
||||
|
||||
public static IntPtr SS(long l)
|
||||
{
|
||||
if (IntPtr.Size == 8)
|
||||
return (IntPtr)l;
|
||||
else
|
||||
return (IntPtr)(int)l;
|
||||
}
|
||||
|
||||
public static UIntPtr SU(long l)
|
||||
{
|
||||
if (UIntPtr.Size == 8)
|
||||
return (UIntPtr)(ulong)l;
|
||||
else
|
||||
return (UIntPtr)(uint)l;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1278,7 +1278,6 @@
|
|||
<Compile Include="Waterbox\Heap.cs" />
|
||||
<Compile Include="Waterbox\LibWaterboxCore.cs" />
|
||||
<Compile Include="Waterbox\MapHeap.cs" />
|
||||
<Compile Include="Waterbox\MemoryBlock.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Sound\CDAudio.cs" />
|
||||
<Compile Include="Sound\HuC6280PSG.cs" />
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
PlainHeapSizeKB = 16 * 1024,
|
||||
MmapHeapSizeKB = 32 * 1024
|
||||
});
|
||||
_pizza = BizInvoker.GetInvoker<LibPizza>(_exe, _exe);
|
||||
_pizza = BizInvoker.GetInvoker<LibPizza>(_exe, _exe, CallingConventionAdapters.Waterbox);
|
||||
if (!_pizza.Init(rom, rom.Length))
|
||||
{
|
||||
throw new InvalidOperationException("Core rejected the rom!");
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
|||
static QuickNES()
|
||||
{
|
||||
Resolver = new DynamicLibraryImportResolver(LibQuickNES.dllname);
|
||||
QN = BizInvoker.GetInvoker<LibQuickNES>(Resolver);
|
||||
QN = BizInvoker.GetInvoker<LibQuickNES>(Resolver, CallingConventionAdapters.Native);
|
||||
}
|
||||
|
||||
[CoreConstructor("NES")]
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
// Marshal checks that function pointers passed to GetDelegateForFunctionPointer are
|
||||
// _currently_ valid when created, even though they don't need to be valid until
|
||||
// the delegate is later invoked. so GetInvoker needs to be acquired within a lock.
|
||||
_core = BizInvoker.GetInvoker<CoreImpl>(_exe, _exe);
|
||||
_core = BizInvoker.GetInvoker<CoreImpl>(_exe, _exe, CallingConventionAdapters.Waterbox);
|
||||
_comm = (CommStruct*)_core.DllInit().ToPointer();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,348 +1,348 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Sound;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.SNK
|
||||
{
|
||||
[CoreAttributes("Dual NeoPop", "Thomas Klausner and natt", true, false, "0.9.44.1",
|
||||
"https://mednafen.github.io/releases/", false)]
|
||||
public class DualNeoGeoPort : IEmulator
|
||||
{
|
||||
private NeoGeoPort _left;
|
||||
private NeoGeoPort _right;
|
||||
private readonly BasicServiceProvider _serviceProvider;
|
||||
private bool _disposed = false;
|
||||
private readonly DualSyncSound _soundProvider;
|
||||
private readonly SideBySideVideo _videoProvider;
|
||||
private readonly LinkInterop _leftEnd;
|
||||
private readonly LinkInterop _rightEnd;
|
||||
private readonly LinkCable _linkCable;
|
||||
|
||||
[CoreConstructor("DNGP")]
|
||||
public DualNeoGeoPort(CoreComm comm, byte[] rom, bool deterministic)
|
||||
{
|
||||
CoreComm = comm;
|
||||
_left = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, PeRunner.CanonicalStart);
|
||||
_right = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, PeRunner.AlternateStart);
|
||||
_linkCable = new LinkCable();
|
||||
_leftEnd = new LinkInterop(_left, _linkCable.LeftIn, _linkCable.LeftOut);
|
||||
_rightEnd = new LinkInterop(_right, _linkCable.RightIn, _linkCable.RightOut);
|
||||
|
||||
|
||||
_serviceProvider = new BasicServiceProvider(this);
|
||||
_soundProvider = new DualSyncSound(_left, _right);
|
||||
_serviceProvider.Register<ISoundProvider>(_soundProvider);
|
||||
_videoProvider = new SideBySideVideo(_left, _right);
|
||||
_serviceProvider.Register<IVideoProvider>(_videoProvider);
|
||||
}
|
||||
|
||||
public void FrameAdvance(IController controller, bool render, bool rendersound = true)
|
||||
{
|
||||
var t1 = Task.Run(() =>
|
||||
{
|
||||
_left.FrameAdvance(new PrefixController(controller, "P1 "), render, rendersound);
|
||||
_leftEnd.SignalEndOfFrame();
|
||||
});
|
||||
var t2 = Task.Run(() =>
|
||||
{
|
||||
_right.FrameAdvance(new PrefixController(controller, "P2 "), render, rendersound);
|
||||
_rightEnd.SignalEndOfFrame();
|
||||
});
|
||||
var t3 = Task.Run(() =>
|
||||
{
|
||||
_linkCable.RunFrame();
|
||||
});
|
||||
Task.WaitAll(t1, t2, t3);
|
||||
Frame++;
|
||||
_soundProvider.Fetch();
|
||||
_videoProvider.Fetch();
|
||||
}
|
||||
|
||||
#region link cable
|
||||
|
||||
private class LinkCable
|
||||
{
|
||||
public readonly BlockingCollection<LinkRequest> LeftIn = new BlockingCollection<LinkRequest>();
|
||||
public readonly BlockingCollection<LinkResult> LeftOut = new BlockingCollection<LinkResult>();
|
||||
public readonly BlockingCollection<LinkRequest> RightIn = new BlockingCollection<LinkRequest>();
|
||||
public readonly BlockingCollection<LinkResult> RightOut = new BlockingCollection<LinkResult>();
|
||||
|
||||
private readonly Queue<byte> _leftData = new Queue<byte>();
|
||||
private readonly Queue<byte> _rightData = new Queue<byte>();
|
||||
|
||||
public void RunFrame()
|
||||
{
|
||||
LinkRequest l = LeftIn.Take();
|
||||
LinkRequest r = RightIn.Take();
|
||||
while (true)
|
||||
{
|
||||
switch (l.RequestType)
|
||||
{
|
||||
case LinkRequest.RequestTypes.EndOfFrame:
|
||||
if (r.RequestType == LinkRequest.RequestTypes.EndOfFrame)
|
||||
{
|
||||
Console.WriteLine("\nEnd of Frame {0} {1}", _leftData.Count, _rightData.Count);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LinkRequest.RequestTypes.Write:
|
||||
Console.Write("LW ");
|
||||
_leftData.Enqueue(l.Data);
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
case LinkRequest.RequestTypes.Read:
|
||||
case LinkRequest.RequestTypes.Poll:
|
||||
if (_rightData.Count > 0)
|
||||
{
|
||||
if (l.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("LR ");
|
||||
LeftOut.Add(new LinkResult
|
||||
{
|
||||
Data = l.RequestType == LinkRequest.RequestTypes.Read ? _rightData.Dequeue() : _rightData.Peek(),
|
||||
Return = true
|
||||
});
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
}
|
||||
else if (r.RequestType != LinkRequest.RequestTypes.Write)
|
||||
{
|
||||
if (l.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("L! ");
|
||||
LeftOut.Add(new LinkResult
|
||||
{
|
||||
Data = l.Data,
|
||||
Return = false
|
||||
});
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (r.RequestType)
|
||||
{
|
||||
case LinkRequest.RequestTypes.Write:
|
||||
Console.Write("RW ");
|
||||
_rightData.Enqueue(r.Data);
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
case LinkRequest.RequestTypes.Read:
|
||||
case LinkRequest.RequestTypes.Poll:
|
||||
if (_leftData.Count > 0)
|
||||
{
|
||||
if (r.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("RR ");
|
||||
RightOut.Add(new LinkResult
|
||||
{
|
||||
Data = r.RequestType == LinkRequest.RequestTypes.Read ? _leftData.Dequeue() : _leftData.Peek(),
|
||||
Return = true
|
||||
});
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
}
|
||||
else if (l.RequestType != LinkRequest.RequestTypes.Write)
|
||||
{
|
||||
if (r.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("R! ");
|
||||
RightOut.Add(new LinkResult
|
||||
{
|
||||
Data = r.Data,
|
||||
Return = false
|
||||
});
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct LinkRequest
|
||||
{
|
||||
public enum RequestTypes : byte
|
||||
{
|
||||
Read,
|
||||
Poll,
|
||||
Write,
|
||||
EndOfFrame
|
||||
}
|
||||
public RequestTypes RequestType;
|
||||
public byte Data;
|
||||
}
|
||||
public struct LinkResult
|
||||
{
|
||||
public byte Data;
|
||||
public bool Return;
|
||||
}
|
||||
|
||||
private unsafe class LinkInterop
|
||||
{
|
||||
private readonly BlockingCollection<LinkRequest> _push;
|
||||
private readonly BlockingCollection<LinkResult> _pull;
|
||||
private NeoGeoPort _core;
|
||||
private readonly IntPtr _readcb;
|
||||
private readonly IntPtr _pollcb;
|
||||
private readonly IntPtr _writecb;
|
||||
private readonly IImportResolver _exporter;
|
||||
|
||||
public LinkInterop(NeoGeoPort core, BlockingCollection<LinkRequest> push, BlockingCollection<LinkResult> pull)
|
||||
{
|
||||
_core = core;
|
||||
_push = push;
|
||||
_pull = pull;
|
||||
_exporter = BizExvoker.GetExvoker(this);
|
||||
_readcb = _exporter.SafeResolve("CommsReadCallback");
|
||||
_pollcb = _exporter.SafeResolve("CommsPollCallback");
|
||||
_writecb = _exporter.SafeResolve("CommsWriteCallback");
|
||||
ConnectPointers();
|
||||
}
|
||||
|
||||
private void ConnectPointers()
|
||||
{
|
||||
_core._neopop.SetCommsCallbacks(_readcb, _pollcb, _writecb);
|
||||
}
|
||||
|
||||
private bool CommsPollNoBuffer()
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Poll
|
||||
});
|
||||
return _pull.Take().Return;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public bool CommsReadCallback(byte* buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return CommsPollNoBuffer();
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Read,
|
||||
Data = *buffer
|
||||
});
|
||||
var r = _pull.Take();
|
||||
*buffer = r.Data;
|
||||
return r.Return;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public bool CommsPollCallback(byte* buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return CommsPollNoBuffer();
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Poll,
|
||||
Data = *buffer
|
||||
});
|
||||
var r = _pull.Take();
|
||||
*buffer = r.Data;
|
||||
return r.Return;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public void CommsWriteCallback(byte data)
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Write,
|
||||
Data = data
|
||||
});
|
||||
}
|
||||
|
||||
public void SignalEndOfFrame()
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.EndOfFrame
|
||||
});
|
||||
}
|
||||
|
||||
public void PostLoadState()
|
||||
{
|
||||
ConnectPointers();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class PrefixController : IController
|
||||
{
|
||||
public PrefixController(IController controller, string prefix)
|
||||
{
|
||||
_controller = controller;
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
private readonly IController _controller;
|
||||
private readonly string _prefix;
|
||||
|
||||
public ControllerDefinition Definition => null;
|
||||
|
||||
public float GetFloat(string name)
|
||||
{
|
||||
return _controller.GetFloat(_prefix + name);
|
||||
}
|
||||
|
||||
public bool IsPressed(string button)
|
||||
{
|
||||
return _controller.IsPressed(_prefix + button);
|
||||
}
|
||||
}
|
||||
|
||||
public ControllerDefinition ControllerDefinition => DualNeoGeoPortController;
|
||||
|
||||
private static readonly ControllerDefinition DualNeoGeoPortController = new ControllerDefinition
|
||||
{
|
||||
BoolButtons =
|
||||
{
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 Option", "P1 Power",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 A", "P2 B", "P2 Option", "P2 Power"
|
||||
},
|
||||
Name = "Dual NeoGeo Portable Controller"
|
||||
};
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
|
||||
public int Frame { get; private set; }
|
||||
|
||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||
|
||||
public CoreComm CoreComm { get; }
|
||||
|
||||
public bool DeterministicEmulation => _left.DeterministicEmulation && _right.DeterministicEmulation;
|
||||
|
||||
public string SystemId => "DNGP";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_left.Dispose();
|
||||
_right.Dispose();
|
||||
_left = null;
|
||||
_right = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Sound;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.SNK
|
||||
{
|
||||
[CoreAttributes("Dual NeoPop", "Thomas Klausner and natt", true, false, "0.9.44.1",
|
||||
"https://mednafen.github.io/releases/", false)]
|
||||
public class DualNeoGeoPort : IEmulator
|
||||
{
|
||||
private NeoGeoPort _left;
|
||||
private NeoGeoPort _right;
|
||||
private readonly BasicServiceProvider _serviceProvider;
|
||||
private bool _disposed = false;
|
||||
private readonly DualSyncSound _soundProvider;
|
||||
private readonly SideBySideVideo _videoProvider;
|
||||
private readonly LinkInterop _leftEnd;
|
||||
private readonly LinkInterop _rightEnd;
|
||||
private readonly LinkCable _linkCable;
|
||||
|
||||
[CoreConstructor("DNGP")]
|
||||
public DualNeoGeoPort(CoreComm comm, byte[] rom, bool deterministic)
|
||||
{
|
||||
CoreComm = comm;
|
||||
_left = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, PeRunner.CanonicalStart);
|
||||
_right = new NeoGeoPort(comm, rom, new NeoGeoPort.SyncSettings { Language = LibNeoGeoPort.Language.English }, deterministic, PeRunner.AlternateStart);
|
||||
_linkCable = new LinkCable();
|
||||
_leftEnd = new LinkInterop(_left, _linkCable.LeftIn, _linkCable.LeftOut);
|
||||
_rightEnd = new LinkInterop(_right, _linkCable.RightIn, _linkCable.RightOut);
|
||||
|
||||
|
||||
_serviceProvider = new BasicServiceProvider(this);
|
||||
_soundProvider = new DualSyncSound(_left, _right);
|
||||
_serviceProvider.Register<ISoundProvider>(_soundProvider);
|
||||
_videoProvider = new SideBySideVideo(_left, _right);
|
||||
_serviceProvider.Register<IVideoProvider>(_videoProvider);
|
||||
}
|
||||
|
||||
public void FrameAdvance(IController controller, bool render, bool rendersound = true)
|
||||
{
|
||||
var t1 = Task.Run(() =>
|
||||
{
|
||||
_left.FrameAdvance(new PrefixController(controller, "P1 "), render, rendersound);
|
||||
_leftEnd.SignalEndOfFrame();
|
||||
});
|
||||
var t2 = Task.Run(() =>
|
||||
{
|
||||
_right.FrameAdvance(new PrefixController(controller, "P2 "), render, rendersound);
|
||||
_rightEnd.SignalEndOfFrame();
|
||||
});
|
||||
var t3 = Task.Run(() =>
|
||||
{
|
||||
_linkCable.RunFrame();
|
||||
});
|
||||
Task.WaitAll(t1, t2, t3);
|
||||
Frame++;
|
||||
_soundProvider.Fetch();
|
||||
_videoProvider.Fetch();
|
||||
}
|
||||
|
||||
#region link cable
|
||||
|
||||
private class LinkCable
|
||||
{
|
||||
public readonly BlockingCollection<LinkRequest> LeftIn = new BlockingCollection<LinkRequest>();
|
||||
public readonly BlockingCollection<LinkResult> LeftOut = new BlockingCollection<LinkResult>();
|
||||
public readonly BlockingCollection<LinkRequest> RightIn = new BlockingCollection<LinkRequest>();
|
||||
public readonly BlockingCollection<LinkResult> RightOut = new BlockingCollection<LinkResult>();
|
||||
|
||||
private readonly Queue<byte> _leftData = new Queue<byte>();
|
||||
private readonly Queue<byte> _rightData = new Queue<byte>();
|
||||
|
||||
public void RunFrame()
|
||||
{
|
||||
LinkRequest l = LeftIn.Take();
|
||||
LinkRequest r = RightIn.Take();
|
||||
while (true)
|
||||
{
|
||||
switch (l.RequestType)
|
||||
{
|
||||
case LinkRequest.RequestTypes.EndOfFrame:
|
||||
if (r.RequestType == LinkRequest.RequestTypes.EndOfFrame)
|
||||
{
|
||||
Console.WriteLine("\nEnd of Frame {0} {1}", _leftData.Count, _rightData.Count);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LinkRequest.RequestTypes.Write:
|
||||
Console.Write("LW ");
|
||||
_leftData.Enqueue(l.Data);
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
case LinkRequest.RequestTypes.Read:
|
||||
case LinkRequest.RequestTypes.Poll:
|
||||
if (_rightData.Count > 0)
|
||||
{
|
||||
if (l.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("LR ");
|
||||
LeftOut.Add(new LinkResult
|
||||
{
|
||||
Data = l.RequestType == LinkRequest.RequestTypes.Read ? _rightData.Dequeue() : _rightData.Peek(),
|
||||
Return = true
|
||||
});
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
}
|
||||
else if (r.RequestType != LinkRequest.RequestTypes.Write)
|
||||
{
|
||||
if (l.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("L! ");
|
||||
LeftOut.Add(new LinkResult
|
||||
{
|
||||
Data = l.Data,
|
||||
Return = false
|
||||
});
|
||||
l = LeftIn.Take();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (r.RequestType)
|
||||
{
|
||||
case LinkRequest.RequestTypes.Write:
|
||||
Console.Write("RW ");
|
||||
_rightData.Enqueue(r.Data);
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
case LinkRequest.RequestTypes.Read:
|
||||
case LinkRequest.RequestTypes.Poll:
|
||||
if (_leftData.Count > 0)
|
||||
{
|
||||
if (r.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("RR ");
|
||||
RightOut.Add(new LinkResult
|
||||
{
|
||||
Data = r.RequestType == LinkRequest.RequestTypes.Read ? _leftData.Dequeue() : _leftData.Peek(),
|
||||
Return = true
|
||||
});
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
}
|
||||
else if (l.RequestType != LinkRequest.RequestTypes.Write)
|
||||
{
|
||||
if (r.RequestType == LinkRequest.RequestTypes.Read)
|
||||
Console.Write("R! ");
|
||||
RightOut.Add(new LinkResult
|
||||
{
|
||||
Data = r.Data,
|
||||
Return = false
|
||||
});
|
||||
r = RightIn.Take();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct LinkRequest
|
||||
{
|
||||
public enum RequestTypes : byte
|
||||
{
|
||||
Read,
|
||||
Poll,
|
||||
Write,
|
||||
EndOfFrame
|
||||
}
|
||||
public RequestTypes RequestType;
|
||||
public byte Data;
|
||||
}
|
||||
public struct LinkResult
|
||||
{
|
||||
public byte Data;
|
||||
public bool Return;
|
||||
}
|
||||
|
||||
private unsafe class LinkInterop
|
||||
{
|
||||
private readonly BlockingCollection<LinkRequest> _push;
|
||||
private readonly BlockingCollection<LinkResult> _pull;
|
||||
private NeoGeoPort _core;
|
||||
private readonly IntPtr _readcb;
|
||||
private readonly IntPtr _pollcb;
|
||||
private readonly IntPtr _writecb;
|
||||
private readonly IImportResolver _exporter;
|
||||
|
||||
public LinkInterop(NeoGeoPort core, BlockingCollection<LinkRequest> push, BlockingCollection<LinkResult> pull)
|
||||
{
|
||||
_core = core;
|
||||
_push = push;
|
||||
_pull = pull;
|
||||
_exporter = BizExvoker.GetExvoker(this, CallingConventionAdapters.Waterbox);
|
||||
_readcb = _exporter.SafeResolve("CommsReadCallback");
|
||||
_pollcb = _exporter.SafeResolve("CommsPollCallback");
|
||||
_writecb = _exporter.SafeResolve("CommsWriteCallback");
|
||||
ConnectPointers();
|
||||
}
|
||||
|
||||
private void ConnectPointers()
|
||||
{
|
||||
_core._neopop.SetCommsCallbacks(_readcb, _pollcb, _writecb);
|
||||
}
|
||||
|
||||
private bool CommsPollNoBuffer()
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Poll
|
||||
});
|
||||
return _pull.Take().Return;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public bool CommsReadCallback(byte* buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return CommsPollNoBuffer();
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Read,
|
||||
Data = *buffer
|
||||
});
|
||||
var r = _pull.Take();
|
||||
*buffer = r.Data;
|
||||
return r.Return;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public bool CommsPollCallback(byte* buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return CommsPollNoBuffer();
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Poll,
|
||||
Data = *buffer
|
||||
});
|
||||
var r = _pull.Take();
|
||||
*buffer = r.Data;
|
||||
return r.Return;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl)]
|
||||
public void CommsWriteCallback(byte data)
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.Write,
|
||||
Data = data
|
||||
});
|
||||
}
|
||||
|
||||
public void SignalEndOfFrame()
|
||||
{
|
||||
_push.Add(new LinkRequest
|
||||
{
|
||||
RequestType = LinkRequest.RequestTypes.EndOfFrame
|
||||
});
|
||||
}
|
||||
|
||||
public void PostLoadState()
|
||||
{
|
||||
ConnectPointers();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class PrefixController : IController
|
||||
{
|
||||
public PrefixController(IController controller, string prefix)
|
||||
{
|
||||
_controller = controller;
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
private readonly IController _controller;
|
||||
private readonly string _prefix;
|
||||
|
||||
public ControllerDefinition Definition => null;
|
||||
|
||||
public float GetFloat(string name)
|
||||
{
|
||||
return _controller.GetFloat(_prefix + name);
|
||||
}
|
||||
|
||||
public bool IsPressed(string button)
|
||||
{
|
||||
return _controller.IsPressed(_prefix + button);
|
||||
}
|
||||
}
|
||||
|
||||
public ControllerDefinition ControllerDefinition => DualNeoGeoPortController;
|
||||
|
||||
private static readonly ControllerDefinition DualNeoGeoPortController = new ControllerDefinition
|
||||
{
|
||||
BoolButtons =
|
||||
{
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 Option", "P1 Power",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 A", "P2 B", "P2 Option", "P2 Power"
|
||||
},
|
||||
Name = "Dual NeoGeo Portable Controller"
|
||||
};
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
|
||||
public int Frame { get; private set; }
|
||||
|
||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||
|
||||
public CoreComm CoreComm { get; }
|
||||
|
||||
public bool DeterministicEmulation => _left.DeterministicEmulation && _right.DeterministicEmulation;
|
||||
|
||||
public string SystemId => "DNGP";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_left.Dispose();
|
||||
_right.Dispose();
|
||||
_left = null;
|
||||
_right = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
|||
|
||||
using (_elf.EnterExit())
|
||||
{
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(_elf, _elf);
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(_elf, _elf, CallingConventionAdapters.Waterbox);
|
||||
_syncSettings = (GPGXSyncSettings)syncSettings ?? new GPGXSyncSettings();
|
||||
_settings = (GPGXSettings)settings ?? new GPGXSettings();
|
||||
|
||||
|
|
|
@ -1,487 +1,488 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using ELFSharp.ELF;
|
||||
using ELFSharp.ELF.Sections;
|
||||
using ELFSharp.ELF.Segments;
|
||||
using System.Reflection;
|
||||
using BizHawk.Common;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable
|
||||
{
|
||||
// TODO: a lot of things only work with our elves and aren't fully generalized
|
||||
|
||||
private ELF<long> _elf;
|
||||
private byte[] _elfhash;
|
||||
|
||||
/// <summary>
|
||||
/// executable is loaded here
|
||||
/// </summary>
|
||||
private MemoryBlock _base;
|
||||
/// <summary>
|
||||
/// standard malloc() heap
|
||||
/// </summary>
|
||||
private Heap _heap;
|
||||
|
||||
/// <summary>
|
||||
/// sealed heap (writable only during init)
|
||||
/// </summary>
|
||||
private Heap _sealedheap;
|
||||
|
||||
/// <summary>
|
||||
/// invisible heap (not savestated, use with care)
|
||||
/// </summary>
|
||||
private Heap _invisibleheap;
|
||||
|
||||
private long _loadoffset;
|
||||
private Dictionary<string, SymbolEntry<long>> _symdict;
|
||||
private List<SymbolEntry<long>> _symlist;
|
||||
|
||||
/// <summary>
|
||||
/// everything to clean up at dispose time
|
||||
/// </summary>
|
||||
private List<IDisposable> _disposeList = new List<IDisposable>();
|
||||
|
||||
private ulong GetHeapStart(ulong prevend)
|
||||
{
|
||||
// if relocatable, we won't have constant pointers, so put the heap anywhere
|
||||
// otherwise, put the heap at a canonical location aligned 1MB from the end of the elf, then incremented 16MB
|
||||
ulong heapstart = HasRelocations() ? 0 : ((prevend - 1) | 0xfffff) + 0x1000001;
|
||||
return heapstart;
|
||||
}
|
||||
|
||||
public ElfRunner(string filename, long heapsize, long sealedheapsize, long invisibleheapsize)
|
||||
{
|
||||
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
_elfhash = WaterboxUtils.Hash(fs);
|
||||
}
|
||||
|
||||
// todo: hack up this baby to take Streams
|
||||
_elf = ELFReader.Load<long>(filename);
|
||||
|
||||
var loadsegs = _elf.Segments.Where(s => s.Type == SegmentType.Load);
|
||||
|
||||
long orig_start = loadsegs.Min(s => s.Address);
|
||||
orig_start &= ~(Environment.SystemPageSize - 1);
|
||||
long orig_end = loadsegs.Max(s => s.Address + s.Size);
|
||||
if (HasRelocations())
|
||||
{
|
||||
_base = new MemoryBlock((ulong)(orig_end - orig_start));
|
||||
_loadoffset = (long)_base.Start - orig_start;
|
||||
Initialize(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Initialize((ulong)orig_start);
|
||||
_base = new MemoryBlock((ulong)orig_start, (ulong)(orig_end - orig_start));
|
||||
_loadoffset = 0;
|
||||
Enter();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_disposeList.Add(_base);
|
||||
AddMemoryBlock(_base);
|
||||
_base.Activate();
|
||||
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.RW);
|
||||
|
||||
foreach (var seg in loadsegs)
|
||||
{
|
||||
var data = seg.GetContents();
|
||||
Marshal.Copy(data, 0, Z.SS(seg.Address + _loadoffset), data.Length);
|
||||
}
|
||||
RegisterSymbols();
|
||||
ProcessRelocations();
|
||||
|
||||
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.R);
|
||||
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
|
||||
{
|
||||
if ((sec.Flags & SectionFlags.Executable) != 0)
|
||||
_base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RX);
|
||||
else if ((sec.Flags & SectionFlags.Writable) != 0)
|
||||
_base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RW);
|
||||
}
|
||||
|
||||
ulong end = _base.End;
|
||||
|
||||
if (heapsize > 0)
|
||||
{
|
||||
_heap = new Heap(GetHeapStart(end), (ulong)heapsize, "sbrk-heap");
|
||||
_heap.Memory.Activate();
|
||||
end = _heap.Memory.End;
|
||||
_disposeList.Add(_heap);
|
||||
AddMemoryBlock(_heap.Memory);
|
||||
}
|
||||
|
||||
if (sealedheapsize > 0)
|
||||
{
|
||||
_sealedheap = new Heap(GetHeapStart(end), (ulong)sealedheapsize, "sealed-heap");
|
||||
_sealedheap.Memory.Activate();
|
||||
end = _sealedheap.Memory.End;
|
||||
_disposeList.Add(_sealedheap);
|
||||
AddMemoryBlock(_sealedheap.Memory);
|
||||
}
|
||||
|
||||
if (invisibleheapsize > 0)
|
||||
{
|
||||
_invisibleheap = new Heap(GetHeapStart(end), (ulong)invisibleheapsize, "invisible-heap");
|
||||
_invisibleheap.Memory.Activate();
|
||||
end = _invisibleheap.Memory.End;
|
||||
_disposeList.Add(_invisibleheap);
|
||||
AddMemoryBlock(_invisibleheap.Memory);
|
||||
}
|
||||
|
||||
ConnectAllClibPatches();
|
||||
Console.WriteLine("Loaded {0}@{1:X16}", filename, _base.Start);
|
||||
foreach (var sec in _elf.Sections.Where(s => s.LoadAddress != 0))
|
||||
{
|
||||
Console.WriteLine(" {0}@{1:X16}, size {2}", sec.Name.PadLeft(20), sec.LoadAddress + _loadoffset, sec.Size.ToString().PadLeft(12));
|
||||
}
|
||||
|
||||
PrintTopSavableSymbols();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintTopSavableSymbols()
|
||||
{
|
||||
Console.WriteLine("Top savestate symbols:");
|
||||
foreach (var text in _symlist
|
||||
.Where(s => s.PointedSection != null && (s.PointedSection.Flags & SectionFlags.Writable) != 0)
|
||||
.OrderByDescending(s => s.Size)
|
||||
.Take(30)
|
||||
.Select(s => string.Format("{0} size {1}", s.Name, s.Size)))
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
}
|
||||
|
||||
private class Elf32_Rel
|
||||
{
|
||||
public long Address;
|
||||
public byte Type;
|
||||
public int SymbolIdx;
|
||||
public long Addend;
|
||||
|
||||
public Elf32_Rel(byte[] data, int start, int len)
|
||||
{
|
||||
if (len == 8 || len == 12)
|
||||
{
|
||||
Address = BitConverter.ToInt32(data, start);
|
||||
Type = data[start + 4];
|
||||
SymbolIdx = (int)(BitConverter.ToUInt32(data, start + 4) >> 8);
|
||||
Addend = data.Length == 12 ? BitConverter.ToInt32(data, start + 8) : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasRelocations()
|
||||
{
|
||||
return _elf.Sections.Any(s => s.Name.StartsWith(".rel"));
|
||||
}
|
||||
|
||||
// elfsharp does not read relocation tables, so there
|
||||
private void ProcessRelocations()
|
||||
{
|
||||
// todo: amd64
|
||||
foreach (var rel in _elf.Sections.Where(s => s.Name.StartsWith(".rel")))
|
||||
{
|
||||
byte[] data = rel.GetContents();
|
||||
var symbols = Enumerable.Range(0, data.Length / 8)
|
||||
.Select(i => new Elf32_Rel(data, i * 8, 8));
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
ApplyRelocation(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void ApplyRelocation(Elf32_Rel rel)
|
||||
{
|
||||
// http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf
|
||||
// this is probably mostly wrong
|
||||
|
||||
long val = 0;
|
||||
long A = rel.Addend;
|
||||
// since all symbols were moved by the same amount, just add _loadoffset here
|
||||
long S = _symlist[rel.SymbolIdx].Value + _loadoffset;
|
||||
long B = _loadoffset;
|
||||
switch (rel.Type)
|
||||
{
|
||||
case 0: val = 0; break;
|
||||
case 1: val = S + A; break;
|
||||
case 2: throw new NotImplementedException();
|
||||
case 3: throw new NotImplementedException();
|
||||
case 4: throw new NotImplementedException();
|
||||
case 5: val = 0; break;
|
||||
case 6: val = S; break;
|
||||
case 7: val = S; break;
|
||||
case 8: val = B + A; break;
|
||||
case 9: throw new NotImplementedException();
|
||||
case 10: throw new NotImplementedException();
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
byte[] tmp = new byte[4];
|
||||
Marshal.Copy((IntPtr)(rel.Address + _loadoffset), tmp, 0, 4);
|
||||
long currentVal = BitConverter.ToUInt32(tmp, 0);
|
||||
tmp = BitConverter.GetBytes((uint)(currentVal + val));
|
||||
Marshal.Copy(tmp, 0, (IntPtr)(rel.Address + _loadoffset), 4);
|
||||
}
|
||||
|
||||
private void RegisterSymbols()
|
||||
{
|
||||
var symbols = ((ISymbolTable)_elf.GetSection(".symtab"))
|
||||
.Entries
|
||||
.Cast<SymbolEntry<long>>();
|
||||
|
||||
// when there are duplicate names, don't register either in the dictionary
|
||||
_symdict = symbols
|
||||
.GroupBy(e => e.Name)
|
||||
.Where(g => g.Count() == 1)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
_symlist = symbols.ToList();
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
_sealedheap.Seal();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var d in _disposeList)
|
||||
d.Dispose();
|
||||
_disposeList.Clear();
|
||||
PurgeMemoryBlocks();
|
||||
_base = null;
|
||||
_heap = null;
|
||||
_sealedheap = null;
|
||||
_invisibleheap = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region clib monkeypatches
|
||||
|
||||
// our clib expects a few function pointers to be defined for it
|
||||
|
||||
/// <summary>
|
||||
/// abort() / other abnormal situation
|
||||
/// </summary>
|
||||
/// <param name="status">desired exit code</param>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void Trap_D();
|
||||
|
||||
/// <summary>
|
||||
/// expand heap
|
||||
/// </summary>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr Sbrk_D(UIntPtr n);
|
||||
|
||||
/// <summary>
|
||||
/// output a string
|
||||
/// </summary>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void DebugPuts_D(string s);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr SbrkSealed_D(UIntPtr n);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr SbrkInvisible_D(UIntPtr n);
|
||||
|
||||
[CLibPatch("_ecl_trap")]
|
||||
private void Trap()
|
||||
{
|
||||
throw new InvalidOperationException("Waterbox code trapped!");
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk")]
|
||||
private IntPtr Sbrk(UIntPtr n)
|
||||
{
|
||||
return Z.US(_heap.Allocate((ulong)n, 1));
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_debug_puts")]
|
||||
private void DebugPuts(string s)
|
||||
{
|
||||
Console.WriteLine("Waterbox debug puts: {0}", s);
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk_sealed")]
|
||||
private IntPtr SbrkSealed(UIntPtr n)
|
||||
{
|
||||
return Z.US(_sealedheap.Allocate((ulong)n, 16));
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk_invisible")]
|
||||
private IntPtr SbrkInvisible(UIntPtr n)
|
||||
{
|
||||
return Z.US(_invisibleheap.Allocate((ulong)n, 16));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// list of delegates that need to not be GCed
|
||||
/// </summary>
|
||||
private List<Delegate> _delegates = new List<Delegate>();
|
||||
|
||||
private void ConnectAllClibPatches()
|
||||
{
|
||||
_delegates.Clear(); // in case we're reconnecting
|
||||
|
||||
var methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(mi => mi.GetCustomAttributes(typeof(CLibPatchAttribute), false).Length > 0);
|
||||
foreach (var mi in methods)
|
||||
{
|
||||
var delegateType = GetType().GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Single(t => t.Name == mi.Name + "_D");
|
||||
var del = Delegate.CreateDelegate(delegateType, this, mi);
|
||||
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
_delegates.Add(del);
|
||||
var sym = _symdict[((CLibPatchAttribute)mi.GetCustomAttributes(typeof(CLibPatchAttribute), false)[0]).NativeName];
|
||||
if (sym.Size != IntPtr.Size)
|
||||
throw new InvalidOperationException("Unexpected function pointer size patching clib!");
|
||||
IntPtr dest = Z.SS(sym.Value + _loadoffset);
|
||||
Marshal.Copy(new[] { ptr }, 0, dest, 1);
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
private class CLibPatchAttribute : Attribute
|
||||
{
|
||||
public string NativeName { get; private set; }
|
||||
public CLibPatchAttribute(string nativeName)
|
||||
{
|
||||
NativeName = nativeName;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
SymbolEntry<long> sym;
|
||||
if (_symdict.TryGetValue(entryPoint, out sym))
|
||||
{
|
||||
return Z.SS(sym.Value + _loadoffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
#region state
|
||||
|
||||
const ulong MAGIC = 0xb00b1e5b00b1e569;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
bw.Write(MAGIC);
|
||||
bw.Write(_elfhash);
|
||||
bw.Write(_loadoffset);
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
|
||||
{
|
||||
var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, false);
|
||||
bw.Write(sec.Size);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
|
||||
if (_heap != null) _heap.SaveStateBinary(bw);
|
||||
if (_sealedheap != null) _sealedheap.SaveStateBinary(bw);
|
||||
bw.Write(MAGIC);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
if (br.ReadUInt64() != MAGIC)
|
||||
throw new InvalidOperationException("Magic not magic enough!");
|
||||
if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash))
|
||||
throw new InvalidOperationException("Elf changed disguise!");
|
||||
if (br.ReadInt64() != _loadoffset)
|
||||
throw new InvalidOperationException("Trickys elves moved on you!");
|
||||
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
|
||||
{
|
||||
var len = br.ReadInt64();
|
||||
if (sec.Size != len)
|
||||
throw new InvalidOperationException("Unexpected section size for " + sec.Name);
|
||||
var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, len);
|
||||
}
|
||||
|
||||
if (_heap != null) _heap.LoadStateBinary(br);
|
||||
if (_sealedheap != null) _sealedheap.LoadStateBinary(br);
|
||||
if (br.ReadUInt64() != MAGIC)
|
||||
throw new InvalidOperationException("Magic not magic enough!");
|
||||
|
||||
// the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session,
|
||||
// are no longer valid. cores must similiarly resend any external pointers they gave the core.
|
||||
ConnectAllClibPatches();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region utils
|
||||
|
||||
private byte[] HashSection(ulong ptr, ulong len)
|
||||
{
|
||||
using (var h = SHA1.Create())
|
||||
{
|
||||
var ms = _base.GetStream(ptr, len, false);
|
||||
return h.ComputeHash(ms);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using ELFSharp.ELF;
|
||||
using ELFSharp.ELF.Sections;
|
||||
using ELFSharp.ELF.Segments;
|
||||
using System.Reflection;
|
||||
using BizHawk.Common;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable
|
||||
{
|
||||
// TODO: a lot of things only work with our elves and aren't fully generalized
|
||||
|
||||
private ELF<long> _elf;
|
||||
private byte[] _elfhash;
|
||||
|
||||
/// <summary>
|
||||
/// executable is loaded here
|
||||
/// </summary>
|
||||
private MemoryBlock _base;
|
||||
/// <summary>
|
||||
/// standard malloc() heap
|
||||
/// </summary>
|
||||
private Heap _heap;
|
||||
|
||||
/// <summary>
|
||||
/// sealed heap (writable only during init)
|
||||
/// </summary>
|
||||
private Heap _sealedheap;
|
||||
|
||||
/// <summary>
|
||||
/// invisible heap (not savestated, use with care)
|
||||
/// </summary>
|
||||
private Heap _invisibleheap;
|
||||
|
||||
private long _loadoffset;
|
||||
private Dictionary<string, SymbolEntry<long>> _symdict;
|
||||
private List<SymbolEntry<long>> _symlist;
|
||||
|
||||
/// <summary>
|
||||
/// everything to clean up at dispose time
|
||||
/// </summary>
|
||||
private List<IDisposable> _disposeList = new List<IDisposable>();
|
||||
|
||||
private ulong GetHeapStart(ulong prevend)
|
||||
{
|
||||
// if relocatable, we won't have constant pointers, so put the heap anywhere
|
||||
// otherwise, put the heap at a canonical location aligned 1MB from the end of the elf, then incremented 16MB
|
||||
ulong heapstart = HasRelocations() ? 0 : ((prevend - 1) | 0xfffff) + 0x1000001;
|
||||
return heapstart;
|
||||
}
|
||||
|
||||
public ElfRunner(string filename, long heapsize, long sealedheapsize, long invisibleheapsize)
|
||||
{
|
||||
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
_elfhash = WaterboxUtils.Hash(fs);
|
||||
}
|
||||
|
||||
// todo: hack up this baby to take Streams
|
||||
_elf = ELFReader.Load<long>(filename);
|
||||
|
||||
var loadsegs = _elf.Segments.Where(s => s.Type == SegmentType.Load);
|
||||
|
||||
long orig_start = loadsegs.Min(s => s.Address);
|
||||
orig_start &= ~(Environment.SystemPageSize - 1);
|
||||
long orig_end = loadsegs.Max(s => s.Address + s.Size);
|
||||
if (HasRelocations())
|
||||
{
|
||||
_base = new MemoryBlock((ulong)(orig_end - orig_start));
|
||||
_loadoffset = (long)_base.Start - orig_start;
|
||||
Initialize(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Initialize((ulong)orig_start);
|
||||
_base = new MemoryBlock((ulong)orig_start, (ulong)(orig_end - orig_start));
|
||||
_loadoffset = 0;
|
||||
Enter();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_disposeList.Add(_base);
|
||||
AddMemoryBlock(_base);
|
||||
_base.Activate();
|
||||
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.RW);
|
||||
|
||||
foreach (var seg in loadsegs)
|
||||
{
|
||||
var data = seg.GetContents();
|
||||
Marshal.Copy(data, 0, Z.SS(seg.Address + _loadoffset), data.Length);
|
||||
}
|
||||
RegisterSymbols();
|
||||
ProcessRelocations();
|
||||
|
||||
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.R);
|
||||
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
|
||||
{
|
||||
if ((sec.Flags & SectionFlags.Executable) != 0)
|
||||
_base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RX);
|
||||
else if ((sec.Flags & SectionFlags.Writable) != 0)
|
||||
_base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RW);
|
||||
}
|
||||
|
||||
ulong end = _base.End;
|
||||
|
||||
if (heapsize > 0)
|
||||
{
|
||||
_heap = new Heap(GetHeapStart(end), (ulong)heapsize, "sbrk-heap");
|
||||
_heap.Memory.Activate();
|
||||
end = _heap.Memory.End;
|
||||
_disposeList.Add(_heap);
|
||||
AddMemoryBlock(_heap.Memory);
|
||||
}
|
||||
|
||||
if (sealedheapsize > 0)
|
||||
{
|
||||
_sealedheap = new Heap(GetHeapStart(end), (ulong)sealedheapsize, "sealed-heap");
|
||||
_sealedheap.Memory.Activate();
|
||||
end = _sealedheap.Memory.End;
|
||||
_disposeList.Add(_sealedheap);
|
||||
AddMemoryBlock(_sealedheap.Memory);
|
||||
}
|
||||
|
||||
if (invisibleheapsize > 0)
|
||||
{
|
||||
_invisibleheap = new Heap(GetHeapStart(end), (ulong)invisibleheapsize, "invisible-heap");
|
||||
_invisibleheap.Memory.Activate();
|
||||
end = _invisibleheap.Memory.End;
|
||||
_disposeList.Add(_invisibleheap);
|
||||
AddMemoryBlock(_invisibleheap.Memory);
|
||||
}
|
||||
|
||||
ConnectAllClibPatches();
|
||||
Console.WriteLine("Loaded {0}@{1:X16}", filename, _base.Start);
|
||||
foreach (var sec in _elf.Sections.Where(s => s.LoadAddress != 0))
|
||||
{
|
||||
Console.WriteLine(" {0}@{1:X16}, size {2}", sec.Name.PadLeft(20), sec.LoadAddress + _loadoffset, sec.Size.ToString().PadLeft(12));
|
||||
}
|
||||
|
||||
PrintTopSavableSymbols();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintTopSavableSymbols()
|
||||
{
|
||||
Console.WriteLine("Top savestate symbols:");
|
||||
foreach (var text in _symlist
|
||||
.Where(s => s.PointedSection != null && (s.PointedSection.Flags & SectionFlags.Writable) != 0)
|
||||
.OrderByDescending(s => s.Size)
|
||||
.Take(30)
|
||||
.Select(s => string.Format("{0} size {1}", s.Name, s.Size)))
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
}
|
||||
|
||||
private class Elf32_Rel
|
||||
{
|
||||
public long Address;
|
||||
public byte Type;
|
||||
public int SymbolIdx;
|
||||
public long Addend;
|
||||
|
||||
public Elf32_Rel(byte[] data, int start, int len)
|
||||
{
|
||||
if (len == 8 || len == 12)
|
||||
{
|
||||
Address = BitConverter.ToInt32(data, start);
|
||||
Type = data[start + 4];
|
||||
SymbolIdx = (int)(BitConverter.ToUInt32(data, start + 4) >> 8);
|
||||
Addend = data.Length == 12 ? BitConverter.ToInt32(data, start + 8) : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasRelocations()
|
||||
{
|
||||
return _elf.Sections.Any(s => s.Name.StartsWith(".rel"));
|
||||
}
|
||||
|
||||
// elfsharp does not read relocation tables, so there
|
||||
private void ProcessRelocations()
|
||||
{
|
||||
// todo: amd64
|
||||
foreach (var rel in _elf.Sections.Where(s => s.Name.StartsWith(".rel")))
|
||||
{
|
||||
byte[] data = rel.GetContents();
|
||||
var symbols = Enumerable.Range(0, data.Length / 8)
|
||||
.Select(i => new Elf32_Rel(data, i * 8, 8));
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
ApplyRelocation(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void ApplyRelocation(Elf32_Rel rel)
|
||||
{
|
||||
// http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf
|
||||
// this is probably mostly wrong
|
||||
|
||||
long val = 0;
|
||||
long A = rel.Addend;
|
||||
// since all symbols were moved by the same amount, just add _loadoffset here
|
||||
long S = _symlist[rel.SymbolIdx].Value + _loadoffset;
|
||||
long B = _loadoffset;
|
||||
switch (rel.Type)
|
||||
{
|
||||
case 0: val = 0; break;
|
||||
case 1: val = S + A; break;
|
||||
case 2: throw new NotImplementedException();
|
||||
case 3: throw new NotImplementedException();
|
||||
case 4: throw new NotImplementedException();
|
||||
case 5: val = 0; break;
|
||||
case 6: val = S; break;
|
||||
case 7: val = S; break;
|
||||
case 8: val = B + A; break;
|
||||
case 9: throw new NotImplementedException();
|
||||
case 10: throw new NotImplementedException();
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
byte[] tmp = new byte[4];
|
||||
Marshal.Copy((IntPtr)(rel.Address + _loadoffset), tmp, 0, 4);
|
||||
long currentVal = BitConverter.ToUInt32(tmp, 0);
|
||||
tmp = BitConverter.GetBytes((uint)(currentVal + val));
|
||||
Marshal.Copy(tmp, 0, (IntPtr)(rel.Address + _loadoffset), 4);
|
||||
}
|
||||
|
||||
private void RegisterSymbols()
|
||||
{
|
||||
var symbols = ((ISymbolTable)_elf.GetSection(".symtab"))
|
||||
.Entries
|
||||
.Cast<SymbolEntry<long>>();
|
||||
|
||||
// when there are duplicate names, don't register either in the dictionary
|
||||
_symdict = symbols
|
||||
.GroupBy(e => e.Name)
|
||||
.Where(g => g.Count() == 1)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
_symlist = symbols.ToList();
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
_sealedheap.Seal();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var d in _disposeList)
|
||||
d.Dispose();
|
||||
_disposeList.Clear();
|
||||
PurgeMemoryBlocks();
|
||||
_base = null;
|
||||
_heap = null;
|
||||
_sealedheap = null;
|
||||
_invisibleheap = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region clib monkeypatches
|
||||
|
||||
// our clib expects a few function pointers to be defined for it
|
||||
|
||||
/// <summary>
|
||||
/// abort() / other abnormal situation
|
||||
/// </summary>
|
||||
/// <param name="status">desired exit code</param>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void Trap_D();
|
||||
|
||||
/// <summary>
|
||||
/// expand heap
|
||||
/// </summary>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr Sbrk_D(UIntPtr n);
|
||||
|
||||
/// <summary>
|
||||
/// output a string
|
||||
/// </summary>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void DebugPuts_D(string s);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr SbrkSealed_D(UIntPtr n);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr SbrkInvisible_D(UIntPtr n);
|
||||
|
||||
[CLibPatch("_ecl_trap")]
|
||||
private void Trap()
|
||||
{
|
||||
throw new InvalidOperationException("Waterbox code trapped!");
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk")]
|
||||
private IntPtr Sbrk(UIntPtr n)
|
||||
{
|
||||
return Z.US(_heap.Allocate((ulong)n, 1));
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_debug_puts")]
|
||||
private void DebugPuts(string s)
|
||||
{
|
||||
Console.WriteLine("Waterbox debug puts: {0}", s);
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk_sealed")]
|
||||
private IntPtr SbrkSealed(UIntPtr n)
|
||||
{
|
||||
return Z.US(_sealedheap.Allocate((ulong)n, 16));
|
||||
}
|
||||
|
||||
[CLibPatch("_ecl_sbrk_invisible")]
|
||||
private IntPtr SbrkInvisible(UIntPtr n)
|
||||
{
|
||||
return Z.US(_invisibleheap.Allocate((ulong)n, 16));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// list of delegates that need to not be GCed
|
||||
/// </summary>
|
||||
private List<Delegate> _delegates = new List<Delegate>();
|
||||
|
||||
private void ConnectAllClibPatches()
|
||||
{
|
||||
_delegates.Clear(); // in case we're reconnecting
|
||||
|
||||
var methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(mi => mi.GetCustomAttributes(typeof(CLibPatchAttribute), false).Length > 0);
|
||||
foreach (var mi in methods)
|
||||
{
|
||||
var delegateType = GetType().GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Single(t => t.Name == mi.Name + "_D");
|
||||
var del = Delegate.CreateDelegate(delegateType, this, mi);
|
||||
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
_delegates.Add(del);
|
||||
var sym = _symdict[((CLibPatchAttribute)mi.GetCustomAttributes(typeof(CLibPatchAttribute), false)[0]).NativeName];
|
||||
if (sym.Size != IntPtr.Size)
|
||||
throw new InvalidOperationException("Unexpected function pointer size patching clib!");
|
||||
IntPtr dest = Z.SS(sym.Value + _loadoffset);
|
||||
Marshal.Copy(new[] { ptr }, 0, dest, 1);
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
private class CLibPatchAttribute : Attribute
|
||||
{
|
||||
public string NativeName { get; private set; }
|
||||
public CLibPatchAttribute(string nativeName)
|
||||
{
|
||||
NativeName = nativeName;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
SymbolEntry<long> sym;
|
||||
if (_symdict.TryGetValue(entryPoint, out sym))
|
||||
{
|
||||
return Z.SS(sym.Value + _loadoffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
#region state
|
||||
|
||||
const ulong MAGIC = 0xb00b1e5b00b1e569;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
bw.Write(MAGIC);
|
||||
bw.Write(_elfhash);
|
||||
bw.Write(_loadoffset);
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
|
||||
{
|
||||
var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, false);
|
||||
bw.Write(sec.Size);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
|
||||
if (_heap != null) _heap.SaveStateBinary(bw);
|
||||
if (_sealedheap != null) _sealedheap.SaveStateBinary(bw);
|
||||
bw.Write(MAGIC);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
Enter();
|
||||
try
|
||||
{
|
||||
if (br.ReadUInt64() != MAGIC)
|
||||
throw new InvalidOperationException("Magic not magic enough!");
|
||||
if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash))
|
||||
throw new InvalidOperationException("Elf changed disguise!");
|
||||
if (br.ReadInt64() != _loadoffset)
|
||||
throw new InvalidOperationException("Trickys elves moved on you!");
|
||||
|
||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
|
||||
{
|
||||
var len = br.ReadInt64();
|
||||
if (sec.Size != len)
|
||||
throw new InvalidOperationException("Unexpected section size for " + sec.Name);
|
||||
var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, len);
|
||||
}
|
||||
|
||||
if (_heap != null) _heap.LoadStateBinary(br);
|
||||
if (_sealedheap != null) _sealedheap.LoadStateBinary(br);
|
||||
if (br.ReadUInt64() != MAGIC)
|
||||
throw new InvalidOperationException("Magic not magic enough!");
|
||||
|
||||
// the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session,
|
||||
// are no longer valid. cores must similiarly resend any external pointers they gave the core.
|
||||
ConnectAllClibPatches();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region utils
|
||||
|
||||
private byte[] HashSection(ulong ptr, ulong len)
|
||||
{
|
||||
using (var h = SHA1.Create())
|
||||
{
|
||||
var ms = _base.GetStream(ptr, len, false);
|
||||
return h.ComputeHash(ms);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,144 +1,145 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
/// <summary>
|
||||
/// a simple grow-only fixed max size heap
|
||||
/// </summary>
|
||||
internal sealed class Heap : IBinaryStateable, IDisposable
|
||||
{
|
||||
public MemoryBlock Memory { get; private set; }
|
||||
/// <summary>
|
||||
/// name, used in identifying errors
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// total number of bytes used
|
||||
/// </summary>
|
||||
public ulong Used { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// true if the heap has been sealed, preventing further changes
|
||||
/// </summary>
|
||||
public bool Sealed { get; private set; }
|
||||
|
||||
private byte[] _hash;
|
||||
|
||||
public Heap(ulong start, ulong size, string name)
|
||||
{
|
||||
Memory = new MemoryBlock(start, size);
|
||||
Used = 0;
|
||||
Name = name;
|
||||
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
||||
}
|
||||
|
||||
private void EnsureAlignment(int align)
|
||||
{
|
||||
if (align > 1)
|
||||
{
|
||||
ulong newused = ((Used - 1) | (ulong)(align - 1)) + 1;
|
||||
if (newused > Memory.Size)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Failed to meet alignment {0} on heap {1}", align, Name));
|
||||
}
|
||||
Used = newused;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong Allocate(ulong size, int align)
|
||||
{
|
||||
if (Sealed)
|
||||
throw new InvalidOperationException(string.Format("Attempt made to allocate from sealed heap {0}", Name));
|
||||
|
||||
EnsureAlignment(align);
|
||||
|
||||
ulong newused = Used + size;
|
||||
if (newused > Memory.Size)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Failed to allocate {0} bytes from heap {1}", size, Name));
|
||||
}
|
||||
ulong ret = Memory.Start + Used;
|
||||
Memory.Protect(ret, newused - Used, MemoryBlock.Protection.RW);
|
||||
Used = newused;
|
||||
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
if (!Sealed)
|
||||
{
|
||||
Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R);
|
||||
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
|
||||
Sealed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Attempt to reseal heap {0}", Name));
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Name);
|
||||
bw.Write(Used);
|
||||
if (!Sealed)
|
||||
{
|
||||
bw.Write(Memory.XorHash);
|
||||
var ms = Memory.GetXorStream(Memory.Start, Used, false);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
bw.Write(_hash);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
var name = br.ReadString();
|
||||
if (name != Name)
|
||||
// probable cause: internal error
|
||||
throw new InvalidOperationException(string.Format("Name did not match for heap {0}", Name));
|
||||
var used = br.ReadUInt64();
|
||||
if (used > Memory.Size)
|
||||
throw new InvalidOperationException(string.Format("Heap {0} used {1} larger than available {2}", Name, used, Memory.Size));
|
||||
if (!Sealed)
|
||||
{
|
||||
var hash = br.ReadBytes(Memory.XorHash.Length);
|
||||
if (!hash.SequenceEqual(Memory.XorHash))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name));
|
||||
}
|
||||
|
||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None);
|
||||
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
||||
var ms = Memory.GetXorStream(Memory.Start, used, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, (long)used);
|
||||
Used = used;
|
||||
}
|
||||
else
|
||||
{
|
||||
var hash = br.ReadBytes(_hash.Length);
|
||||
if (!hash.SequenceEqual(_hash))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Memory != null)
|
||||
{
|
||||
Memory.Dispose();
|
||||
Memory = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
/// <summary>
|
||||
/// a simple grow-only fixed max size heap
|
||||
/// </summary>
|
||||
internal sealed class Heap : IBinaryStateable, IDisposable
|
||||
{
|
||||
public MemoryBlock Memory { get; private set; }
|
||||
/// <summary>
|
||||
/// name, used in identifying errors
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// total number of bytes used
|
||||
/// </summary>
|
||||
public ulong Used { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// true if the heap has been sealed, preventing further changes
|
||||
/// </summary>
|
||||
public bool Sealed { get; private set; }
|
||||
|
||||
private byte[] _hash;
|
||||
|
||||
public Heap(ulong start, ulong size, string name)
|
||||
{
|
||||
Memory = new MemoryBlock(start, size);
|
||||
Used = 0;
|
||||
Name = name;
|
||||
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
||||
}
|
||||
|
||||
private void EnsureAlignment(int align)
|
||||
{
|
||||
if (align > 1)
|
||||
{
|
||||
ulong newused = ((Used - 1) | (ulong)(align - 1)) + 1;
|
||||
if (newused > Memory.Size)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Failed to meet alignment {0} on heap {1}", align, Name));
|
||||
}
|
||||
Used = newused;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong Allocate(ulong size, int align)
|
||||
{
|
||||
if (Sealed)
|
||||
throw new InvalidOperationException(string.Format("Attempt made to allocate from sealed heap {0}", Name));
|
||||
|
||||
EnsureAlignment(align);
|
||||
|
||||
ulong newused = Used + size;
|
||||
if (newused > Memory.Size)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Failed to allocate {0} bytes from heap {1}", size, Name));
|
||||
}
|
||||
ulong ret = Memory.Start + Used;
|
||||
Memory.Protect(ret, newused - Used, MemoryBlock.Protection.RW);
|
||||
Used = newused;
|
||||
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
if (!Sealed)
|
||||
{
|
||||
Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R);
|
||||
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
|
||||
Sealed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Attempt to reseal heap {0}", Name));
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Name);
|
||||
bw.Write(Used);
|
||||
if (!Sealed)
|
||||
{
|
||||
bw.Write(Memory.XorHash);
|
||||
var ms = Memory.GetXorStream(Memory.Start, Used, false);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
bw.Write(_hash);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
var name = br.ReadString();
|
||||
if (name != Name)
|
||||
// probable cause: internal error
|
||||
throw new InvalidOperationException(string.Format("Name did not match for heap {0}", Name));
|
||||
var used = br.ReadUInt64();
|
||||
if (used > Memory.Size)
|
||||
throw new InvalidOperationException(string.Format("Heap {0} used {1} larger than available {2}", Name, used, Memory.Size));
|
||||
if (!Sealed)
|
||||
{
|
||||
var hash = br.ReadBytes(Memory.XorHash.Length);
|
||||
if (!hash.SequenceEqual(Memory.XorHash))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name));
|
||||
}
|
||||
|
||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None);
|
||||
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
||||
var ms = Memory.GetXorStream(Memory.Start, used, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, (long)used);
|
||||
Used = used;
|
||||
}
|
||||
else
|
||||
{
|
||||
var hash = br.ReadBytes(_hash.Length);
|
||||
if (!hash.SequenceEqual(_hash))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Memory != null)
|
||||
{
|
||||
Memory.Dispose();
|
||||
Memory = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new InvalidOperationException(s);
|
||||
};
|
||||
_traps.Add(del);
|
||||
ptr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
ptr = CallingConventionAdapters.Waterbox.GetFunctionPointerForDelegate(del);
|
||||
}
|
||||
return ptr;
|
||||
}).ToArray();
|
||||
|
@ -354,17 +354,17 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
// in midipix, this just sets up a SEH frame and then calls musl's start_main
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "start_main")]
|
||||
public unsafe int StartMain(IntPtr main, int argc, IntPtr argv, IntPtr libc_start_main)
|
||||
{
|
||||
//var del = (LibcStartMain)Marshal.GetDelegateForFunctionPointer(libc_start_main, typeof(LibcStartMain));
|
||||
// this will init, and then call user main, and then call exit()
|
||||
//del(main, argc, argv);
|
||||
//int* foobar = stackalloc int[128];
|
||||
|
||||
|
||||
// if we return from this, psx will then halt, so break out
|
||||
//if (_firstTime)
|
||||
//{
|
||||
//_firstTime = false;
|
||||
{
|
||||
//var del = (LibcStartMain)CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer(libc_start_main, typeof(LibcStartMain));
|
||||
// this will init, and then call user main, and then call exit()
|
||||
//del(main, argc, argv);
|
||||
//int* foobar = stackalloc int[128];
|
||||
|
||||
|
||||
// if we return from this, psx will then halt, so break out
|
||||
//if (_firstTime)
|
||||
//{
|
||||
//_firstTime = false;
|
||||
throw new InvalidOperationException("This shouldn't be called");
|
||||
//}
|
||||
//else
|
||||
|
@ -676,11 +676,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
// load any predefined exports
|
||||
_psx = new Psx(this);
|
||||
_exports.Add("libpsxscl.so", BizExvoker.GetExvoker(_psx));
|
||||
_exports.Add("libpsxscl.so", BizExvoker.GetExvoker(_psx, CallingConventionAdapters.Waterbox));
|
||||
_emu = new Emu(this);
|
||||
_exports.Add("libemuhost.so", BizExvoker.GetExvoker(_emu));
|
||||
_exports.Add("libemuhost.so", BizExvoker.GetExvoker(_emu, CallingConventionAdapters.Waterbox));
|
||||
_syscalls = new Syscalls(this);
|
||||
_exports.Add("__syscalls", BizExvoker.GetExvoker(_syscalls));
|
||||
_exports.Add("__syscalls", BizExvoker.GetExvoker(_syscalls, CallingConventionAdapters.Waterbox));
|
||||
|
||||
// load and connect all modules, starting with the executable
|
||||
var todoModules = new Queue<string>();
|
||||
|
@ -730,7 +730,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
|
||||
_libcpatch = new LibcPatch(this);
|
||||
_exports["libc.so"] = new PatchImportResolver(_exports["libc.so"], BizExvoker.GetExvoker(_libcpatch));
|
||||
_exports["libc.so"] = new PatchImportResolver(_exports["libc.so"], BizExvoker.GetExvoker(_libcpatch, CallingConventionAdapters.Waterbox));
|
||||
|
||||
ConnectAllImports();
|
||||
|
||||
|
@ -764,7 +764,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
var libcEnter = _exports["libc.so"].SafeResolve("__libc_entry_routine");
|
||||
var psxInit = _exports["libpsxscl.so"].SafeResolve("__psx_init");
|
||||
|
||||
var del = (LibcEntryRoutineD)Marshal.GetDelegateForFunctionPointer(libcEnter, typeof(LibcEntryRoutineD));
|
||||
var del = (LibcEntryRoutineD)CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer(libcEnter, typeof(LibcEntryRoutineD));
|
||||
// the current mmglue code doesn't use the main pointer at all, and this just returns
|
||||
del(IntPtr.Zero, psxInit, 0);
|
||||
|
||||
|
@ -811,7 +811,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (_exports.TryGetValue("libco.so", out libco))
|
||||
{
|
||||
Console.WriteLine("Calling co_clean()...");
|
||||
Marshal.GetDelegateForFunctionPointer<Action>(libco.SafeResolve("co_clean"))();
|
||||
CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer<Action>(libco.SafeResolve("co_clean"))();
|
||||
}
|
||||
|
||||
_sealedheap.Seal();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
using PeNet;
|
||||
using System;
|
||||
|
@ -73,18 +74,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
private delegate void ExeEntry();*/
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void GlobalCtor();
|
||||
|
||||
private delegate void GlobalCtor();
|
||||
|
||||
/*public bool RunDllEntry()
|
||||
{
|
||||
var entryThunk = (DllEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(DllEntry));
|
||||
var entryThunk = (DllEntry)CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer(EntryPoint, typeof(DllEntry));
|
||||
return entryThunk(Z.US(Start), 1, IntPtr.Zero); // DLL_PROCESS_ATTACH
|
||||
}
|
||||
public void RunExeEntry()
|
||||
{
|
||||
var entryThunk = (ExeEntry)Marshal.GetDelegateForFunctionPointer(EntryPoint, typeof(ExeEntry));
|
||||
var entryThunk = (ExeEntry)CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer(EntryPoint, typeof(ExeEntry));
|
||||
entryThunk();
|
||||
}*/
|
||||
}*/
|
||||
public unsafe void RunGlobalCtors()
|
||||
{
|
||||
int did = 0;
|
||||
|
@ -94,7 +95,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
IntPtr f;
|
||||
while ((f = *++p) != IntPtr.Zero) // skip 0th dummy pointer
|
||||
{
|
||||
var ctorThunk = (GlobalCtor)Marshal.GetDelegateForFunctionPointer(f, typeof(GlobalCtor));
|
||||
var ctorThunk = (GlobalCtor)CallingConventionAdapters.Waterbox.GetDelegateForFunctionPointer(f, typeof(GlobalCtor));
|
||||
//Console.WriteLine(f);
|
||||
//System.Diagnostics.Debugger.Break();
|
||||
ctorThunk();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.BizInvoke;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
|
|
@ -12,7 +12,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
public abstract class WaterboxCore : IEmulator, IVideoProvider, ISoundProvider, IStatable,
|
||||
public abstract class WaterboxCore : IEmulator, IVideoProvider, ISoundProvider, IStatable,
|
||||
IInputPollable, ISaveRam
|
||||
{
|
||||
private LibWaterboxCore _core;
|
||||
|
@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
_exe = new PeRunner(options);
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
var ret = BizInvoker.GetInvoker<T>(_exe, _exe);
|
||||
var ret = BizInvoker.GetInvoker<T>(_exe, _exe, CallingConventionAdapters.Waterbox);
|
||||
_core = ret;
|
||||
return ret;
|
||||
}
|
||||
|
@ -87,15 +87,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
|
||||
private LibWaterboxCore.MemoryArea[] _saveramAreas;
|
||||
private int _saveramSize;
|
||||
|
||||
public unsafe bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_saveramSize == 0)
|
||||
return false;
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
|
||||
public unsafe bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_saveramSize == 0)
|
||||
return false;
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
foreach (var area in _saveramAreas)
|
||||
{
|
||||
int* p = (int*)area.Data;
|
||||
|
@ -107,35 +107,35 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (*p++ != cmp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
if (_saveramSize == 0)
|
||||
return null;
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
var ret = new byte[_saveramSize];
|
||||
var offs = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
if (_saveramSize == 0)
|
||||
return null;
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
var ret = new byte[_saveramSize];
|
||||
var offs = 0;
|
||||
foreach (var area in _saveramAreas)
|
||||
{
|
||||
Marshal.Copy(area.Data, ret, offs, (int)area.Size);
|
||||
offs += (int)area.Size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
if (data.Length != _saveramSize)
|
||||
throw new InvalidOperationException("Saveram size mismatch");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
if (data.Length != _saveramSize)
|
||||
throw new InvalidOperationException("Saveram size mismatch");
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
var offs = 0;
|
||||
|
@ -144,11 +144,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
Marshal.Copy(data, offs, area.Data, (int)area.Size);
|
||||
offs += (int)area.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ISaveRam
|
||||
#endregion ISaveRam
|
||||
|
||||
#region IEmulator
|
||||
|
||||
|
@ -196,14 +196,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
|
||||
public CoreComm CoreComm { get; }
|
||||
public int Frame { get; private set; }
|
||||
public int LagCount { get; set; }
|
||||
public bool IsLagFrame { get; set; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
public int Frame { get; private set; }
|
||||
public int LagCount { get; set; }
|
||||
public bool IsLagFrame { get; set; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
|
||||
protected readonly BasicServiceProvider _serviceProvider;
|
||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||
|
@ -214,56 +214,56 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
|
||||
#endregion
|
||||
|
||||
#region IStatable
|
||||
#region IStatable
|
||||
|
||||
public bool BinarySaveStatesPreferred => true;
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
var temp = SaveStateBinary();
|
||||
temp.SaveAsHexFast(writer);
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
string hex = reader.ReadLine();
|
||||
byte[] state = new byte[hex.Length / 2];
|
||||
state.ReadFromHexFast(hex);
|
||||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
public bool BinarySaveStatesPreferred => true;
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
var temp = SaveStateBinary();
|
||||
temp.SaveAsHexFast(writer);
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
string hex = reader.ReadLine();
|
||||
byte[] state = new byte[hex.Length / 2];
|
||||
state.ReadFromHexFast(hex);
|
||||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
_exe.LoadStateBinary(reader);
|
||||
// other variables
|
||||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
BufferWidth = reader.ReadInt32();
|
||||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
BufferWidth = reader.ReadInt32();
|
||||
BufferHeight = reader.ReadInt32();
|
||||
// reset pointers here!
|
||||
_core.SetInputCallback(null);
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
_core.SetInputCallback(null);
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
_exe.SaveStateBinary(writer);
|
||||
// other variables
|
||||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
writer.Write(BufferWidth);
|
||||
writer.Write(BufferHeight);
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return ms.ToArray();
|
||||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
writer.Write(BufferWidth);
|
||||
writer.Write(BufferHeight);
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -288,54 +288,54 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region ISoundProvider
|
||||
#region ISoundProvider
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = _soundBuffer;
|
||||
nsamp = _numSamples;
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
}
|
||||
|
||||
protected readonly short[] _soundBuffer;
|
||||
protected int _numSamples;
|
||||
public bool CanProvideAsync => false;
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = _soundBuffer;
|
||||
nsamp = _numSamples;
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
}
|
||||
|
||||
protected readonly short[] _soundBuffer;
|
||||
protected int _numSamples;
|
||||
public bool CanProvideAsync => false;
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region IVideoProvider
|
||||
#region IVideoProvider
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return _videoBuffer;
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return _videoBuffer;
|
||||
}
|
||||
|
||||
protected readonly int[] _videoBuffer;
|
||||
public virtual int VirtualWidth => BufferWidth;
|
||||
public virtual int VirtualHeight => BufferWidth;
|
||||
public int BufferWidth { get; private set; }
|
||||
public int BufferHeight { get; private set; }
|
||||
public virtual int VsyncNumerator { get; protected set; }
|
||||
public virtual int VsyncDenominator { get; protected set; }
|
||||
public virtual int VirtualWidth => BufferWidth;
|
||||
public virtual int VirtualHeight => BufferWidth;
|
||||
public int BufferWidth { get; private set; }
|
||||
public int BufferHeight { get; private set; }
|
||||
public virtual int VsyncNumerator { get; protected set; }
|
||||
public virtual int VsyncDenominator { get; protected set; }
|
||||
public int BackgroundColor => unchecked((int)0xff000000);
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
gcc test.c -o test.exe -Wall
|
|
@ -0,0 +1,20 @@
|
|||
private static readonly byte[][] Depart =
|
||||
{
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x55, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x48, 0x89, 0xd1, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x4c, 0x89, 0x4d, 0xd0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd0, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
};
|
||||
private static readonly byte[][] Arrive =
|
||||
{
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x8b, 0x55, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x48, 0x89, 0xd1, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x40, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
new byte[] { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x48, 0x89, 0x55, 0xe8, 0x48, 0x89, 0x4d, 0xe0, 0x4c, 0x89, 0x45, 0xd8, 0x4c, 0x89, 0x4d, 0xd0, 0x48, 0x8b, 0x7d, 0xe0, 0x48, 0x8b, 0x75, 0xe8, 0x48, 0x8b, 0x55, 0xf0, 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x45, 0xd0, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x8b, 0x45, 0xd8, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0xce, 0xfa, 0xed, 0xfe, 0xef, 0xbe, 0xad, 0xde, 0x49, 0x89, 0xf9, 0x49, 0x89, 0xf0, 0xff, 0xd0, 0xc9, 0xc3, },
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
#include <stdint.h>
|
||||
|
||||
typedef int64_t ll;
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart0(void)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(void))0xdeadbeeffeedface)();
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart1(ll a)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll))0xdeadbeeffeedface)(a);
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart2(ll a, ll b)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll, ll))0xdeadbeeffeedface)(a, b);
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart3(ll a, ll b, ll c)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll, ll, ll))0xdeadbeeffeedface)(a, b, c);
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart4(ll a, ll b, ll c, ll d)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d);
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart5(ll a, ll b, ll c, ll d, ll e)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll, ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d, e);
|
||||
}
|
||||
|
||||
__attribute__((sysv_abi)) ll Depart6(ll a, ll b, ll c, ll d, ll e, ll f)
|
||||
{
|
||||
return ((__attribute__((ms_abi)) ll (*)(ll, ll, ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d, e, f);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive0(void)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(void))0xdeadbeeffeedface)();
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive1(ll a)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll))0xdeadbeeffeedface)(a);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive2(ll a, ll b)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll, ll))0xdeadbeeffeedface)(a, b);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive3(ll a, ll b, ll c)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll, ll, ll))0xdeadbeeffeedface)(a, b, c);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive4(ll a, ll b, ll c, ll d)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive5(ll a, ll b, ll c, ll d, ll e)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll, ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d, e);
|
||||
}
|
||||
|
||||
__attribute__((ms_abi)) ll Arrive6(ll a, ll b, ll c, ll d, ll e, ll f)
|
||||
{
|
||||
return ((__attribute__((sysv_abi)) ll (*)(ll, ll, ll, ll, ll, ll))0xdeadbeeffeedface)(a, b, c, d, e, f);
|
||||
}
|
||||
|
||||
void End(void)
|
||||
{
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
const void* ptrs[] = { Depart0, Depart1, Depart2, Depart3, Depart4, Depart5, Depart6,
|
||||
Arrive0, Arrive1, Arrive2, Arrive3, Arrive4, Arrive5, Arrive6, End };
|
||||
|
||||
void print(const char* name, int offs)
|
||||
{
|
||||
printf("private static readonly byte[][] %s =\n{\n", name);
|
||||
for (int i = offs; i < offs + 7; i++)
|
||||
{
|
||||
printf("\tnew byte[] { ");
|
||||
const uint8_t* start = ptrs[i];
|
||||
const uint8_t* end = ptrs[i + 1];
|
||||
while (start < end)
|
||||
printf("0x%02x, ", *start++);
|
||||
printf("},\n");
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("Depart", 0);
|
||||
print("Arrive", 0);
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,682 @@
|
|||
.file "test.c"
|
||||
.text
|
||||
.globl Depart0
|
||||
.def Depart0; .scl 2; .type 32; .endef
|
||||
Depart0:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $32, %rsp
|
||||
movabsq $-2401053088335136050, %rax
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart1
|
||||
.def Depart1; .scl 2; .type 32; .endef
|
||||
Depart1:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $48, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq -8(%rbp), %rdx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rdx, %rcx
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart2
|
||||
.def Depart2; .scl 2; .type 32; .endef
|
||||
Depart2:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $48, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq %rsi, -16(%rbp)
|
||||
movq -16(%rbp), %rdx
|
||||
movq -8(%rbp), %rcx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart3
|
||||
.def Depart3; .scl 2; .type 32; .endef
|
||||
Depart3:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $64, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq %rsi, -16(%rbp)
|
||||
movq %rdx, -24(%rbp)
|
||||
movq -24(%rbp), %rsi
|
||||
movq -16(%rbp), %rdx
|
||||
movq -8(%rbp), %rcx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rsi, %r8
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart4
|
||||
.def Depart4; .scl 2; .type 32; .endef
|
||||
Depart4:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $64, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq %rsi, -16(%rbp)
|
||||
movq %rdx, -24(%rbp)
|
||||
movq %rcx, -32(%rbp)
|
||||
movq -32(%rbp), %rdi
|
||||
movq -24(%rbp), %rsi
|
||||
movq -16(%rbp), %rdx
|
||||
movq -8(%rbp), %rcx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rdi, %r9
|
||||
movq %rsi, %r8
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart5
|
||||
.def Depart5; .scl 2; .type 32; .endef
|
||||
Depart5:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $96, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq %rsi, -16(%rbp)
|
||||
movq %rdx, -24(%rbp)
|
||||
movq %rcx, -32(%rbp)
|
||||
movq %r8, -40(%rbp)
|
||||
movq -32(%rbp), %rdi
|
||||
movq -24(%rbp), %rsi
|
||||
movq -16(%rbp), %rdx
|
||||
movq -8(%rbp), %rcx
|
||||
movq -40(%rbp), %rax
|
||||
movq %rax, 32(%rsp)
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rdi, %r9
|
||||
movq %rsi, %r8
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Depart6
|
||||
.def Depart6; .scl 2; .type 32; .endef
|
||||
Depart6:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
subq $96, %rsp
|
||||
movq %rdi, -8(%rbp)
|
||||
movq %rsi, -16(%rbp)
|
||||
movq %rdx, -24(%rbp)
|
||||
movq %rcx, -32(%rbp)
|
||||
movq %r8, -40(%rbp)
|
||||
movq %r9, -48(%rbp)
|
||||
movq -32(%rbp), %rdi
|
||||
movq -24(%rbp), %rsi
|
||||
movq -16(%rbp), %rdx
|
||||
movq -8(%rbp), %rcx
|
||||
movq -48(%rbp), %rax
|
||||
movq %rax, 40(%rsp)
|
||||
movq -40(%rbp), %rax
|
||||
movq %rax, 32(%rsp)
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rdi, %r9
|
||||
movq %rsi, %r8
|
||||
call *%rax
|
||||
leave
|
||||
ret
|
||||
.globl Arrive0
|
||||
.def Arrive0; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive0
|
||||
Arrive0:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movabsq $-2401053088335136050, %rax
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive1
|
||||
.def Arrive1; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive1
|
||||
Arrive1:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive2
|
||||
.def Arrive2; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive2
|
||||
Arrive2:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movq %rdx, 40(%rbp)
|
||||
movq 40(%rbp), %rdx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rdx, %rsi
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive3
|
||||
.def Arrive3; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive3
|
||||
Arrive3:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movq %rdx, 40(%rbp)
|
||||
movq %r8, 48(%rbp)
|
||||
movq 48(%rbp), %rdx
|
||||
movq 40(%rbp), %rcx
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %rcx, %rsi
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive4
|
||||
.def Arrive4; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive4
|
||||
Arrive4:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movq %rdx, 40(%rbp)
|
||||
movq %r8, 48(%rbp)
|
||||
movq %r9, 56(%rbp)
|
||||
movq 56(%rbp), %rcx
|
||||
movq 48(%rbp), %rdx
|
||||
movq 40(%rbp), %r8
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %r8, %rsi
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive5
|
||||
.def Arrive5; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive5
|
||||
Arrive5:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movq %rdx, 40(%rbp)
|
||||
movq %r8, 48(%rbp)
|
||||
movq %r9, 56(%rbp)
|
||||
movq 64(%rbp), %r8
|
||||
movq 56(%rbp), %rcx
|
||||
movq 48(%rbp), %rdx
|
||||
movq 40(%rbp), %r9
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %r9, %rsi
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl Arrive6
|
||||
.def Arrive6; .scl 2; .type 32; .endef
|
||||
.seh_proc Arrive6
|
||||
Arrive6:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
pushq %rdi
|
||||
.seh_pushreg %rdi
|
||||
pushq %rsi
|
||||
.seh_pushreg %rsi
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $160, %rsp
|
||||
.seh_stackalloc 160
|
||||
movaps %xmm6, (%rsp)
|
||||
.seh_savexmm %xmm6, 0
|
||||
movaps %xmm7, 16(%rsp)
|
||||
.seh_savexmm %xmm7, 16
|
||||
movaps %xmm8, -128(%rbp)
|
||||
.seh_savexmm %xmm8, 32
|
||||
movaps %xmm9, -112(%rbp)
|
||||
.seh_savexmm %xmm9, 48
|
||||
movaps %xmm10, -96(%rbp)
|
||||
.seh_savexmm %xmm10, 64
|
||||
movaps %xmm11, -80(%rbp)
|
||||
.seh_savexmm %xmm11, 80
|
||||
movaps %xmm12, -64(%rbp)
|
||||
.seh_savexmm %xmm12, 96
|
||||
movaps %xmm13, -48(%rbp)
|
||||
.seh_savexmm %xmm13, 112
|
||||
movaps %xmm14, -32(%rbp)
|
||||
.seh_savexmm %xmm14, 128
|
||||
movaps %xmm15, -16(%rbp)
|
||||
.seh_savexmm %xmm15, 144
|
||||
.seh_endprologue
|
||||
movq %rcx, 32(%rbp)
|
||||
movq %rdx, 40(%rbp)
|
||||
movq %r8, 48(%rbp)
|
||||
movq %r9, 56(%rbp)
|
||||
movq 72(%rbp), %r9
|
||||
movq 64(%rbp), %r8
|
||||
movq 56(%rbp), %rcx
|
||||
movq 48(%rbp), %rdx
|
||||
movq 40(%rbp), %r10
|
||||
movabsq $-2401053088335136050, %rax
|
||||
movq %r10, %rsi
|
||||
movq 32(%rbp), %rdi
|
||||
call *%rax
|
||||
movaps (%rsp), %xmm6
|
||||
movaps 16(%rsp), %xmm7
|
||||
movaps -128(%rbp), %xmm8
|
||||
movaps -112(%rbp), %xmm9
|
||||
movaps -96(%rbp), %xmm10
|
||||
movaps -80(%rbp), %xmm11
|
||||
movaps -64(%rbp), %xmm12
|
||||
movaps -48(%rbp), %xmm13
|
||||
movaps -32(%rbp), %xmm14
|
||||
movaps -16(%rbp), %xmm15
|
||||
addq $160, %rsp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl End
|
||||
.def End; .scl 2; .type 32; .endef
|
||||
.seh_proc End
|
||||
End:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
.seh_endprologue
|
||||
nop
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.globl ptrs
|
||||
.data
|
||||
.align 32
|
||||
ptrs:
|
||||
.quad Depart0
|
||||
.quad Depart1
|
||||
.quad Depart2
|
||||
.quad Depart3
|
||||
.quad Depart4
|
||||
.quad Depart5
|
||||
.quad Depart6
|
||||
.quad Arrive0
|
||||
.quad Arrive1
|
||||
.quad Arrive2
|
||||
.quad Arrive3
|
||||
.quad Arrive4
|
||||
.quad Arrive5
|
||||
.quad Arrive6
|
||||
.quad End
|
||||
.section .rdata,"dr"
|
||||
.align 8
|
||||
.LC0:
|
||||
.ascii "private static readonly byte[][] %s =\12{\12\0"
|
||||
.LC1:
|
||||
.ascii "\11new byte[] { \0"
|
||||
.LC2:
|
||||
.ascii "0x%02x, \0"
|
||||
.LC3:
|
||||
.ascii "},\0"
|
||||
.LC4:
|
||||
.ascii "};\0"
|
||||
.text
|
||||
.globl print
|
||||
.def print; .scl 2; .type 32; .endef
|
||||
.seh_proc print
|
||||
print:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $64, %rsp
|
||||
.seh_stackalloc 64
|
||||
.seh_endprologue
|
||||
movq %rcx, 16(%rbp)
|
||||
movl %edx, 24(%rbp)
|
||||
movq 16(%rbp), %rdx
|
||||
leaq .LC0(%rip), %rcx
|
||||
call printf
|
||||
movl 24(%rbp), %eax
|
||||
movl %eax, -4(%rbp)
|
||||
jmp .L31
|
||||
.L34:
|
||||
leaq .LC1(%rip), %rcx
|
||||
call printf
|
||||
movl -4(%rbp), %eax
|
||||
cltq
|
||||
leaq 0(,%rax,8), %rdx
|
||||
leaq ptrs(%rip), %rax
|
||||
movq (%rdx,%rax), %rax
|
||||
movq %rax, -16(%rbp)
|
||||
movl -4(%rbp), %eax
|
||||
addl $1, %eax
|
||||
cltq
|
||||
leaq 0(,%rax,8), %rdx
|
||||
leaq ptrs(%rip), %rax
|
||||
movq (%rdx,%rax), %rax
|
||||
movq %rax, -24(%rbp)
|
||||
jmp .L32
|
||||
.L33:
|
||||
movq -16(%rbp), %rax
|
||||
leaq 1(%rax), %rdx
|
||||
movq %rdx, -16(%rbp)
|
||||
movzbl (%rax), %eax
|
||||
movzbl %al, %eax
|
||||
movl %eax, %edx
|
||||
leaq .LC2(%rip), %rcx
|
||||
call printf
|
||||
.L32:
|
||||
movq -16(%rbp), %rax
|
||||
cmpq -24(%rbp), %rax
|
||||
jb .L33
|
||||
leaq .LC3(%rip), %rcx
|
||||
call puts
|
||||
addl $1, -4(%rbp)
|
||||
.L31:
|
||||
movl 24(%rbp), %eax
|
||||
addl $7, %eax
|
||||
cmpl -4(%rbp), %eax
|
||||
jg .L34
|
||||
leaq .LC4(%rip), %rcx
|
||||
call puts
|
||||
nop
|
||||
addq $64, %rsp
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.def __main; .scl 2; .type 32; .endef
|
||||
.section .rdata,"dr"
|
||||
.LC5:
|
||||
.ascii "Depart\0"
|
||||
.LC6:
|
||||
.ascii "Arrive\0"
|
||||
.text
|
||||
.globl main
|
||||
.def main; .scl 2; .type 32; .endef
|
||||
.seh_proc main
|
||||
main:
|
||||
pushq %rbp
|
||||
.seh_pushreg %rbp
|
||||
movq %rsp, %rbp
|
||||
.seh_setframe %rbp, 0
|
||||
subq $32, %rsp
|
||||
.seh_stackalloc 32
|
||||
.seh_endprologue
|
||||
call __main
|
||||
movl $0, %edx
|
||||
leaq .LC5(%rip), %rcx
|
||||
call print
|
||||
movl $0, %edx
|
||||
leaq .LC6(%rip), %rcx
|
||||
call print
|
||||
movl $0, %eax
|
||||
addq $32, %rsp
|
||||
popq %rbp
|
||||
ret
|
||||
.seh_endproc
|
||||
.ident "GCC: (Rev2, Built by MSYS2 project) 5.3.0"
|
||||
.def printf; .scl 2; .type 32; .endef
|
||||
.def puts; .scl 2; .type 32; .endef
|
Loading…
Reference in New Issue