/* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * This software may be used and distributed according to the terms of the GNU * General Public License version 2 or any later version. */ #include "translate_arm.h" namespace Dynarmic { namespace Arm { // Multiply (Normal) instructions bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a)); ir.SetRegister(d, result); if (S) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } } return true; } bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m)); ir.SetRegister(d, result); if (S) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } } return true; } // Multiply (Long) instructions bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto product = ir.Mul64(n64, m64); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto result = ir.Add64(product, addend); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetZFlag(ir.IsZero64(result)); } } return true; } bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto result = ir.Mul64(n64, m64); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetZFlag(ir.IsZero64(result)); } } return true; } bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo)); auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi)); auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } return true; } bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); auto result = ir.Add64(ir.Mul64(n64, m64), addend); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetZFlag(ir.IsZero64(result)); } } return true; } bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); auto result = ir.Mul64(n64, m64); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetZFlag(ir.IsZero64(result)); } } return true; } // Multiply (Halfword) instructions bool ArmTranslatorVisitor::arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto product = ir.SignExtendWordToLong(ir.Mul(n16, m16)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto result = ir.Add64(product, addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } return true; } bool ArmTranslatorVisitor::arm_SMLAxy(Cond cond, Reg d, Reg a, Reg m, bool M, bool N, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto product = ir.Mul(n16, m16); auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); } return true; } bool ArmTranslatorVisitor::arm_SMULxy(Cond cond, Reg d, Reg m, bool M, bool N, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto result = ir.Mul(n16, m16); ir.SetRegister(d, result); } return true; } // Multiply (word by halfword) instructions bool ArmTranslatorVisitor::arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m32 = ir.GetRegister(m); if (M) m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; auto m16 = ir.LeastSignificantHalf(m32); m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(m16)); auto product = ir.LeastSignificantWord(ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16))); auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); } return true; } bool ArmTranslatorVisitor::arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m32 = ir.GetRegister(m); if (M) m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; auto m16 = ir.LeastSignificantHalf(m32); m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(m16)); auto result = ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16)); ir.SetRegister(d, ir.LeastSignificantWord(result)); } return true; } // Multiply (Most significant word) instructions bool ArmTranslatorVisitor::arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC /* no check for a */) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); auto temp = ir.Add64(a64, ir.Mul64(n64, m64)); auto result_carry = ir.MostSignificantWord(temp); auto result = result_carry.result; if (R) result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; ir.SetRegister(d, result); } return true; } bool ArmTranslatorVisitor::arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); auto temp = ir.Sub64(a64, ir.Mul64(n64, m64)); auto result_carry = ir.MostSignificantWord(temp); auto result = result_carry.result; if (R) result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; ir.SetRegister(d, result); } return true; } bool ArmTranslatorVisitor::arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto product = ir.Mul64(n64, m64); auto result_carry = ir.MostSignificantWord(product); auto result = result_carry.result; if (R) result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; ir.SetRegister(d, result); } return true; } // Multiply (Dual) instructions bool ArmTranslatorVisitor::arm_SMLAD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { if (a == Reg::PC) return arm_SMUAD(cond, d, m, M, n); if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.Mul(n_lo, m_lo); auto product_hi = ir.Mul(n_hi, m_hi); auto addend = ir.GetRegister(a); auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0)); ir.OrQFlag(result_overflow.overflow); result_overflow = ir.AddWithCarry(result_overflow.result, addend, ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); } return true; } bool ArmTranslatorVisitor::arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto result = ir.Add64(ir.Add64(product_lo, product_hi), addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } return true; } bool ArmTranslatorVisitor::arm_SMLSD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { if (a == Reg::PC) return arm_SMUSD(cond, d, m, M, n); if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.Mul(n_lo, m_lo); auto product_hi = ir.Mul(n_hi, m_hi); auto addend = ir.GetRegister(a); auto result_overflow = ir.AddWithCarry(ir.Sub(product_lo, product_hi), addend, ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); } return true; } bool ArmTranslatorVisitor::arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (dLo == dHi) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto result = ir.Add64(ir.Sub64(product_lo, product_hi), addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } return true; } bool ArmTranslatorVisitor::arm_SMUAD(Cond cond, Reg d, Reg m, bool M, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.Mul(n_lo, m_lo); auto product_hi = ir.Mul(n_hi, m_hi); auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); } return true; } bool ArmTranslatorVisitor::arm_SMUSD(Cond cond, Reg d, Reg m, bool M, Reg n) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) return UnpredictableInstruction(); if (ConditionPassed(cond)) { auto n32 = ir.GetRegister(n); auto m32 = ir.GetRegister(m); auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; if (M) std::swap(m_lo, m_hi); auto product_lo = ir.Mul(n_lo, m_lo); auto product_hi = ir.Mul(n_hi, m_hi); auto result = ir.Sub(product_lo, product_hi); ir.SetRegister(d, result); } return true; } } // namespace Arm } // namespace Dynarmic