From 0e28c634567919ad682165564f82dbe942719d49 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Mon, 1 Feb 2021 16:36:18 -0500
Subject: [PATCH] thumb32: Implement SSAX/USAX

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

diff --git a/src/frontend/A32/decoder/thumb32.h b/src/frontend/A32/decoder/thumb32.h
index 2e819088..aeac8d18 100644
--- a/src/frontend/A32/decoder/thumb32.h
+++ b/src/frontend/A32/decoder/thumb32.h
@@ -237,7 +237,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         // Parallel Addition and Subtraction (signed)
         INST(&V::thumb32_SADD16,         "SADD16",                   "111110101001nnnn1111dddd0000mmmm"),
         INST(&V::thumb32_SASX,           "SASX",                     "111110101010nnnn1111dddd0000mmmm"),
-        //INST(&V::thumb32_SSAX,           "SSAX",                     "111110101110----1111----0000----"),
+        INST(&V::thumb32_SSAX,           "SSAX",                     "111110101110nnnn1111dddd0000mmmm"),
         //INST(&V::thumb32_SSUB16,         "SSUB16",                   "111110101101----1111----0000----"),
         //INST(&V::thumb32_SADD8,          "SADD8",                    "111110101000----1111----0000----"),
         //INST(&V::thumb32_SSUB8,          "SSUB8",                    "111110101100----1111----0000----"),
@@ -257,7 +257,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
         // Parallel Addition and Subtraction (unsigned)
         INST(&V::thumb32_UADD16,         "UADD16",                   "111110101001nnnn1111dddd0100mmmm"),
         INST(&V::thumb32_UASX,           "UASX",                     "111110101010nnnn1111dddd0100mmmm"),
-        //INST(&V::thumb32_USAX,           "USAX",                     "111110101110----1111----0100----"),
+        INST(&V::thumb32_USAX,           "USAX",                     "111110101110nnnn1111dddd0100mmmm"),
         //INST(&V::thumb32_USUB16,         "USUB16",                   "111110101101----1111----0100----"),
         //INST(&V::thumb32_UADD8,          "UADD8",                    "111110101000----1111----0100----"),
         //INST(&V::thumb32_USUB8,          "USUB8",                    "111110101100----1111----0100----"),
diff --git a/src/frontend/A32/translate/impl/thumb32_parallel.cpp b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
index 48f9759e..32fac7dc 100644
--- a/src/frontend/A32/translate/impl/thumb32_parallel.cpp
+++ b/src/frontend/A32/translate/impl/thumb32_parallel.cpp
@@ -35,6 +35,20 @@ bool ThumbTranslatorVisitor::thumb32_SASX(Reg n, Reg d, Reg m) {
     return true;
 }
 
+bool ThumbTranslatorVisitor::thumb32_SSAX(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.PackedSubAddS16(reg_n, reg_m);
+
+    ir.SetRegister(d, result.result);
+    ir.SetGEFlags(result.ge);
+    return true;
+}
+
 bool ThumbTranslatorVisitor::thumb32_UADD16(Reg n, Reg d, Reg m) {
     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
         return UnpredictableInstruction();
@@ -63,4 +77,18 @@ bool ThumbTranslatorVisitor::thumb32_UASX(Reg n, Reg d, Reg m) {
     return true;
 }
 
+bool ThumbTranslatorVisitor::thumb32_USAX(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.PackedSubAddU16(reg_n, reg_m);
+
+    ir.SetRegister(d, result.result);
+    ir.SetGEFlags(result.ge);
+    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 65df3225..54662723 100644
--- a/src/frontend/A32/translate/impl/translate_thumb.h
+++ b/src/frontend/A32/translate/impl/translate_thumb.h
@@ -131,8 +131,10 @@ struct ThumbTranslatorVisitor final {
     // thumb32 parallel add/sub instructions
     bool thumb32_SADD16(Reg n, Reg d, Reg m);
     bool thumb32_SASX(Reg n, Reg d, Reg m);
+    bool thumb32_SSAX(Reg n, Reg d, Reg m);
     bool thumb32_UADD16(Reg n, Reg d, Reg m);
     bool thumb32_UASX(Reg n, Reg d, Reg m);
+    bool thumb32_USAX(Reg n, Reg d, Reg m);
 };
 
 } // namespace Dynarmic::A32
diff --git a/tests/A32/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp
index 164c381e..d239f4b2 100644
--- a/tests/A32/fuzz_thumb.cpp
+++ b/tests/A32/fuzz_thumb.cpp
@@ -418,10 +418,14 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
                      three_reg_not_r15),
         ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL
                      three_reg_not_r15),
+        ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX
+                     three_reg_not_r15),
         ThumbInstGen("111110101001nnnn1111dddd0100mmmm", // UADD16
                      three_reg_not_r15),
         ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX
                      three_reg_not_r15),
+        ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX
+                     three_reg_not_r15),
     };
 
     const auto instruction_select = [&]() -> u32 {