diff --git a/src/frontend/A32/decoder/arm.inc b/src/frontend/A32/decoder/arm.inc index 54b761bb..8aa57043 100644 --- a/src/frontend/A32/decoder/arm.inc +++ b/src/frontend/A32/decoder/arm.inc @@ -199,6 +199,7 @@ INST(arm_UDIV, "UDIV", "cccc01110011dddd1111mmmm0001nnnn // Multiply (Normal) instructions INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") // v2 +INST(arm_MLS, "MLS", "cccc00000110ddddaaaammmm1001nnnn") // v6T2 INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") // v2 // Multiply (Long) instructions diff --git a/src/frontend/A32/disassembler/disassembler_arm.cpp b/src/frontend/A32/disassembler/disassembler_arm.cpp index 145c6381..598c9ae0 100644 --- a/src/frontend/A32/disassembler/disassembler_arm.cpp +++ b/src/frontend/A32/disassembler/disassembler_arm.cpp @@ -664,6 +664,9 @@ public: std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { return fmt::format("mla{}{} {}, {}, {}, {}", S ? "s" : "", CondToString(cond), d, n, m, a); } + std::string arm_MLS(Cond cond, Reg d, Reg a, Reg m, Reg n) { + return fmt::format("mls{} {}, {}, {}, {}", CondToString(cond), d, n, m, a); + } std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { return fmt::format("mul{}{} {}, {}, {}", S ? "s" : "", CondToString(cond), d, n, m); } diff --git a/src/frontend/A32/translate/translate_arm/multiply.cpp b/src/frontend/A32/translate/translate_arm/multiply.cpp index e6ac9215..334fe2a6 100644 --- a/src/frontend/A32/translate/translate_arm/multiply.cpp +++ b/src/frontend/A32/translate/translate_arm/multiply.cpp @@ -28,6 +28,25 @@ bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n return true; } +// MLS , , , +bool ArmTranslatorVisitor::arm_MLS(Cond cond, Reg d, Reg a, Reg m, Reg n) { + if (d == Reg::PC || a == Reg::PC || m == Reg::PC || n == Reg::PC) { + return UnpredictableInstruction(); + } + + if (!ConditionPassed(cond)) { + return true; + } + + const IR::U32 operand1 = ir.GetRegister(n); + const IR::U32 operand2 = ir.GetRegister(m); + const IR::U32 operand3 = ir.GetRegister(a); + const IR::U32 result = ir.Sub(operand3, ir.Mul(operand1, operand2)); + + ir.SetRegister(d, result); + return true; +} + // MUL{S} , , bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { diff --git a/src/frontend/A32/translate/translate_arm/translate_arm.h b/src/frontend/A32/translate/translate_arm/translate_arm.h index 6165109b..dd8f3cb2 100644 --- a/src/frontend/A32/translate/translate_arm/translate_arm.h +++ b/src/frontend/A32/translate/translate_arm/translate_arm.h @@ -242,6 +242,7 @@ struct ArmTranslatorVisitor final { // Multiply (Normal) instructions bool arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n); + bool arm_MLS(Cond cond, Reg d, Reg a, Reg m, Reg n); bool arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n); // Multiply (Long) instructions diff --git a/tests/A32/fuzz_arm.cpp b/tests/A32/fuzz_arm.cpp index c2044765..a0ae4cfa 100644 --- a/tests/A32/fuzz_arm.cpp +++ b/tests/A32/fuzz_arm.cpp @@ -848,6 +848,7 @@ TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") { const std::array instructions = { InstructionGenerator("cccc0000001Sddddaaaammmm1001nnnn", validate_d_a_m_n), // MLA + InstructionGenerator("cccc00000110ddddaaaammmm1001nnnn", validate_d_a_m_n), // MLS InstructionGenerator("cccc0000000Sdddd0000mmmm1001nnnn", validate_d_m_n), // MUL InstructionGenerator("cccc0000111Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMLAL