From 416fe26df00538fac19cac1b88edd2894ab08c08 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Mon, 1 Feb 2021 17:01:29 -0500
Subject: [PATCH] thumb32: Implement QSAX/UQSAX

---
 src/frontend/A32/decoder/thumb32.h            |  4 +-
 .../A32/translate/impl/thumb32_parallel.cpp   | 38 +++++++++++++++++++
 .../A32/translate/impl/translate_thumb.h      |  2 +
 tests/A32/fuzz_thumb.cpp                      |  4 ++
 4 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/src/frontend/A32/decoder/thumb32.h b/src/frontend/A32/decoder/thumb32.h
index 15f0abc3..18331827 100644
--- a/src/frontend/A32/decoder/thumb32.h
+++ b/src/frontend/A32/decoder/thumb32.h
@@ -243,7 +243,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         INST(&V::thumb32_SSUB8,          "SSUB8",                    "111110101100nnnn1111dddd0000mmmm"),
         INST(&V::thumb32_QADD16,         "QADD16",                   "111110101001nnnn1111dddd0001mmmm"),
         INST(&V::thumb32_QASX,           "QASX",                     "111110101010nnnn1111dddd0001mmmm"),
-        //INST(&V::thumb32_QSAX,           "QSAX",                     "111110101110----1111----0001----"),
+        INST(&V::thumb32_QSAX,           "QSAX",                     "111110101110nnnn1111dddd0001mmmm"),
         //INST(&V::thumb32_QSUB16,         "QSUB16",                   "111110101101----1111----0001----"),
         //INST(&V::thumb32_QADD8,          "QADD8",                    "111110101000----1111----0001----"),
         //INST(&V::thumb32_QSUB8,          "QSUB8",                    "111110101100----1111----0001----"),
@@ -263,7 +263,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         INST(&V::thumb32_USUB8,          "USUB8",                    "111110101100nnnn1111dddd0100mmmm"),
         INST(&V::thumb32_UQADD16,        "UQADD16",                  "111110101001nnnn1111dddd0101mmmm"),
         INST(&V::thumb32_UQASX,          "UQASX",                    "111110101010nnnn1111dddd0101mmmm"),
-        //INST(&V::thumb32_UQSAX,          "UQSAX",                    "111110101110----1111----0101----"),
+        INST(&V::thumb32_UQSAX,          "UQSAX",                    "111110101110nnnn1111dddd0101mmmm"),
         //INST(&V::thumb32_UQSUB16,        "UQSUB16",                  "111110101101----1111----0101----"),
         //INST(&V::thumb32_UQADD8,         "UQADD8",                   "111110101000----1111----0101----"),
         //INST(&V::thumb32_UQSUB8,         "UQSUB8",                   "111110101100----1111----0101----"),
diff --git a/src/frontend/A32/translate/impl/thumb32_parallel.cpp b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
index 1efbb22b..9bd865ae 100644
--- a/src/frontend/A32/translate/impl/thumb32_parallel.cpp
+++ b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
@@ -214,6 +214,25 @@ bool ThumbTranslatorVisitor::thumb32_QASX(Reg n, Reg d, Reg m) {
     return true;
 }
 
+bool ThumbTranslatorVisitor::thumb32_QSAX(Reg n, Reg d, Reg m) {
+    if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+
+    const auto Rn = ir.GetRegister(n);
+    const auto Rm = ir.GetRegister(m);
+    const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
+    const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
+    const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
+    const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
+    const auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
+    const auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
+    const auto result = Pack2x16To1x32(ir, sum, diff);
+
+    ir.SetRegister(d, result);
+    return true;
+}
+
 bool ThumbTranslatorVisitor::thumb32_UQADD16(Reg n, Reg d, Reg m) {
     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
         return UnpredictableInstruction();
@@ -246,4 +265,23 @@ bool ThumbTranslatorVisitor::thumb32_UQASX(Reg n, Reg d, Reg m) {
     return true;
 }
 
+bool ThumbTranslatorVisitor::thumb32_UQSAX(Reg n, Reg d, Reg m) {
+    if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+
+    const auto Rn = ir.GetRegister(n);
+    const auto Rm = ir.GetRegister(m);
+    const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
+    const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
+    const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
+    const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
+    const auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
+    const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
+    const auto result = Pack2x16To1x32(ir, sum, diff);
+
+    ir.SetRegister(d, result);
+    return true;
+}
+
 } // namespace Dynarmic::A32
diff --git a/src/frontend/A32/translate/impl/translate_thumb.h b/src/frontend/A32/translate/impl/translate_thumb.h
index c5ea19dd..16e1233e 100644
--- a/src/frontend/A32/translate/impl/translate_thumb.h
+++ b/src/frontend/A32/translate/impl/translate_thumb.h
@@ -144,8 +144,10 @@ struct ThumbTranslatorVisitor final {
 
     bool thumb32_QADD16(Reg n, Reg d, Reg m);
     bool thumb32_QASX(Reg n, Reg d, Reg m);
+    bool thumb32_QSAX(Reg n, Reg d, Reg m);
     bool thumb32_UQADD16(Reg n, Reg d, Reg m);
     bool thumb32_UQASX(Reg n, Reg d, Reg m);
+    bool thumb32_UQSAX(Reg n, Reg d, Reg m);
 };
 
 } // namespace Dynarmic::A32
diff --git a/tests/A32/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp
index 4ff5a482..9d9fda33 100644
--- a/tests/A32/fuzz_thumb.cpp
+++ b/tests/A32/fuzz_thumb.cpp
@@ -386,6 +386,8 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
                      three_reg_not_r15),
         ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB
                      three_reg_not_r15),
+        ThumbInstGen("111110101110nnnn1111dddd0001mmmm", // QSAX
+                     three_reg_not_r15),
         ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB
                      three_reg_not_r15),
         ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT
@@ -440,6 +442,8 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
                      three_reg_not_r15),
         ThumbInstGen("111110101010nnnn1111dddd0101mmmm", // UQASX
                      three_reg_not_r15),
+        ThumbInstGen("111110101110nnnn1111dddd0101mmmm", // UQSAX
+                     three_reg_not_r15),
         ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX
                      three_reg_not_r15),
         ThumbInstGen("111110101100nnnn1111dddd0100mmmm", // USUB8