fix #3726
This commit is contained in:
parent
1cf5af83dd
commit
8260a59bcf
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace BizHawk.BizInvoke
|
||||||
|
{
|
||||||
|
// .NET is weird and sets the x87 control register to double precision
|
||||||
|
// It does this even on Linux which normally expects it set to extended x87 precision
|
||||||
|
// Even stranger is that this appears to be completely unneeded on x86-64
|
||||||
|
// On Windows, x87 registers are prohibited to be used in kernel code, and
|
||||||
|
// MSVC will not use the x87 registers (and presumably this extends to userland OS code)
|
||||||
|
// .NET presumbly follows MSVC and would not use the x87 registers (why would it? SSE is available! long double doesn't exist!)
|
||||||
|
// This thus only screws over code compiled with MinGW (which use extended x87 precision for long double)
|
||||||
|
// Or screws over any unmanaged code on Linux, which doubly includes all waterbox cores (!!!)
|
||||||
|
// Of course in practice, this only applies to any code using long double, which is rarely used typically
|
||||||
|
// But musl (used for waterbox cores) does end up using it for float formating in the printf family
|
||||||
|
// This can extend to issues in games: https://github.com/TASEmulators/BizHawk/issues/3726
|
||||||
|
public static class FPCtrl
|
||||||
|
{
|
||||||
|
private const CallingConvention cc = CallingConvention.Cdecl;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(cc)]
|
||||||
|
private delegate void FixFPCtrlDelegate();
|
||||||
|
|
||||||
|
private static readonly MemoryBlock? _memory;
|
||||||
|
private static readonly FixFPCtrlDelegate? _fixFpCtrl;
|
||||||
|
|
||||||
|
static FPCtrl()
|
||||||
|
{
|
||||||
|
// not usable outside of x64
|
||||||
|
if (RuntimeInformation.ProcessArchitecture != Architecture.X64)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate assembly for fixing the x87 control register
|
||||||
|
_memory = new(4096);
|
||||||
|
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RW);
|
||||||
|
var ss = _memory.GetStream(_memory.Start, 64, true);
|
||||||
|
var bw = new BinaryWriter(ss);
|
||||||
|
|
||||||
|
// FYI: The push/pop is only needed on Windows, but doesn't do any harm on Linux
|
||||||
|
|
||||||
|
bw.Write((byte)0x50); // push rax
|
||||||
|
bw.Write(0x06247CD9); // fnstcw word[rsp + 6]
|
||||||
|
bw.Write(0x07244C80); // or byte[rsp + 7], 3
|
||||||
|
bw.Write((byte)0x03);
|
||||||
|
bw.Write(0x06246CD9); // fldcw word[rsp + 6]
|
||||||
|
bw.Write((byte)0x58); // pop rax
|
||||||
|
bw.Write((byte)0xC3); // ret
|
||||||
|
|
||||||
|
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RX);
|
||||||
|
_fixFpCtrl = Marshal.GetDelegateForFunctionPointer<FixFPCtrlDelegate>((IntPtr)_memory.Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FixFPCtrl()
|
||||||
|
{
|
||||||
|
// not usable outside of x64
|
||||||
|
if (RuntimeInformation.ProcessArchitecture != Architecture.X64)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fixFpCtrl!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using BizHawk.BizInvoke;
|
||||||
using BizHawk.Bizware.BizwareGL;
|
using BizHawk.Bizware.BizwareGL;
|
||||||
using BizHawk.Bizware.Graphics;
|
using BizHawk.Bizware.Graphics;
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
|
@ -285,6 +286,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FPCtrl.FixFPCtrl();
|
||||||
|
|
||||||
var exitCode = 0;
|
var exitCode = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -29,12 +28,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
||||||
|
|
||||||
[CoreConstructor(VSystemID.Raw.NES, Priority = CorePriority.Low)]
|
[CoreConstructor(VSystemID.Raw.NES, Priority = CorePriority.Low)]
|
||||||
public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings)
|
public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings)
|
||||||
{
|
|
||||||
FP = OSTailoredCode.IsUnixHost
|
|
||||||
? (IFPCtrl) new Unix_FPCtrl()
|
|
||||||
: new Win32_FPCtrl();
|
|
||||||
|
|
||||||
using (FP.Save())
|
|
||||||
{
|
{
|
||||||
ServiceProvider = new BasicServiceProvider(this);
|
ServiceProvider = new BasicServiceProvider(this);
|
||||||
Context = QN.qn_new();
|
Context = QN.qn_new();
|
||||||
|
@ -73,7 +66,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly LibQuickNES QN;
|
private static readonly LibQuickNES QN;
|
||||||
|
|
||||||
|
@ -83,40 +75,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
||||||
|
|
||||||
int IVideoLogicalOffsets.ScreenY => _settings.ClipTopAndBottom ? 8 : 0;
|
int IVideoLogicalOffsets.ScreenY => _settings.ClipTopAndBottom ? 8 : 0;
|
||||||
|
|
||||||
private interface IFPCtrl : IDisposable
|
|
||||||
{
|
|
||||||
IDisposable Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Win32_FPCtrl : IFPCtrl
|
|
||||||
{
|
|
||||||
[Conditional("DEBUG")]
|
|
||||||
public static void PrintCurrentFP() => Console.WriteLine($"Current FP word: 0x{Win32Imports._control87(0, 0):X8}");
|
|
||||||
|
|
||||||
private uint cw;
|
|
||||||
|
|
||||||
public IDisposable Save()
|
|
||||||
{
|
|
||||||
cw = Win32Imports._control87(0, 0);
|
|
||||||
Win32Imports._control87(0x00000, 0x30000);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Win32Imports._control87(cw, 0x30000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Unix_FPCtrl : IFPCtrl
|
|
||||||
{
|
|
||||||
public IDisposable Save() => this;
|
|
||||||
|
|
||||||
public void Dispose() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly IFPCtrl FP;
|
|
||||||
|
|
||||||
public ControllerDefinition ControllerDefinition { get; private set; }
|
public ControllerDefinition ControllerDefinition { get; private set; }
|
||||||
|
|
||||||
private void SetControllerDefinition()
|
private void SetControllerDefinition()
|
||||||
|
@ -185,8 +143,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
||||||
public bool FrameAdvance(IController controller, bool render, bool rendersound = true)
|
public bool FrameAdvance(IController controller, bool render, bool rendersound = true)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
using (FP.Save())
|
|
||||||
{
|
|
||||||
if (controller.IsPressed("Power"))
|
if (controller.IsPressed("Power"))
|
||||||
QN.qn_reset(Context, true);
|
QN.qn_reset(Context, true);
|
||||||
if (controller.IsPressed("Reset"))
|
if (controller.IsPressed("Reset"))
|
||||||
|
@ -213,7 +170,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private IntPtr Context;
|
private IntPtr Context;
|
||||||
public int Frame { get; private set; }
|
public int Frame { get; private set; }
|
||||||
|
|
Loading…
Reference in New Issue