using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Instructions { static partial class InstEmit32 { [Flags] private enum MullFlags { Subtract = 1, Add = 1 << 1, Signed = 1 << 2, SignedAdd = Signed | Add, SignedSubtract = Signed | Subtract } public static void Mla(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetAluN(context); Operand m = GetAluM(context); Operand a = GetIntA32(context, op.Ra); Operand res = context.Add(a, context.Multiply(n, m)); if (ShouldSetFlags(context)) { EmitNZFlagsCheck(context, res); } EmitAluStore(context, res); } public static void Mls(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetAluN(context); Operand m = GetAluM(context); Operand a = GetIntA32(context, op.Ra); Operand res = context.Subtract(a, context.Multiply(n, m)); EmitAluStore(context, res); } public static void Smmla(ArmEmitterContext context) { EmitSmmul(context, MullFlags.SignedAdd); } public static void Smmls(ArmEmitterContext context) { EmitSmmul(context, MullFlags.SignedSubtract); } public static void Smmul(ArmEmitterContext context) { EmitSmmul(context, MullFlags.Signed); } private static void EmitSmmul(ArmEmitterContext context, MullFlags flags) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn)); Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm)); Operand res = context.Multiply(n, m); if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf) { res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res); } else if (flags.HasFlag(MullFlags.Subtract)) { res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res); } if (op.R) { res = context.Add(res, Const(0x80000000L)); } Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32))); EmitGenericAluStoreA32(context, op.Rd, false, hi); } public static void Smla__(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); Operand a = GetIntA32(context, op.Ra); if (op.NHigh) { n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16))); } else { n = context.SignExtend16(OperandType.I64, n); } if (op.MHigh) { m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16))); } else { m = context.SignExtend16(OperandType.I64, m); } Operand res = context.Multiply(n, m); Operand toAdd = context.SignExtend32(OperandType.I64, a); res = context.Add(res, toAdd); Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res)); res = context.ConvertI64ToI32(res); UpdateQFlag(context, q); EmitGenericAluStoreA32(context, op.Rd, false, res); } public static void Smlal(ArmEmitterContext context) { EmitMlal(context, true); } public static void Smlal__(ArmEmitterContext context) { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); if (op.NHigh) { n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16))); } else { n = context.SignExtend16(OperandType.I64, n); } if (op.MHigh) { m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16))); } else { m = context.SignExtend16(OperandType.I64, m); } Operand res = context.Multiply(n, m); Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32)); toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo))); res = context.Add(res, toAdd); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); EmitGenericAluStoreA32(context, op.RdHi, false, hi); EmitGenericAluStoreA32(context, op.RdLo, false, lo); } public static void Smlaw_(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); Operand a = GetIntA32(context, op.Ra); if (op.MHigh) { m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16))); } else { m = context.SignExtend16(OperandType.I64, m); } Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m); Operand toAdd = context.ShiftLeft(context.SignExtend32(OperandType.I64, a), Const(16)); res = context.Add(res, toAdd); res = context.ShiftRightSI(res, Const(16)); Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res)); res = context.ConvertI64ToI32(res); UpdateQFlag(context, q); EmitGenericAluStoreA32(context, op.Rd, false, res); } public static void Smul__(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); if (op.NHigh) { n = context.ShiftRightSI(n, Const(16)); } else { n = context.SignExtend16(OperandType.I32, n); } if (op.MHigh) { m = context.ShiftRightSI(m, Const(16)); } else { m = context.SignExtend16(OperandType.I32, m); } Operand res = context.Multiply(n, m); EmitGenericAluStoreA32(context, op.Rd, false, res); } public static void Smull(ArmEmitterContext context) { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn)); Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm)); Operand res = context.Multiply(n, m); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); if (ShouldSetFlags(context)) { EmitNZFlagsCheck(context, res); } EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo); } public static void Smulw_(ArmEmitterContext context) { IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); if (op.MHigh) { m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16))); } else { m = context.SignExtend16(OperandType.I64, m); } Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m); res = context.ShiftRightUI(res, Const(16)); res = context.ConvertI64ToI32(res); EmitGenericAluStoreA32(context, op.Rd, false, res); } public static void Umaal(ArmEmitterContext context) { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn)); Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm)); Operand dHi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)); Operand dLo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)); Operand res = context.Multiply(n, m); res = context.Add(res, dHi); res = context.Add(res, dLo); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); EmitGenericAluStoreA32(context, op.RdHi, false, hi); EmitGenericAluStoreA32(context, op.RdLo, false, lo); } public static void Umlal(ArmEmitterContext context) { EmitMlal(context, false); } public static void Umull(ArmEmitterContext context) { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn)); Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm)); Operand res = context.Multiply(n, m); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); if (ShouldSetFlags(context)) { EmitNZFlagsCheck(context, res); } EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo); } private static void EmitMlal(ArmEmitterContext context, bool signed) { IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp; Operand n = GetIntA32(context, op.Rn); Operand m = GetIntA32(context, op.Rm); if (signed) { n = context.SignExtend32(OperandType.I64, n); m = context.SignExtend32(OperandType.I64, m); } else { n = context.ZeroExtend32(OperandType.I64, n); m = context.ZeroExtend32(OperandType.I64, m); } Operand res = context.Multiply(n, m); Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32)); toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo))); res = context.Add(res, toAdd); Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32))); Operand lo = context.ConvertI64ToI32(res); if (ShouldSetFlags(context)) { EmitNZFlagsCheck(context, res); } EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi); EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo); } private static void UpdateQFlag(ArmEmitterContext context, Operand q) { Operand lblSkipSetQ = Label(); context.BranchIfFalse(lblSkipSetQ, q); SetFlag(context, PState.QFlag, Const(1)); context.MarkLabel(lblSkipSetQ); } } }