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