From 4a1db13072764f464db5217c6481b87074963d87 Mon Sep 17 00:00:00 2001
From: aroulin <andy.roulin@epfl.ch>
Date: Mon, 10 Aug 2015 14:45:22 +0200
Subject: [PATCH] arm_disasm: ARMv6 parallel add/sub media instructions {S, U,
 Q, UQ, SH, UH}{ADD16, ASX, SAX, SUB16, ADD8, SUB8}

---
 src/core/arm/disassembler/arm_disasm.cpp | 129 +++++++++++++++++++++++
 src/core/arm/disassembler/arm_disasm.h   |  38 +++++++
 2 files changed, 167 insertions(+)

diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp
index a03f113bc..59a714c44 100644
--- a/src/core/arm/disassembler/arm_disasm.cpp
+++ b/src/core/arm/disassembler/arm_disasm.cpp
@@ -69,18 +69,36 @@ static const char *opcode_names[] = {
     "orr",
     "pkh",
     "pld",
+    "qadd16",
+    "qadd8",
+    "qasx",
+    "qsax",
+    "qsub16",
+    "qsub8",
     "rev",
     "rev16",
     "revsh",
     "rsb",
     "rsc",
+    "sadd16",
+    "sadd8",
+    "sasx",
     "sbc",
     "sel",
     "sev",
+    "shadd16",
+    "shadd8",
+    "shasx",
+    "shsax",
+    "shsub16",
+    "shsub8",
     "smlal",
     "smull",
     "ssat",
     "ssat16",
+    "ssax",
+    "ssub16",
+    "ssub8",
     "stc",
     "stm",
     "str",
@@ -104,10 +122,28 @@ static const char *opcode_names[] = {
     "sxth",
     "teq",
     "tst",
+    "uadd16",
+    "uadd8",
+    "uasx",
+    "uhadd16",
+    "uhadd8",
+    "uhasx",
+    "uhsax",
+    "uhsub16",
+    "uhsub8",
     "umlal",
     "umull",
+    "uqadd16",
+    "uqadd8",
+    "uqasx",
+    "uqsax",
+    "uqsub16",
+    "uqsub8",
     "usat",
     "usat16",
+    "usax",
+    "usub16",
+    "usub8",
     "uxtab",
     "uxtab16",
     "uxtah",
@@ -262,6 +298,43 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
             return DisassemblePKH(insn);
         case OP_PLD:
             return DisassemblePLD(insn);
+        case OP_QADD16:
+        case OP_QADD8:
+        case OP_QASX:
+        case OP_QSAX:
+        case OP_QSUB16:
+        case OP_QSUB8:
+        case OP_SADD16:
+        case OP_SADD8:
+        case OP_SASX:
+        case OP_SHADD16:
+        case OP_SHADD8:
+        case OP_SHASX:
+        case OP_SHSAX:
+        case OP_SHSUB16:
+        case OP_SHSUB8:
+        case OP_SSAX:
+        case OP_SSUB16:
+        case OP_SSUB8:
+        case OP_UADD16:
+        case OP_UADD8:
+        case OP_UASX:
+        case OP_UHADD16:
+        case OP_UHADD8:
+        case OP_UHASX:
+        case OP_UHSAX:
+        case OP_UHSUB16:
+        case OP_UHSUB8:
+        case OP_UQADD16:
+        case OP_UQADD8:
+        case OP_UQASX:
+        case OP_UQSAX:
+        case OP_UQSUB16:
+        case OP_UQSUB8:
+        case OP_USAX:
+        case OP_USUB16:
+        case OP_USUB8:
+            return DisassembleParallelAddSub(opcode, insn);
         case OP_REV:
         case OP_REV16:
         case OP_REVSH:
@@ -732,6 +805,16 @@ std::string ARM_Disasm::DisassembleNoOperands(Opcode opcode, uint32_t insn)
     return Common::StringFromFormat("%s%s", opcode_names[opcode], cond_to_str(cond));
 }
 
+std::string ARM_Disasm::DisassembleParallelAddSub(Opcode opcode, uint32_t insn) {
+    uint32_t cond = BITS(insn, 28, 31);
+    uint32_t rn = BITS(insn, 16, 19);
+    uint32_t rd = BITS(insn, 12, 15);
+    uint32_t rm = BITS(insn, 0, 3);
+
+    return Common::StringFromFormat("%s%s\tr%u, r%u, r%u", opcode_names[opcode], cond_to_str(cond),
+                                    rd, rn, rm);
+}
+
 std::string ARM_Disasm::DisassemblePKH(uint32_t insn)
 {
     uint32_t cond = BITS(insn, 28, 31);
@@ -1083,6 +1166,49 @@ Opcode ARM_Disasm::DecodeSyncPrimitive(uint32_t insn) {
     }
 }
 
+Opcode ARM_Disasm::DecodeParallelAddSub(uint32_t insn) {
+    uint32_t op1 = BITS(insn, 20, 21);
+    uint32_t op2 = BITS(insn, 5, 7);
+    uint32_t is_unsigned = BIT(insn, 22);
+
+    if (op1 == 0x0 || op2 == 0x5 || op2 == 0x6)
+        return OP_UNDEFINED;
+
+    // change op1 range from [1, 3] to range [0, 2]
+    op1--;
+
+    // change op2 range from [0, 4] U {7} to range [0, 5]
+    if (op2 == 0x7)
+        op2 = 0x5;
+
+    static std::vector<Opcode> opcodes = {
+        // op1 = 0
+        OP_SADD16, OP_UADD16,
+        OP_SASX, OP_UASX,
+        OP_SSAX, OP_USAX,
+        OP_SSUB16, OP_USUB16,
+        OP_SADD8, OP_UADD8,
+        OP_SSUB8, OP_USUB8,
+        // op1 = 1
+        OP_QADD16, OP_UQADD16,
+        OP_QASX, OP_UQASX,
+        OP_QSAX, OP_UQSAX,
+        OP_QSUB16, OP_UQSUB16,
+        OP_QADD8, OP_UQADD8,
+        OP_QSUB8, OP_UQSUB8,
+        // op1 = 2
+        OP_SHADD16, OP_UHADD16,
+        OP_SHASX, OP_UHASX,
+        OP_SHSAX, OP_UHSAX,
+        OP_SHSUB16, OP_UHSUB16,
+        OP_SHADD8, OP_UHADD8,
+        OP_SHSUB8, OP_UHSUB8
+    };
+
+    uint32_t opcode_index = op1 * 12 + op2 * 2 + is_unsigned;
+    return opcodes[opcode_index];
+}
+
 Opcode ARM_Disasm::DecodePackingSaturationReversal(uint32_t insn) {
     uint32_t op1 = BITS(insn, 20, 22);
     uint32_t a = BITS(insn, 16, 19);
@@ -1220,6 +1346,9 @@ Opcode ARM_Disasm::DecodeMedia(uint32_t insn) {
     uint32_t rn = BITS(insn, 0, 3);
 
     switch (BITS(op1, 3, 4)) {
+        case 0x0:
+            // unsigned and signed parallel addition and subtraction
+            return DecodeParallelAddSub(insn);
         case 0x1:
             // Packing, unpacking, saturation, and reversal
             return DecodePackingSaturationReversal(insn);
diff --git a/src/core/arm/disassembler/arm_disasm.h b/src/core/arm/disassembler/arm_disasm.h
index a6b34daeb..c1bd1b948 100644
--- a/src/core/arm/disassembler/arm_disasm.h
+++ b/src/core/arm/disassembler/arm_disasm.h
@@ -50,18 +50,36 @@ enum Opcode {
     OP_ORR,
     OP_PKH,
     OP_PLD,
+    OP_QADD16,
+    OP_QADD8,
+    OP_QASX,
+    OP_QSAX,
+    OP_QSUB16,
+    OP_QSUB8,
     OP_REV,
     OP_REV16,
     OP_REVSH,
     OP_RSB,
     OP_RSC,
+    OP_SADD16,
+    OP_SADD8,
+    OP_SASX,
     OP_SBC,
     OP_SEL,
     OP_SEV,
+    OP_SHADD16,
+    OP_SHADD8,
+    OP_SHASX,
+    OP_SHSAX,
+    OP_SHSUB16,
+    OP_SHSUB8,
     OP_SMLAL,
     OP_SMULL,
     OP_SSAT,
     OP_SSAT16,
+    OP_SSAX,
+    OP_SSUB16,
+    OP_SSUB8,
     OP_STC,
     OP_STM,
     OP_STR,
@@ -85,10 +103,28 @@ enum Opcode {
     OP_SXTH,
     OP_TEQ,
     OP_TST,
+    OP_UADD16,
+    OP_UADD8,
+    OP_UASX,
+    OP_UHADD16,
+    OP_UHADD8,
+    OP_UHASX,
+    OP_UHSAX,
+    OP_UHSUB16,
+    OP_UHSUB8,
     OP_UMLAL,
     OP_UMULL,
+    OP_UQADD16,
+    OP_UQADD8,
+    OP_UQASX,
+    OP_UQSAX,
+    OP_UQSUB16,
+    OP_UQSUB8,
     OP_USAT,
     OP_USAT16,
+    OP_USAX,
+    OP_USUB16,
+    OP_USUB8,
     OP_UXTAB,
     OP_UXTAB16,
     OP_UXTAH,
@@ -153,6 +189,7 @@ class ARM_Disasm {
   static Opcode Decode10(uint32_t insn);
   static Opcode Decode11(uint32_t insn);
   static Opcode DecodeSyncPrimitive(uint32_t insn);
+  static Opcode DecodeParallelAddSub(uint32_t insn);
   static Opcode DecodePackingSaturationReversal(uint32_t insn);
   static Opcode DecodeMUL(uint32_t insn);
   static Opcode DecodeMSRImmAndHints(uint32_t insn);
@@ -175,6 +212,7 @@ class ARM_Disasm {
   static std::string DisassembleMRS(uint32_t insn);
   static std::string DisassembleMSR(uint32_t insn);
   static std::string DisassembleNoOperands(Opcode opcode, uint32_t insn);
+  static std::string DisassembleParallelAddSub(Opcode opcode, uint32_t insn);
   static std::string DisassemblePKH(uint32_t insn);
   static std::string DisassemblePLD(uint32_t insn);
   static std::string DisassembleREV(Opcode opcode, uint32_t insn);