From 94f8efbb033e9838148009f6cff4e536c089e1c0 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Mon, 1 Feb 2021 17:13:44 -0500
Subject: [PATCH] thumb32: Implement SHADD16/UHADD16

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

diff --git a/src/frontend/A32/decoder/thumb32.h b/src/frontend/A32/decoder/thumb32.h
index 24e1ec56..7b9c5c01 100644
--- a/src/frontend/A32/decoder/thumb32.h
+++ b/src/frontend/A32/decoder/thumb32.h
@@ -247,7 +247,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         INST(&V::thumb32_QSUB16,         "QSUB16",                   "111110101101nnnn1111dddd0001mmmm"),
         INST(&V::thumb32_QADD8,          "QADD8",                    "111110101000nnnn1111dddd0001mmmm"),
         INST(&V::thumb32_QSUB8,          "QSUB8",                    "111110101100nnnn1111dddd0001mmmm"),
-        //INST(&V::thumb32_SHADD16,        "SHADD16",                  "111110101001----1111----0010----"),
+        INST(&V::thumb32_SHADD16,        "SHADD16",                  "111110101001nnnn1111dddd0010mmmm"),
         //INST(&V::thumb32_SHASX,          "SHASX",                    "111110101010----1111----0010----"),
         //INST(&V::thumb32_SHSAX,          "SHSAX",                    "111110101110----1111----0010----"),
         //INST(&V::thumb32_SHSUB16,        "SHSUB16",                  "111110101101----1111----0010----"),
@@ -267,7 +267,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         INST(&V::thumb32_UQSUB16,        "UQSUB16",                  "111110101101nnnn1111dddd0101mmmm"),
         INST(&V::thumb32_UQADD8,         "UQADD8",                   "111110101000nnnn1111dddd0101mmmm"),
         INST(&V::thumb32_UQSUB8,         "UQSUB8",                   "111110101100nnnn1111dddd0101mmmm"),
-        //INST(&V::thumb32_UHADD16,        "UHADD16",                  "111110101001----1111----0110----"),
+        INST(&V::thumb32_UHADD16,        "UHADD16",                  "111110101001nnnn1111dddd0110mmmm"),
         //INST(&V::thumb32_UHASX,          "UHASX",                    "111110101010----1111----0110----"),
         //INST(&V::thumb32_UHSAX,          "UHSAX",                    "111110101110----1111----0110----"),
         //INST(&V::thumb32_UHSUB16,        "UHSUB16",                  "111110101101----1111----0110----"),
diff --git a/src/frontend/A32/translate/impl/thumb32_parallel.cpp b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
index b3b5cd3b..c00b8c5a 100644
--- a/src/frontend/A32/translate/impl/thumb32_parallel.cpp
+++ b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
@@ -362,4 +362,30 @@ bool ThumbTranslatorVisitor::thumb32_UQSUB16(Reg n, Reg d, Reg m) {
     return true;
 }
 
+bool ThumbTranslatorVisitor::thumb32_SHADD16(Reg n, Reg d, Reg m) {
+    if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+
+    const auto reg_m = ir.GetRegister(m);
+    const auto reg_n = ir.GetRegister(n);
+    const auto result = ir.PackedHalvingAddS16(reg_n, reg_m);
+
+    ir.SetRegister(d, result);
+    return true;
+}
+
+bool ThumbTranslatorVisitor::thumb32_UHADD16(Reg n, Reg d, Reg m) {
+    if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+
+    const auto reg_m = ir.GetRegister(m);
+    const auto reg_n = ir.GetRegister(n);
+    const auto result = ir.PackedHalvingAddU16(reg_n, reg_m);
+
+    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 a88f406b..25413df2 100644
--- a/src/frontend/A32/translate/impl/translate_thumb.h
+++ b/src/frontend/A32/translate/impl/translate_thumb.h
@@ -154,6 +154,9 @@ struct ThumbTranslatorVisitor final {
     bool thumb32_UQSAX(Reg n, Reg d, Reg m);
     bool thumb32_UQSUB8(Reg n, Reg d, Reg m);
     bool thumb32_UQSUB16(Reg n, Reg d, Reg m);
+
+    bool thumb32_SHADD16(Reg n, Reg d, Reg m);
+    bool thumb32_UHADD16(Reg n, Reg d, Reg m);
 };
 
 } // namespace Dynarmic::A32
diff --git a/tests/A32/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp
index 47064207..b02f0c4b 100644
--- a/tests/A32/fuzz_thumb.cpp
+++ b/tests/A32/fuzz_thumb.cpp
@@ -432,6 +432,8 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
                      three_reg_not_r15),
         ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL
                      three_reg_not_r15),
+        ThumbInstGen("111110101001nnnn1111dddd0010mmmm", // SHADD16
+                     three_reg_not_r15),
         ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX
                      three_reg_not_r15),
         ThumbInstGen("111110101100nnnn1111dddd0000mmmm", // SSUB8
@@ -444,6 +446,8 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
                      three_reg_not_r15),
         ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX
                      three_reg_not_r15),
+        ThumbInstGen("111110101001nnnn1111dddd0110mmmm", // UHADD16
+                     three_reg_not_r15),
         ThumbInstGen("111110101000nnnn1111dddd0101mmmm", // UQADD8
                      three_reg_not_r15),
         ThumbInstGen("111110101001nnnn1111dddd0101mmmm", // UQADD16