From 097888dd845b7292bb107af80d87bc926001a9a1 Mon Sep 17 00:00:00 2001
From: iximeow <me@iximeow.net>
Date: Thu, 21 Oct 2021 17:47:46 -0700
Subject: data processing (three source)

---
 src/armv8/a64.rs  | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 test/armv8/a64.rs | 23 ++++++++++++++
 2 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/src/armv8/a64.rs b/src/armv8/a64.rs
index 60bbbed..3609103 100644
--- a/src/armv8/a64.rs
+++ b/src/armv8/a64.rs
@@ -134,7 +134,7 @@ mod docs {
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
 pub enum DecodeError {
     ExhaustedInput,
     InvalidOpcode,
@@ -665,6 +665,30 @@ impl Display for Instruction {
             Opcode::CLS => {
                 write!(fmt, "cls")?;
             }
+            Opcode::MADD => {
+                write!(fmt, "madd")?;
+            }
+            Opcode::MSUB => {
+                write!(fmt, "msub")?;
+            }
+            Opcode::SMADDL => {
+                write!(fmt, "smaddl")?;
+            }
+            Opcode::SMSUBL => {
+                write!(fmt, "smsubl")?;
+            }
+            Opcode::SMULH => {
+                write!(fmt, "smulh")?;
+            }
+            Opcode::UMADDL => {
+                write!(fmt, "umaddl")?;
+            }
+            Opcode::UMSUBL => {
+                write!(fmt, "umsubl")?;
+            }
+            Opcode::UMULH => {
+                write!(fmt, "umulh")?;
+            }
         };
 
         if self.operands[0] != Operand::Nothing {
@@ -840,6 +864,14 @@ pub enum Opcode {
     REV32,
     CLZ,
     CLS,
+    MADD,
+    MSUB,
+    SMADDL,
+    SMSUBL,
+    SMULH,
+    UMADDL,
+    UMSUBL,
+    UMULH,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -1414,7 +1446,63 @@ impl Decoder<ARMv8> for InstDecoder {
                         },
                         _ => {
                             // Data processing (3 source)
-                            return Err(DecodeError::IncompleteDecoder);
+                            let op0 = ((word >> 15) & 0x01) as u8;
+                            let op31 = ((word >> 21) & 0x07) as u8;
+                            let op54 = ((word >> 29) & 0x03) as u8;
+                            let op = op0 | (op31 << 1) | (op54 << 4);
+                            let sf = ((word >> 31) & 0x01) as u8;
+
+                            let Rd = ((word >> 0) & 0x1f) as u16;
+                            let Rn = ((word >> 5) & 0x1f) as u16;
+                            let Ra = ((word >> 10) & 0x1f) as u16;
+                            let Rm = ((word >> 16) & 0x1f) as u16;
+
+                            const DATA_PROCESSING_3_SOURCE: &[Result<(Opcode, SizeCode, SizeCode), DecodeError>] = &[
+                                Ok((Opcode::MADD, SizeCode::W, SizeCode::W)),
+                                Ok((Opcode::MADD, SizeCode::X, SizeCode::X)),
+                                Ok((Opcode::MSUB, SizeCode::W, SizeCode::W)),
+                                Ok((Opcode::MSUB, SizeCode::X, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::SMADDL, SizeCode::W, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::SMSUBL, SizeCode::W, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::SMULH, SizeCode::W, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::UMADDL, SizeCode::W, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::UMSUBL, SizeCode::W, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                                Ok((Opcode::UMULH, SizeCode::X, SizeCode::X)),
+                                Err(DecodeError::InvalidOpcode),
+                            ];
+
+                            let compound_idx = (op << 1) | sf;
+                            crate::armv8::a64::std::eprintln!("word: {:#08x}, compound_idx: {:x}", word, compound_idx);
+                            let (opcode, source_size, dest_size) = DATA_PROCESSING_3_SOURCE.get(compound_idx as usize)
+                                .map(std::borrow::ToOwned::to_owned)
+                                .unwrap_or(Err(DecodeError::InvalidOpcode))?;
+
+                            inst.opcode = opcode;
+                            inst.operands = [
+                                Operand::Register(dest_size, Rd),
+                                Operand::Register(source_size, Rn),
+                                Operand::Register(source_size, Rm),
+                                Operand::Register(dest_size, Ra), // in practice these match up with the corresponding operand.
+                            ];
+                            if opcode == Opcode::UMULH {
+                                inst.operands[3] = Operand::Nothing;
+                            }
                         }
                     }
                 } else {
diff --git a/test/armv8/a64.rs b/test/armv8/a64.rs
index feb878a..c251d4a 100644
--- a/test/armv8/a64.rs
+++ b/test/armv8/a64.rs
@@ -314,6 +314,29 @@ fn test_decode_data_processing_one_source() {
 }
 
 #[test]
+fn test_decode_data_processing_three_source() {
+    const TESTS: &[([u8; 4], &'static str)] = &[
+        ([0x11, 0x01, 0x0f, 0x1b], "madd w17, w8, w15, w0"),
+        ([0x11, 0x81, 0x0f, 0x1b], "msub w17, w8, w15, w0"),
+        ([0x11, 0x81, 0x0f, 0x9b], "msub x17, x8, x15, x0"),
+        ([0x11, 0x89, 0x0f, 0x9b], "msub x17, x8, x15, x2"),
+
+        ([0x11, 0x09, 0x2f, 0x9b], "smaddl x17, w8, w15, x2"),
+        ([0x11, 0x89, 0x2f, 0x9b], "smsubl x17, w8, w15, x2"),
+        ([0x11, 0x09, 0x4f, 0x9b], "smulh x17, w8, w15, x2"),
+        ([0x11, 0x09, 0xaf, 0x9b], "umaddl x17, w8, w15, x2"),
+        ([0x11, 0x89, 0xaf, 0x9b], "umsubl x17, w8, w15, x2"),
+        ([0x11, 0x09, 0xcf, 0x9b], "umulh x17, x8, x15"),
+    ];
+
+    for (bytes, instr) in TESTS {
+        test_display(*bytes, instr);
+    }
+
+    test_err([0x11, 0x81, 0x0f, 0x3b], DecodeError::InvalidOpcode);
+}
+
+#[test]
 fn test_decode_chrome_entrypoint() {
     // 1400 instructions from the entrypoint of a chrome binary, sorted by
     // instruction word for no good reason.
-- 
cgit v1.1