From f5952722266879c84d260e8d9dfd2a4d0c27fe56 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sun, 21 Oct 2018 19:18:32 +0200 Subject: [PATCH] Update ASoftFloat.cs --- ChocolArm64/Instruction/ASoftFloat.cs | 652 ++++++++++++++++++++++++-- 1 file changed, 625 insertions(+), 27 deletions(-) diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index 2d9a9f0ebe..c2fac58d58 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -195,41 +195,639 @@ namespace ChocolArm64.Instruction ulong result = x_sign | (result_exp << 52) | fraction; return BitConverter.Int64BitsToDouble((long)result); } + } - public static float ConvertHalfToSingle(ushort x) + static class ASoftFloat16_32 + { + public static float FPConvert(ushort ValueBits, AThreadState State) { - uint x_sign = (uint)(x >> 15) & 0x0001; - uint x_exp = (uint)(x >> 10) & 0x001F; - uint x_mantissa = (uint)x & 0x03FF; + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat16_32.FPConvert: State.Fpcr = 0x{State.Fpcr:X8}"); - if (x_exp == 0 && x_mantissa == 0) + double Real = ValueBits.FPUnpackCV(out FPType Type, out bool Sign, State); + + float Result; + + if (Type == FPType.SNaN || Type == FPType.QNaN) { - // Zero - return BitConverter.Int32BitsToSingle((int)(x_sign << 31)); - } - - if (x_exp == 0x1F) - { - // NaN or Infinity - return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | 0x7F800000 | (x_mantissa << 13))); - } - - int exponent = (int)x_exp - 15; - - if (x_exp == 0) - { - // Denormal - x_mantissa <<= 1; - while ((x_mantissa & 0x0400) == 0) + if (State.GetFpcrFlag(FPCR.DN)) { - x_mantissa <<= 1; - exponent--; + Result = FPDefaultNaN(); } - x_mantissa &= 0x03FF; + else + { + Result = FPConvertNaN(ValueBits); + } + + if (Type == FPType.SNaN) + { + FPProcessException(FPExc.InvalidOp, State); + } + } + else if (Type == FPType.Infinity) + { + Result = FPInfinity(Sign); + } + else if (Type == FPType.Zero) + { + Result = FPZero(Sign); + } + else + { + Result = FPRoundCV(Real, State); } - uint new_exp = (uint)((exponent + 127) & 0xFF) << 23; - return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13))); + return Result; + } + + private enum FPType + { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN + } + + private enum FPExc + { + InvalidOp, + DivideByZero, + Overflow, + Underflow, + Inexact, + InputDenorm = 7 + } + + private enum FPRounding + { + TIEEVEN, + POSINF, + NEGINF, + ZERO + }; + + private enum FPCR + { + UFE = 11, + RMode = 22, + FZ = 24, + DN = 25, + AHP = 26 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPDefaultNaN() + { + return -float.NaN; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPInfinity(bool Sign) + { + return Sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPZero(bool Sign) + { + return Sign ? -0f : +0f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPMaxNormal(bool Sign) + { + return Sign ? float.MinValue : float.MaxValue; + } + + private static double FPUnpackCV(this ushort ValueBits, out FPType Type, out bool Sign, AThreadState State) + { + Sign = (~(uint)ValueBits & 0x8000u) == 0u; + + uint Exp16 = ((uint)ValueBits & 0x7C00u) >> 10; + uint Frac16 = (uint)ValueBits & 0x03FFu; + + double Real; + + if (Exp16 == 0u) + { + if (Frac16 == 0u) + { + Type = FPType.Zero; + Real = 0d; + } + else + { + Type = FPType.Nonzero; // Subnormal. + Real = Math.Pow(2d, -14) * ((double)Frac16 * Math.Pow(2d, -10)); + } + } + else if (Exp16 == 0x1Fu && !State.GetFpcrFlag(FPCR.AHP)) + { + if (Frac16 == 0u) + { + Type = FPType.Infinity; + Real = Math.Pow(2d, 1000); + } + else + { + Type = (~Frac16 & 0x0200u) == 0u ? FPType.QNaN : FPType.SNaN; + Real = 0d; + } + } + else + { + Type = FPType.Nonzero; // Normal. + Real = Math.Pow(2d, (int)Exp16 - 15) * (1d + (double)Frac16 * Math.Pow(2d, -10)); + } + + return Sign ? -Real : Real; + } + + private static float FPRoundCV(double Real, AThreadState State) + { + const int UFCBit = 3; // Underflow cumulative floating-point exception bit. + + const int MinimumExp = -126; + + const int E = 8; + const int F = 23; + + bool Sign; + double Mantissa; + + if (Real < 0d) + { + Sign = true; + Mantissa = -Real; + } + else + { + Sign = false; + Mantissa = Real; + } + + int Exponent = 0; + + while (Mantissa < 1d) + { + Mantissa *= 2d; + Exponent--; + } + + while (Mantissa >= 2d) + { + Mantissa /= 2d; + Exponent++; + } + + if (State.GetFpcrFlag(FPCR.FZ) && Exponent < MinimumExp) + { + State.Fpsr |= 1 << UFCBit; + + return FPZero(Sign); + } + + uint BiasedExp = (uint)Max(Exponent - MinimumExp + 1, 0); + if (BiasedExp == 0u) Mantissa /= Math.Pow(2d, MinimumExp - Exponent); + + uint IntMant = (uint)Math.Floor(Mantissa * Math.Pow(2d, F)); + double Error = Mantissa * Math.Pow(2d, F) - (double)IntMant; + + if (BiasedExp == 0u && (Error != 0d || State.GetFpcrFlag(FPCR.UFE))) + { + FPProcessException(FPExc.Underflow, State); + } + + bool OverflowToInf; + bool RoundUp; + + switch (FPRoundingMode(State)) + { + default: + case FPRounding.TIEEVEN: + RoundUp = (Error > 0.5d || (Error == 0.5d && (IntMant & 1u) == 1u)); + OverflowToInf = true; + break; + + case FPRounding.POSINF: + RoundUp = (Error != 0d && !Sign); + OverflowToInf = !Sign; + break; + + case FPRounding.NEGINF: + RoundUp = (Error != 0d && Sign); + OverflowToInf = Sign; + break; + + case FPRounding.ZERO: + RoundUp = false; + OverflowToInf = false; + break; + } + + if (RoundUp) + { + IntMant++; + + if (IntMant == (uint)Math.Pow(2d, F)) + { + BiasedExp = 1u; + } + + if (IntMant == (uint)Math.Pow(2d, F + 1)) + { + BiasedExp++; + IntMant >>= 1; + } + } + + float Result; + + if (BiasedExp >= (uint)Math.Pow(2d, E) - 1) + { + Result = OverflowToInf ? FPInfinity(Sign) : FPMaxNormal(Sign); + + FPProcessException(FPExc.Overflow, State); + + Error = 1d; + } + else + { + Result = BitConverter.Int32BitsToSingle( + (int)((Sign ? 1u : 0u) << 31 | (BiasedExp & 0xFFu) << 23 | (IntMant & 0x007FFFFFu))); + } + + if (Error != 0d) + { + FPProcessException(FPExc.Inexact, State); + } + + return Result; + + int Max(int Left, int Right) => Left >= Right ? Left : Right; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPConvertNaN(ushort ValueBits) + { + return BitConverter.Int32BitsToSingle( + (int)(((uint)ValueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)ValueBits & 0x01FFu) << 13)); + } + + private static void FPProcessException(FPExc Exc, AThreadState State) + { + int Enable = (int)Exc + 8; + + if ((State.Fpcr & (1 << Enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + State.Fpsr |= 1 << (int)Exc; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FPRounding FPRoundingMode(AThreadState State) + { + return (FPRounding)((State.Fpcr >> (int)FPCR.RMode) & 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool GetFpcrFlag(this AThreadState State, FPCR Fpcr) + { + return (State.Fpcr & (1 << (int)Fpcr)) != 0; + } + } + + static class ASoftFloat32_16 + { + public static ushort FPConvert(float Value, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat32_16.FPConvert: State.Fpcr = 0x{State.Fpcr:X8}"); + + double Real = Value.FPUnpackCV(out FPType Type, out bool Sign, State, out uint ValueBits); + + bool AltHp = State.GetFpcrFlag(FPCR.AHP); + + ushort ResultBits; + + if (Type == FPType.SNaN || Type == FPType.QNaN) + { + if (AltHp) + { + ResultBits = FPZero(Sign); + } + else if (State.GetFpcrFlag(FPCR.DN)) + { + ResultBits = FPDefaultNaN(); + } + else + { + ResultBits = FPConvertNaN(ValueBits); + } + + if (Type == FPType.SNaN || AltHp) + { + FPProcessException(FPExc.InvalidOp, State); + } + } + else if (Type == FPType.Infinity) + { + if (AltHp) + { + ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FPExc.InvalidOp, State); + } + else + { + ResultBits = FPInfinity(Sign); + } + } + else if (Type == FPType.Zero) + { + ResultBits = FPZero(Sign); + } + else + { + ResultBits = FPRoundCV(Real, State); + } + + return ResultBits; + } + + private enum FPType + { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN + } + + private enum FPExc + { + InvalidOp, + DivideByZero, + Overflow, + Underflow, + Inexact, + InputDenorm = 7 + } + + private enum FPRounding + { + TIEEVEN, + POSINF, + NEGINF, + ZERO + }; + + private enum FPCR + { + UFE = 11, + RMode = 22, + FZ = 24, + DN = 25, + AHP = 26 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort FPDefaultNaN() + { + return (ushort)0x7E00u; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort FPInfinity(bool Sign) + { + return Sign ? (ushort)0xFC00u : (ushort)0x7C00u; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort FPZero(bool Sign) + { + return Sign ? (ushort)0x8000u : (ushort)0x0000u; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort FPMaxNormal(bool Sign) + { + return Sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; + } + + private static double FPUnpackCV(this float Value, out FPType Type, out bool Sign, AThreadState State, out uint ValueBits) + { + ValueBits = (uint)BitConverter.SingleToInt32Bits(Value); + + Sign = (~ValueBits & 0x80000000u) == 0u; + + uint Exp32 = (ValueBits & 0x7F800000u) >> 23; + uint Frac32 = ValueBits & 0x007FFFFFu; + + double Real; + + if (Exp32 == 0u) + { + if (Frac32 == 0u || State.GetFpcrFlag(FPCR.FZ)) + { + Type = FPType.Zero; + Real = 0d; + + if (Frac32 != 0u) FPProcessException(FPExc.InputDenorm, State); + } + else + { + Type = FPType.Nonzero; // Subnormal. + Real = Math.Pow(2d, -126) * ((double)Frac32 * Math.Pow(2d, -23)); + } + } + else if (Exp32 == 0xFFu) + { + if (Frac32 == 0u) + { + Type = FPType.Infinity; + Real = Math.Pow(2d, 1000); + } + else + { + Type = (~Frac32 & 0x00400000u) == 0u ? FPType.QNaN : FPType.SNaN; + Real = 0d; + } + } + else + { + Type = FPType.Nonzero; // Normal. + Real = Math.Pow(2d, (int)Exp32 - 127) * (1d + (double)Frac32 * Math.Pow(2d, -23)); + } + + return Sign ? -Real : Real; + } + + private static ushort FPRoundCV(double Real, AThreadState State) + { + const int MinimumExp = -14; + + const int E = 5; + const int F = 10; + + bool Sign; + double Mantissa; + + if (Real < 0d) + { + Sign = true; + Mantissa = -Real; + } + else + { + Sign = false; + Mantissa = Real; + } + + int Exponent = 0; + + while (Mantissa < 1d) + { + Mantissa *= 2d; + Exponent--; + } + + while (Mantissa >= 2d) + { + Mantissa /= 2d; + Exponent++; + } + + uint BiasedExp = (uint)Max(Exponent - MinimumExp + 1, 0); + if (BiasedExp == 0u) Mantissa /= Math.Pow(2d, MinimumExp - Exponent); + + uint IntMant = (uint)Math.Floor(Mantissa * Math.Pow(2d, F)); + double Error = Mantissa * Math.Pow(2d, F) - (double)IntMant; + + if (BiasedExp == 0u && (Error != 0d || State.GetFpcrFlag(FPCR.UFE))) + { + FPProcessException(FPExc.Underflow, State); + } + + bool OverflowToInf; + bool RoundUp; + + switch (FPRoundingMode(State)) + { + default: + case FPRounding.TIEEVEN: + RoundUp = (Error > 0.5d || (Error == 0.5d && (IntMant & 1u) == 1u)); + OverflowToInf = true; + break; + + case FPRounding.POSINF: + RoundUp = (Error != 0d && !Sign); + OverflowToInf = !Sign; + break; + + case FPRounding.NEGINF: + RoundUp = (Error != 0d && Sign); + OverflowToInf = Sign; + break; + + case FPRounding.ZERO: + RoundUp = false; + OverflowToInf = false; + break; + } + + if (RoundUp) + { + IntMant++; + + if (IntMant == (uint)Math.Pow(2d, F)) + { + BiasedExp = 1u; + } + + if (IntMant == (uint)Math.Pow(2d, F + 1)) + { + BiasedExp++; + IntMant >>= 1; + } + } + + ushort ResultBits; + + if (!State.GetFpcrFlag(FPCR.AHP)) + { + if (BiasedExp >= (uint)Math.Pow(2d, E) - 1) + { + ResultBits = OverflowToInf ? FPInfinity(Sign) : FPMaxNormal(Sign); + + FPProcessException(FPExc.Overflow, State); + + Error = 1d; + } + else + { + ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | (BiasedExp & 0x1Fu) << 10 | (IntMant & 0x03FFu)); + } + } + else + { + if (BiasedExp >= (uint)Math.Pow(2d, E)) + { + ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FPExc.InvalidOp, State); + + Error = 0d; + } + else + { + ResultBits = (ushort)((Sign ? 1u : 0u) << 15 | (BiasedExp & 0x1Fu) << 10 | (IntMant & 0x03FFu)); + } + } + + if (Error != 0d) + { + FPProcessException(FPExc.Inexact, State); + } + + return ResultBits; + + int Max(int Left, int Right) => Left >= Right ? Left : Right; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort FPConvertNaN(uint ValueBits) + { + return (ushort)((ValueBits & 0x80000000u) >> 16 | 0x7E00u | (ValueBits & 0x003FE000u) >> 13); + } + + private static void FPProcessException(FPExc Exc, AThreadState State) + { + int Enable = (int)Exc + 8; + + if ((State.Fpcr & (1 << Enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + State.Fpsr |= 1 << (int)Exc; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FPRounding FPRoundingMode(AThreadState State) + { + return (FPRounding)((State.Fpcr >> (int)FPCR.RMode) & 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool GetFpcrFlag(this AThreadState State, FPCR Fpcr) + { + return (State.Fpcr & (1 << (int)Fpcr)) != 0; } }