mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-30 01:29:48 -06:00
183 lines
5.0 KiB
C#
183 lines
5.0 KiB
C#
using ARMeilleure.State;
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace ARMeilleure.Instructions
|
|
{
|
|
static class SoftFloat16_32
|
|
{
|
|
[UnmanagedCallersOnly]
|
|
public static float FPConvert(ushort valueBits)
|
|
{
|
|
ExecutionContext context = NativeInterface.GetContext();
|
|
|
|
double real = valueBits.FPUnpackCv(out FPType type, out bool sign, context);
|
|
|
|
float result;
|
|
|
|
if (type is FPType.SNaN or FPType.QNaN)
|
|
{
|
|
if ((context.Fpcr & FPCR.Dn) != 0)
|
|
{
|
|
result = SoftFloat32.FPDefaultNaN();
|
|
}
|
|
else
|
|
{
|
|
result = FPConvertNaN(valueBits);
|
|
}
|
|
|
|
if (type == FPType.SNaN)
|
|
{
|
|
SoftFloat.FPProcessException(FPException.InvalidOp, context);
|
|
}
|
|
}
|
|
else if (type == FPType.Infinity)
|
|
{
|
|
result = SoftFloat32.FPInfinity(sign);
|
|
}
|
|
else if (type == FPType.Zero)
|
|
{
|
|
result = SoftFloat32.FPZero(sign);
|
|
}
|
|
else
|
|
{
|
|
result = FPRoundCv(real, context);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static float FPRoundCv(double real, ExecutionContext context)
|
|
{
|
|
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 ((context.Fpcr & FPCR.Fz) != 0 && exponent < MinimumExp)
|
|
{
|
|
context.Fpsr |= FPSR.Ufc;
|
|
|
|
return SoftFloat32.FPZero(sign);
|
|
}
|
|
|
|
uint biasedExp = (uint)Math.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 || (context.Fpcr & FPCR.Ufe) != 0))
|
|
{
|
|
SoftFloat.FPProcessException(FPException.Underflow, context);
|
|
}
|
|
|
|
bool overflowToInf;
|
|
bool roundUp;
|
|
|
|
switch (context.Fpcr.RoundingMode)
|
|
{
|
|
case FPRoundingMode.ToNearest:
|
|
roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u));
|
|
overflowToInf = true;
|
|
break;
|
|
|
|
case FPRoundingMode.TowardsPlusInfinity:
|
|
roundUp = (error != 0d && !sign);
|
|
overflowToInf = !sign;
|
|
break;
|
|
|
|
case FPRoundingMode.TowardsMinusInfinity:
|
|
roundUp = (error != 0d && sign);
|
|
overflowToInf = sign;
|
|
break;
|
|
|
|
case FPRoundingMode.TowardsZero:
|
|
roundUp = false;
|
|
overflowToInf = false;
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentException($"Invalid rounding mode \"{context.Fpcr.RoundingMode}\".");
|
|
}
|
|
|
|
if (roundUp)
|
|
{
|
|
intMant++;
|
|
|
|
if (intMant == 1u << F)
|
|
{
|
|
biasedExp = 1u;
|
|
}
|
|
|
|
if (intMant == 1u << (F + 1))
|
|
{
|
|
biasedExp++;
|
|
intMant >>= 1;
|
|
}
|
|
}
|
|
|
|
float result;
|
|
|
|
if (biasedExp >= (1u << E) - 1u)
|
|
{
|
|
result = overflowToInf ? SoftFloat32.FPInfinity(sign) : SoftFloat32.FPMaxNormal(sign);
|
|
|
|
SoftFloat.FPProcessException(FPException.Overflow, context);
|
|
|
|
error = 1d;
|
|
}
|
|
else
|
|
{
|
|
result = BitConverter.Int32BitsToSingle(
|
|
(int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu)));
|
|
}
|
|
|
|
if (error != 0d)
|
|
{
|
|
SoftFloat.FPProcessException(FPException.Inexact, context);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static float FPConvertNaN(ushort valueBits)
|
|
{
|
|
return BitConverter.Int32BitsToSingle(
|
|
(int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13));
|
|
}
|
|
}
|
|
}
|