aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMerry <MerryMage@users.noreply.github.com>2018-07-12 19:51:02 +0100
committergdkchan <gab.dark.100@gmail.com>2018-07-12 15:51:02 -0300
commitb233ae964fcaae900cdefa6ce51b0edb2892dfaf (patch)
tree53968c715100251ac6ced620ea2b6fe04c1a6d1e
parentcd18ab29dfacd1f7a3218d4ec73ce664bccc3887 (diff)
AInstEmitSimdCvt: Half-precision to single-precision conversion (#235)
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdCvt.cs8
-rw-r--r--ChocolArm64/Instruction/ASoftFloat.cs36
-rw-r--r--Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs40
3 files changed, 80 insertions, 4 deletions
diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
index 98bb972a..da584743 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
@@ -45,10 +45,10 @@ namespace ChocolArm64.Instruction
{
if (SizeF == 0)
{
- //TODO: This need the half precision floating point type,
- //that is not yet supported on .NET. We should probably
- //do our own implementation on the meantime.
- throw new NotImplementedException();
+ EmitVectorExtractZx(Context, Op.Rn, Part + Index, 1);
+ Context.Emit(OpCodes.Conv_U2);
+
+ Context.EmitCall(typeof(ASoftFloat), nameof(ASoftFloat.ConvertHalfToSingle));
}
else /* if (SizeF == 1) */
{
diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs
index e63c82be..27f4f7fb 100644
--- a/ChocolArm64/Instruction/ASoftFloat.cs
+++ b/ChocolArm64/Instruction/ASoftFloat.cs
@@ -225,5 +225,41 @@ namespace ChocolArm64.Instruction
return 2.0 + op1 * op2;
}
+
+ public static float ConvertHalfToSingle(ushort x)
+ {
+ uint x_sign = (uint)(x >> 15) & 0x0001;
+ uint x_exp = (uint)(x >> 10) & 0x001F;
+ uint x_mantissa = (uint)x & 0x03FF;
+
+ if (x_exp == 0 && x_mantissa == 0)
+ {
+ // 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)
+ {
+ x_mantissa <<= 1;
+ exponent--;
+ }
+ x_mantissa &= 0x03FF;
+ }
+
+ uint new_exp = (uint)((exponent + 127) & 0xFF) << 23;
+ return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13)));
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs
new file mode 100644
index 00000000..2d021616
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs
@@ -0,0 +1,40 @@
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace Ryujinx.Tests.Cpu
+{
+ public class CpuTestSimdCvt : CpuTest
+ {
+ [TestCase((ushort)0x0000, 0x00000000u)] // Positive Zero
+ [TestCase((ushort)0x8000, 0x80000000u)] // Negative Zero
+ [TestCase((ushort)0x3E00, 0x3FC00000u)] // +1.5
+ [TestCase((ushort)0xBE00, 0xBFC00000u)] // -1.5
+ [TestCase((ushort)0xFFFF, 0xFFFFE000u)] // -QNaN
+ [TestCase((ushort)0x7C00, 0x7F800000u)] // +Inf
+ [TestCase((ushort)0x3C00, 0x3F800000u)] // 1.0
+ [TestCase((ushort)0x3C01, 0x3F802000u)] // 1.0009765625
+ [TestCase((ushort)0xC000, 0xC0000000u)] // -2.0
+ [TestCase((ushort)0x7BFF, 0x477FE000u)] // 65504.0 (Largest Normal)
+ [TestCase((ushort)0x03FF, 0x387FC000u)] // 0.00006097555 (Largest Subnormal)
+ [TestCase((ushort)0x0001, 0x33800000u)] // 5.96046448e-8 (Smallest Subnormal)
+ public void Fcvtl_V_f16(ushort Value, uint Result)
+ {
+ uint Opcode = 0x0E217801;
+ Vector128<float> V0 = Sse.StaticCast<ushort, float>(Sse2.SetAllVector128(Value));
+
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)0), Is.EqualTo(Result));
+ Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)1), Is.EqualTo(Result));
+ Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)2), Is.EqualTo(Result));
+ Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)3), Is.EqualTo(Result));
+ });
+ }
+ }
+}