aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Instructions/SoftFloat.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ChocolArm64/Instructions/SoftFloat.cs')
-rw-r--r--ChocolArm64/Instructions/SoftFloat.cs701
1 files changed, 543 insertions, 158 deletions
diff --git a/ChocolArm64/Instructions/SoftFloat.cs b/ChocolArm64/Instructions/SoftFloat.cs
index 2af8afbd..39d279de 100644
--- a/ChocolArm64/Instructions/SoftFloat.cs
+++ b/ChocolArm64/Instructions/SoftFloat.cs
@@ -9,191 +9,72 @@ namespace ChocolArm64.Instructions
{
static SoftFloat()
{
- RecipEstimateTable = BuildRecipEstimateTable();
- InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
+ RecipEstimateTable = BuildRecipEstimateTable();
+ RecipSqrtEstimateTable = BuildRecipSqrtEstimateTable();
}
- private static readonly byte[] RecipEstimateTable;
- private static readonly byte[] InvSqrtEstimateTable;
+ internal static readonly byte[] RecipEstimateTable;
+ internal static readonly byte[] RecipSqrtEstimateTable;
private static byte[] BuildRecipEstimateTable()
{
- byte[] table = new byte[256];
- for (ulong index = 0; index < 256; index++)
+ byte[] tbl = new byte[256];
+
+ for (int idx = 0; idx < 256; idx++)
{
- ulong a = index | 0x100;
+ uint src = (uint)idx + 256u;
- a = (a << 1) + 1;
- ulong b = 0x80000 / a;
- b = (b + 1) >> 1;
+ Debug.Assert(256u <= src && src < 512u);
- table[index] = (byte)(b & 0xFF);
- }
- return table;
- }
+ src = (src << 1) + 1u;
- private static byte[] BuildInvSqrtEstimateTable()
- {
- byte[] table = new byte[512];
- for (ulong index = 128; index < 512; index++)
- {
- ulong a = index;
- if (a < 256)
- {
- a = (a << 1) + 1;
- }
- else
- {
- a = (a | 1) << 1;
- }
+ uint aux = (1u << 19) / src;
- ulong b = 256;
- while (a * (b + 1) * (b + 1) < (1ul << 28))
- {
- b++;
- }
- b = (b + 1) >> 1;
+ uint dst = (aux + 1u) >> 1;
+
+ Debug.Assert(256u <= dst && dst < 512u);
- table[index] = (byte)(b & 0xFF);
+ tbl[idx] = (byte)(dst - 256u);
}
- return table;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static float RecipEstimate(float x)
- {
- return (float)RecipEstimate((double)x);
+ return tbl;
}
- public static double RecipEstimate(double x)
+ private static byte[] BuildRecipSqrtEstimateTable()
{
- ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x);
- ulong xSign = xBits & 0x8000000000000000;
- ulong xExp = (xBits >> 52) & 0x7FF;
- ulong scaled = xBits & ((1ul << 52) - 1);
+ byte[] tbl = new byte[384];
- if (xExp >= 2045)
+ for (int idx = 0; idx < 384; idx++)
{
- if (xExp == 0x7ff && scaled != 0)
- {
- // NaN
- return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000));
- }
+ uint src = (uint)idx + 128u;
- // Infinity, or Out of range -> Zero
- return BitConverter.Int64BitsToDouble((long)xSign);
- }
+ Debug.Assert(128u <= src && src < 512u);
- if (xExp == 0)
- {
- if (scaled == 0)
+ if (src < 256u)
{
- // Zero -> Infinity
- return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000));
- }
-
- // Denormal
- if ((scaled & (1ul << 51)) == 0)
- {
- xExp = ~0ul;
- scaled <<= 2;
+ src = (src << 1) + 1u;
}
else
{
- scaled <<= 1;
+ src = (src >> 1) << 1;
+ src = (src + 1u) << 1;
}
- }
-
- scaled >>= 44;
- scaled &= 0xFF;
- ulong resultExp = (2045 - xExp) & 0x7FF;
- ulong estimate = (ulong)RecipEstimateTable[scaled];
- ulong fraction = estimate << 44;
+ uint aux = 512u;
- if (resultExp == 0)
- {
- fraction >>= 1;
- fraction |= 1ul << 51;
- }
- else if (resultExp == 0x7FF)
- {
- resultExp = 0;
- fraction >>= 2;
- fraction |= 1ul << 50;
- }
-
- ulong result = xSign | (resultExp << 52) | fraction;
- return BitConverter.Int64BitsToDouble((long)result);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static float InvSqrtEstimate(float x)
- {
- return (float)InvSqrtEstimate((double)x);
- }
-
- public static double InvSqrtEstimate(double x)
- {
- ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x);
- ulong xSign = xBits & 0x8000000000000000;
- long xExp = (long)((xBits >> 52) & 0x7FF);
- ulong scaled = xBits & ((1ul << 52) - 1);
-
- if (xExp == 0x7FF && scaled != 0)
- {
- // NaN
- return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000));
- }
-
- if (xExp == 0)
- {
- if (scaled == 0)
- {
- // Zero -> Infinity
- return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000));
- }
-
- // Denormal
- while ((scaled & (1 << 51)) == 0)
+ while (src * (aux + 1u) * (aux + 1u) < (1u << 28))
{
- scaled <<= 1;
- xExp--;
+ aux = aux + 1u;
}
- scaled <<= 1;
- }
- if (xSign != 0)
- {
- // Negative -> NaN
- return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000);
- }
+ uint dst = (aux + 1u) >> 1;
- if (xExp == 0x7ff && scaled == 0)
- {
- // Infinity -> Zero
- return BitConverter.Int64BitsToDouble((long)xSign);
- }
+ Debug.Assert(256u <= dst && dst < 512u);
- if (((ulong)xExp & 1) == 1)
- {
- scaled >>= 45;
- scaled &= 0xFF;
- scaled |= 0x80;
+ tbl[idx] = (byte)(dst - 256u);
}
- else
- {
- scaled >>= 44;
- scaled &= 0xFF;
- scaled |= 0x100;
- }
-
- ulong resultExp = ((ulong)(3068 - xExp) / 2) & 0x7FF;
- ulong estimate = (ulong)InvSqrtEstimateTable[scaled];
- ulong fraction = estimate << 44;
- ulong result = xSign | (resultExp << 52) | fraction;
- return BitConverter.Int64BitsToDouble((long)result);
+ return tbl;
}
}
@@ -395,12 +276,12 @@ namespace ChocolArm64.Instructions
{
intMant++;
- if (intMant == (uint)Math.Pow(2d, f))
+ if (intMant == 1u << f)
{
biasedExp = 1u;
}
- if (intMant == (uint)Math.Pow(2d, f + 1))
+ if (intMant == 1u << (f + 1))
{
biasedExp++;
intMant >>= 1;
@@ -409,7 +290,7 @@ namespace ChocolArm64.Instructions
float result;
- if (biasedExp >= (uint)Math.Pow(2d, e) - 1u)
+ if (biasedExp >= (1u << e) - 1u)
{
result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
@@ -666,12 +547,12 @@ namespace ChocolArm64.Instructions
{
intMant++;
- if (intMant == (uint)Math.Pow(2d, f))
+ if (intMant == 1u << f)
{
biasedExp = 1u;
}
- if (intMant == (uint)Math.Pow(2d, f + 1))
+ if (intMant == 1u << (f + 1))
{
biasedExp++;
intMant >>= 1;
@@ -682,7 +563,7 @@ namespace ChocolArm64.Instructions
if (!state.GetFpcrFlag(Fpcr.Ahp))
{
- if (biasedExp >= (uint)Math.Pow(2d, e) - 1u)
+ if (biasedExp >= (1u << e) - 1u)
{
resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
@@ -697,7 +578,7 @@ namespace ChocolArm64.Instructions
}
else
{
- if (biasedExp >= (uint)Math.Pow(2d, e))
+ if (biasedExp >= 1u << e)
{
resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu);
@@ -826,6 +707,94 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static float FPCompareEQ(float value1, float value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareEQ: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ float result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ if (type1 == FpType.SNaN || type2 == FpType.SNaN)
+ {
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 == value2);
+ }
+
+ return result;
+ }
+
+ public static float FPCompareGE(float value1, float value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareGE: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ float result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 >= value2);
+ }
+
+ return result;
+ }
+
+ public static float FPCompareGT(float value1, float value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareGT: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ float result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 > value2);
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float FPCompareLE(float value1, float value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareLE: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ return FPCompareGE(value2, value1, state);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float FPCompareLT(float value1, float value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompareLT: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ return FPCompareGT(value2, value1, state);
+ }
+
public static float FPDiv(float value1, float value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -1188,6 +1157,95 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static float FPRecipEstimate(float value, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecipEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value.FPUnpack(out FpType type, out bool sign, out uint op, state);
+
+ float result;
+
+ if (type == FpType.SNaN || type == FpType.QNaN)
+ {
+ result = FPProcessNaN(type, op, state);
+ }
+ else if (type == FpType.Infinity)
+ {
+ result = FPZero(sign);
+ }
+ else if (type == FpType.Zero)
+ {
+ result = FPInfinity(sign);
+
+ FPProcessException(FpExc.DivideByZero, state);
+ }
+ else if (MathF.Abs(value) < MathF.Pow(2f, -128))
+ {
+ bool overflowToInf;
+
+ switch (state.FPRoundingMode())
+ {
+ default:
+ case RoundMode.ToNearest: overflowToInf = true; break;
+ case RoundMode.TowardsPlusInfinity: overflowToInf = !sign; break;
+ case RoundMode.TowardsMinusInfinity: overflowToInf = sign; break;
+ case RoundMode.TowardsZero: overflowToInf = false; break;
+ }
+
+ result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
+
+ FPProcessException(FpExc.Overflow, state);
+ FPProcessException(FpExc.Inexact, state);
+ }
+ else if (state.GetFpcrFlag(Fpcr.Fz) && (MathF.Abs(value) >= MathF.Pow(2f, 126)))
+ {
+ result = FPZero(sign);
+
+ state.SetFpsrFlag(Fpsr.Ufc);
+ }
+ else
+ {
+ ulong fraction = (ulong)(op & 0x007FFFFFu) << 29;
+ uint exp = (op & 0x7F800000u) >> 23;
+
+ if (exp == 0u)
+ {
+ if ((fraction & 0x0008000000000000ul) == 0ul)
+ {
+ fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2;
+ exp -= 1u;
+ }
+ else
+ {
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ }
+ }
+
+ uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
+
+ uint resultExp = 253u - exp;
+
+ uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u;
+
+ fraction = (ulong)(estimate & 0xFFu) << 44;
+
+ if (resultExp == 0u)
+ {
+ fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1;
+ }
+ else if (resultExp + 1u == 0u)
+ {
+ fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2;
+ resultExp = 0u;
+ }
+
+ result = BitConverter.Int32BitsToSingle(
+ (int)((sign ? 1u : 0u) << 31 | (resultExp & 0xFFu) << 23 | (uint)(fraction >> 29) & 0x007FFFFFu));
+ }
+
+ return result;
+ }
+
public static float FPRecipStepFused(float value1, float value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -1255,6 +1313,71 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static float FPRSqrtEstimate(float value, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRSqrtEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value.FPUnpack(out FpType type, out bool sign, out uint op, state);
+
+ float result;
+
+ if (type == FpType.SNaN || type == FpType.QNaN)
+ {
+ result = FPProcessNaN(type, op, state);
+ }
+ else if (type == FpType.Zero)
+ {
+ result = FPInfinity(sign);
+
+ FPProcessException(FpExc.DivideByZero, state);
+ }
+ else if (sign)
+ {
+ result = FPDefaultNaN();
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else if (type == FpType.Infinity)
+ {
+ result = FPZero(false);
+ }
+ else
+ {
+ ulong fraction = (ulong)(op & 0x007FFFFFu) << 29;
+ uint exp = (op & 0x7F800000u) >> 23;
+
+ if (exp == 0u)
+ {
+ while ((fraction & 0x0008000000000000ul) == 0ul)
+ {
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ exp -= 1u;
+ }
+
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ }
+
+ uint scaled;
+
+ if ((exp & 1u) == 0u)
+ {
+ scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
+ }
+ else
+ {
+ scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45);
+ }
+
+ uint resultExp = (380u - exp) >> 1;
+
+ uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u;
+
+ result = BitConverter.Int32BitsToSingle((int)((resultExp & 0xFFu) << 23 | (estimate & 0xFFu) << 15));
+ }
+
+ return result;
+ }
+
public static float FPRSqrtStepFused(float value1, float value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -1402,6 +1525,11 @@ namespace ChocolArm64.Instructions
return sign ? -0f : +0f;
}
+ private static float FPMaxNormal(bool sign)
+ {
+ return sign ? float.MinValue : float.MaxValue;
+ }
+
private static float FPTwo(bool sign)
{
return sign ? -2f : +2f;
@@ -1417,6 +1545,11 @@ namespace ChocolArm64.Instructions
return -value;
}
+ private static float ZerosOrOnes(bool zeros)
+ {
+ return BitConverter.Int32BitsToSingle(!zeros ? 0 : -1);
+ }
+
private static float FPUnpack(
this float value,
out FpType type,
@@ -1658,6 +1791,94 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static double FPCompareEQ(double value1, double value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareEQ: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ double result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ if (type1 == FpType.SNaN || type2 == FpType.SNaN)
+ {
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 == value2);
+ }
+
+ return result;
+ }
+
+ public static double FPCompareGE(double value1, double value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareGE: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ double result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 >= value2);
+ }
+
+ return result;
+ }
+
+ public static double FPCompareGT(double value1, double value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareGT: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value1 = value1.FPUnpack(out FpType type1, out _, out _, state);
+ value2 = value2.FPUnpack(out FpType type2, out _, out _, state);
+
+ double result;
+
+ if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN)
+ {
+ result = ZerosOrOnes(false);
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else
+ {
+ result = ZerosOrOnes(value1 > value2);
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double FPCompareLE(double value1, double value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareLE: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ return FPCompareGE(value2, value1, state);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double FPCompareLT(double value1, double value2, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompareLT: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ return FPCompareGT(value2, value1, state);
+ }
+
public static double FPDiv(double value1, double value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -2020,6 +2241,95 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static double FPRecipEstimate(double value, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecipEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value.FPUnpack(out FpType type, out bool sign, out ulong op, state);
+
+ double result;
+
+ if (type == FpType.SNaN || type == FpType.QNaN)
+ {
+ result = FPProcessNaN(type, op, state);
+ }
+ else if (type == FpType.Infinity)
+ {
+ result = FPZero(sign);
+ }
+ else if (type == FpType.Zero)
+ {
+ result = FPInfinity(sign);
+
+ FPProcessException(FpExc.DivideByZero, state);
+ }
+ else if (Math.Abs(value) < Math.Pow(2d, -1024))
+ {
+ bool overflowToInf;
+
+ switch (state.FPRoundingMode())
+ {
+ default:
+ case RoundMode.ToNearest: overflowToInf = true; break;
+ case RoundMode.TowardsPlusInfinity: overflowToInf = !sign; break;
+ case RoundMode.TowardsMinusInfinity: overflowToInf = sign; break;
+ case RoundMode.TowardsZero: overflowToInf = false; break;
+ }
+
+ result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign);
+
+ FPProcessException(FpExc.Overflow, state);
+ FPProcessException(FpExc.Inexact, state);
+ }
+ else if (state.GetFpcrFlag(Fpcr.Fz) && (Math.Abs(value) >= Math.Pow(2d, 1022)))
+ {
+ result = FPZero(sign);
+
+ state.SetFpsrFlag(Fpsr.Ufc);
+ }
+ else
+ {
+ ulong fraction = op & 0x000FFFFFFFFFFFFFul;
+ uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52);
+
+ if (exp == 0u)
+ {
+ if ((fraction & 0x0008000000000000ul) == 0ul)
+ {
+ fraction = (fraction & 0x0003FFFFFFFFFFFFul) << 2;
+ exp -= 1u;
+ }
+ else
+ {
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ }
+ }
+
+ uint scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
+
+ uint resultExp = 2045u - exp;
+
+ uint estimate = (uint)SoftFloat.RecipEstimateTable[scaled - 256u] + 256u;
+
+ fraction = (ulong)(estimate & 0xFFu) << 44;
+
+ if (resultExp == 0u)
+ {
+ fraction = ((fraction & 0x000FFFFFFFFFFFFEul) | 0x0010000000000000ul) >> 1;
+ }
+ else if (resultExp + 1u == 0u)
+ {
+ fraction = ((fraction & 0x000FFFFFFFFFFFFCul) | 0x0010000000000000ul) >> 2;
+ resultExp = 0u;
+ }
+
+ result = BitConverter.Int64BitsToDouble(
+ (long)((sign ? 1ul : 0ul) << 63 | (resultExp & 0x7FFul) << 52 | (fraction & 0x000FFFFFFFFFFFFFul)));
+ }
+
+ return result;
+ }
+
public static double FPRecipStepFused(double value1, double value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -2087,6 +2397,71 @@ namespace ChocolArm64.Instructions
return result;
}
+ public static double FPRSqrtEstimate(double value, CpuThreadState state)
+ {
+ Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRSqrtEstimate: state.Fpcr = 0x{state.Fpcr:X8}");
+
+ value.FPUnpack(out FpType type, out bool sign, out ulong op, state);
+
+ double result;
+
+ if (type == FpType.SNaN || type == FpType.QNaN)
+ {
+ result = FPProcessNaN(type, op, state);
+ }
+ else if (type == FpType.Zero)
+ {
+ result = FPInfinity(sign);
+
+ FPProcessException(FpExc.DivideByZero, state);
+ }
+ else if (sign)
+ {
+ result = FPDefaultNaN();
+
+ FPProcessException(FpExc.InvalidOp, state);
+ }
+ else if (type == FpType.Infinity)
+ {
+ result = FPZero(false);
+ }
+ else
+ {
+ ulong fraction = op & 0x000FFFFFFFFFFFFFul;
+ uint exp = (uint)((op & 0x7FF0000000000000ul) >> 52);
+
+ if (exp == 0u)
+ {
+ while ((fraction & 0x0008000000000000ul) == 0ul)
+ {
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ exp -= 1u;
+ }
+
+ fraction = (fraction & 0x0007FFFFFFFFFFFFul) << 1;
+ }
+
+ uint scaled;
+
+ if ((exp & 1u) == 0u)
+ {
+ scaled = (uint)(((fraction & 0x000FF00000000000ul) | 0x0010000000000000ul) >> 44);
+ }
+ else
+ {
+ scaled = (uint)(((fraction & 0x000FE00000000000ul) | 0x0010000000000000ul) >> 45);
+ }
+
+ uint resultExp = (3068u - exp) >> 1;
+
+ uint estimate = (uint)SoftFloat.RecipSqrtEstimateTable[scaled - 128u] + 256u;
+
+ result = BitConverter.Int64BitsToDouble((long)((resultExp & 0x7FFul) << 52 | (estimate & 0xFFul) << 44));
+ }
+
+ return result;
+ }
+
public static double FPRSqrtStepFused(double value1, double value2, CpuThreadState state)
{
Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}");
@@ -2234,6 +2609,11 @@ namespace ChocolArm64.Instructions
return sign ? -0d : +0d;
}
+ private static double FPMaxNormal(bool sign)
+ {
+ return sign ? double.MinValue : double.MaxValue;
+ }
+
private static double FPTwo(bool sign)
{
return sign ? -2d : +2d;
@@ -2249,6 +2629,11 @@ namespace ChocolArm64.Instructions
return -value;
}
+ private static double ZerosOrOnes(bool zeros)
+ {
+ return BitConverter.Int64BitsToDouble(!zeros ? 0L : -1L);
+ }
+
private static double FPUnpack(
this double value,
out FpType type,