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:
nattthebear 2017-06-18 08:49:55 -04:00
parent d7423d45a0
commit 1292b27163
24 changed files with 2596 additions and 1361 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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!");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
using BizHawk.Common;
using BizHawk.Common.BizInvoke;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

View File

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

2
waterbox/thunk/build.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
gcc test.c -o test.exe -Wall

20
waterbox/thunk/gen.cs Normal file
View File

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

103
waterbox/thunk/test.c Normal file
View File

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

BIN
waterbox/thunk/test.exe Normal file

Binary file not shown.

682
waterbox/thunk/test.s Normal file
View File

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