Add SSAT, SSAT16, USAT and USAT16 ARM32 instructions (#954)
* Implement SMULWB, SMULWT, SMLAWB, SMLAWT, and add tests for some multiply instructions * Improve test descriptions * Rename SMULH to SMUL__ * Add SSAT, SSAT16, USAT and USAT16 ARM32 instructions * Fix new tests * Replace AND 0xFFFF with 16-bits zero extension (more efficient)
This commit is contained in:
parent
b8ee5b15ab
commit
fb0939f9b6
12 changed files with 666 additions and 195 deletions
22
ARMeilleure/Decoders/OpCode32Sat.cs
Normal file
22
ARMeilleure/Decoders/OpCode32Sat.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCode32Sat : OpCode32
|
||||||
|
{
|
||||||
|
public int Rn { get; private set; }
|
||||||
|
public int Imm5 { get; private set; }
|
||||||
|
public int Rd { get; private set; }
|
||||||
|
public int SatImm { get; private set; }
|
||||||
|
|
||||||
|
public ShiftType ShiftType { get; private set; }
|
||||||
|
|
||||||
|
public OpCode32Sat(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rn = (opCode >> 0) & 0xf;
|
||||||
|
Imm5 = (opCode >> 7) & 0x1f;
|
||||||
|
Rd = (opCode >> 12) & 0xf;
|
||||||
|
SatImm = (opCode >> 16) & 0x1f;
|
||||||
|
|
||||||
|
ShiftType = (ShiftType)((opCode >> 5) & 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
ARMeilleure/Decoders/OpCode32Sat16.cs
Normal file
16
ARMeilleure/Decoders/OpCode32Sat16.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCode32Sat16 : OpCode32
|
||||||
|
{
|
||||||
|
public int Rn { get; private set; }
|
||||||
|
public int Rd { get; private set; }
|
||||||
|
public int SatImm { get; private set; }
|
||||||
|
|
||||||
|
public OpCode32Sat16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rn = (opCode >> 0) & 0xf;
|
||||||
|
Rd = (opCode >> 12) & 0xf;
|
||||||
|
SatImm = (opCode >> 16) & 0xf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -158,7 +158,7 @@ namespace ARMeilleure.Decoders
|
||||||
SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstName.Sdiv, InstEmit.Sdiv, typeof(OpCodeAluBinary));
|
SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstName.Sdiv, InstEmit.Sdiv, typeof(OpCodeAluBinary));
|
||||||
SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", InstName.Smaddl, InstEmit.Smaddl, typeof(OpCodeMul));
|
SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", InstName.Smaddl, InstEmit.Smaddl, typeof(OpCodeMul));
|
||||||
SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", InstName.Smsubl, InstEmit.Smsubl, typeof(OpCodeMul));
|
SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", InstName.Smsubl, InstEmit.Smsubl, typeof(OpCodeMul));
|
||||||
SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", InstName.Smulh, InstEmit.Smulh, typeof(OpCodeMul));
|
SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", InstName.Smul__, InstEmit.Smulh, typeof(OpCodeMul));
|
||||||
SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", InstName.Stlr, InstEmit.Stlr, typeof(OpCodeMemEx));
|
SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", InstName.Stlr, InstEmit.Stlr, typeof(OpCodeMemEx));
|
||||||
SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", InstName.Stlxp, InstEmit.Stlxp, typeof(OpCodeMemEx));
|
SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", InstName.Stlxp, InstEmit.Stlxp, typeof(OpCodeMemEx));
|
||||||
SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", InstName.Stlxr, InstEmit.Stlxr, typeof(OpCodeMemEx));
|
SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", InstName.Stlxr, InstEmit.Stlxr, typeof(OpCodeMemEx));
|
||||||
|
@ -696,13 +696,17 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<0000110xxxxxxxxxxxxx0xx1xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg));
|
SetA32("<<<<0000110xxxxxxxxxxxxx0xx1xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg));
|
||||||
SetA32("<<<<0111101xxxxxxxxxxxxxx101xxxx", InstName.Sbfx, InstEmit32.Sbfx, typeof(OpCode32AluBf));
|
SetA32("<<<<0111101xxxxxxxxxxxxxx101xxxx", InstName.Sbfx, InstEmit32.Sbfx, typeof(OpCode32AluBf));
|
||||||
SetA32("<<<<01110001xxxx1111xxxx0001xxxx", InstName.Sdiv, InstEmit32.Sdiv, typeof(OpCode32AluMla));
|
SetA32("<<<<01110001xxxx1111xxxx0001xxxx", InstName.Sdiv, InstEmit32.Sdiv, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<00010000xxxxxxxxxxxx1xx0xxxx", InstName.Smlab, InstEmit32.Smlab, typeof(OpCode32AluMla));
|
SetA32("<<<<00010000xxxxxxxxxxxx1xx0xxxx", InstName.Smla__, InstEmit32.Smla__, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<0000111xxxxxxxxxxxxx1001xxxx", InstName.Smlal, InstEmit32.Smlal, typeof(OpCode32AluUmull));
|
SetA32("<<<<0000111xxxxxxxxxxxxx1001xxxx", InstName.Smlal, InstEmit32.Smlal, typeof(OpCode32AluUmull));
|
||||||
SetA32("<<<<00010100xxxxxxxxxxxx1xx0xxxx", InstName.Smlalh, InstEmit32.Smlalh, typeof(OpCode32AluUmull));
|
SetA32("<<<<00010100xxxxxxxxxxxx1xx0xxxx", InstName.Smlal__, InstEmit32.Smlal__, typeof(OpCode32AluUmull));
|
||||||
|
SetA32("<<<<00010010xxxxxxxxxxxx1x00xxxx", InstName.Smlaw_, InstEmit32.Smlaw_, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<01110101xxxxxxxxxxxx00x1xxxx", InstName.Smmla, InstEmit32.Smmla, typeof(OpCode32AluMla));
|
SetA32("<<<<01110101xxxxxxxxxxxx00x1xxxx", InstName.Smmla, InstEmit32.Smmla, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<01110101xxxxxxxxxxxx11x1xxxx", InstName.Smmls, InstEmit32.Smmls, typeof(OpCode32AluMla));
|
SetA32("<<<<01110101xxxxxxxxxxxx11x1xxxx", InstName.Smmls, InstEmit32.Smmls, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<00010110xxxxxxxxxxxx1xx0xxxx", InstName.Smulh, InstEmit32.Smulh, typeof(OpCode32AluMla));
|
SetA32("<<<<00010110xxxxxxxxxxxx1xx0xxxx", InstName.Smul__, InstEmit32.Smul__, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<0000110xxxxxxxxxxxxx1001xxxx", InstName.Smull, InstEmit32.Smull, typeof(OpCode32AluUmull));
|
SetA32("<<<<0000110xxxxxxxxxxxxx1001xxxx", InstName.Smull, InstEmit32.Smull, typeof(OpCode32AluUmull));
|
||||||
|
SetA32("<<<<00010010xxxx0000xxxx1x10xxxx", InstName.Smulw_, InstEmit32.Smulw_, typeof(OpCode32AluMla));
|
||||||
|
SetA32("<<<<0110101xxxxxxxxxxxxxxx01xxxx", InstName.Ssat, InstEmit32.Ssat, typeof(OpCode32Sat));
|
||||||
|
SetA32("<<<<01101010xxxxxxxx11110011xxxx", InstName.Ssat16, InstEmit32.Ssat16, typeof(OpCode32Sat16));
|
||||||
SetA32("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx));
|
SetA32("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx));
|
||||||
SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
|
SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
|
||||||
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
|
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
|
||||||
|
@ -741,6 +745,8 @@ namespace ARMeilleure.Decoders
|
||||||
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
|
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
|
||||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, typeof(OpCode32AluUmull));
|
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, typeof(OpCode32AluUmull));
|
||||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull));
|
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull));
|
||||||
|
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, typeof(OpCode32Sat));
|
||||||
|
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, typeof(OpCode32Sat16));
|
||||||
SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx));
|
SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx));
|
||||||
SetA32("<<<<01101100xxxxxxxxxx000111xxxx", InstName.Uxtb16, InstEmit32.Uxtb16, typeof(OpCode32AluUx));
|
SetA32("<<<<01101100xxxxxxxxxx000111xxxx", InstName.Uxtb16, InstEmit32.Uxtb16, typeof(OpCode32AluUx));
|
||||||
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
|
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
|
||||||
|
|
|
@ -389,6 +389,20 @@ namespace ARMeilleure.Instructions
|
||||||
EmitDiv(context, false);
|
EmitDiv(context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Ssat(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
|
|
||||||
|
EmitSat(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Ssat16(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||||
|
|
||||||
|
EmitSat16(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Sub(ArmEmitterContext context)
|
public static void Sub(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
@ -460,6 +474,20 @@ namespace ARMeilleure.Instructions
|
||||||
EmitDiv(context, true);
|
EmitDiv(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Usat(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
|
|
||||||
|
EmitSat(context, 0, op.SatImm == 32 ? (int)(~0) : (1 << op.SatImm) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Usat16(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||||
|
|
||||||
|
EmitSat16(context, 0, (1 << op.SatImm) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Uxtb(ArmEmitterContext context)
|
public static void Uxtb(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitSignExtend(context, false, 8);
|
EmitSignExtend(context, false, 8);
|
||||||
|
@ -615,9 +643,116 @@ namespace ARMeilleure.Instructions
|
||||||
context.MarkLabel(lblEnd);
|
context.MarkLabel(lblEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitSat(ArmEmitterContext context, int intMin, int intMax)
|
||||||
|
{
|
||||||
|
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetIntA32(context, op.Rn);
|
||||||
|
|
||||||
|
int shift = DecodeImmShift(op.ShiftType, op.Imm5);
|
||||||
|
|
||||||
|
switch (op.ShiftType)
|
||||||
|
{
|
||||||
|
case ShiftType.Lsl:
|
||||||
|
if (shift == 32)
|
||||||
|
{
|
||||||
|
n = Const(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
n = context.ShiftLeft(n, Const(shift));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ShiftType.Asr:
|
||||||
|
if (shift == 32)
|
||||||
|
{
|
||||||
|
n = context.ShiftRightSI(n, Const(31));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
n = context.ShiftRightSI(n, Const(shift));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand lblCheckLtIntMin = Label();
|
||||||
|
Operand lblNoSat = Label();
|
||||||
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(n, Const(intMax)));
|
||||||
|
|
||||||
|
SetFlag(context, PState.QFlag, Const(1));
|
||||||
|
SetIntA32(context, op.Rd, Const(intMax));
|
||||||
|
context.Branch(lblEnd);
|
||||||
|
|
||||||
|
context.MarkLabel(lblCheckLtIntMin);
|
||||||
|
context.BranchIfFalse(lblNoSat, context.ICompareLess(n, Const(intMin)));
|
||||||
|
|
||||||
|
SetFlag(context, PState.QFlag, Const(1));
|
||||||
|
SetIntA32(context, op.Rd, Const(intMin));
|
||||||
|
context.Branch(lblEnd);
|
||||||
|
|
||||||
|
context.MarkLabel(lblNoSat);
|
||||||
|
|
||||||
|
SetIntA32(context, op.Rd, n);
|
||||||
|
|
||||||
|
context.MarkLabel(lblEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSat16(ArmEmitterContext context, int intMin, int intMax)
|
||||||
|
{
|
||||||
|
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||||
|
|
||||||
|
void SetD(int part, Operand value)
|
||||||
|
{
|
||||||
|
if (part == 0)
|
||||||
|
{
|
||||||
|
SetIntA32(context, op.Rd, context.ZeroExtend16(OperandType.I32, value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetIntA32(context, op.Rd, context.BitwiseOr(GetIntA32(context, op.Rd), context.ShiftLeft(value, Const(16))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand n = GetIntA32(context, op.Rn);
|
||||||
|
|
||||||
|
Operand nLow = context.SignExtend16(OperandType.I32, n);
|
||||||
|
Operand nHigh = context.ShiftRightSI(n, Const(16));
|
||||||
|
|
||||||
|
for (int part = 0; part < 2; part++)
|
||||||
|
{
|
||||||
|
Operand nPart = part == 0 ? nLow : nHigh;
|
||||||
|
|
||||||
|
Operand lblCheckLtIntMin = Label();
|
||||||
|
Operand lblNoSat = Label();
|
||||||
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(nPart, Const(intMax)));
|
||||||
|
|
||||||
|
SetFlag(context, PState.QFlag, Const(1));
|
||||||
|
SetD(part, Const(intMax));
|
||||||
|
context.Branch(lblEnd);
|
||||||
|
|
||||||
|
context.MarkLabel(lblCheckLtIntMin);
|
||||||
|
context.BranchIfFalse(lblNoSat, context.ICompareLess(nPart, Const(intMin)));
|
||||||
|
|
||||||
|
SetFlag(context, PState.QFlag, Const(1));
|
||||||
|
SetD(part, Const(intMin));
|
||||||
|
context.Branch(lblEnd);
|
||||||
|
|
||||||
|
context.MarkLabel(lblNoSat);
|
||||||
|
|
||||||
|
SetD(part, nPart);
|
||||||
|
|
||||||
|
context.MarkLabel(lblEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||||
{
|
{
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
|
||||||
EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value);
|
EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,21 @@ namespace ARMeilleure.Instructions
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int DecodeImmShift(ShiftType shiftType, int shift)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
switch (shiftType)
|
||||||
|
{
|
||||||
|
case ShiftType.Lsr: shift = 32; break;
|
||||||
|
case ShiftType.Asr: shift = 32; break;
|
||||||
|
case ShiftType.Ror: shift = 1; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry)
|
public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry)
|
||||||
{
|
{
|
||||||
Operand m = GetIntA32(context, op.Rm);
|
Operand m = GetIntA32(context, op.Rm);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ARMeilleure.Decoders;
|
using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -53,27 +54,6 @@ namespace ARMeilleure.Instructions
|
||||||
EmitAluStore(context, res);
|
EmitAluStore(context, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Smull(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32AluUmull op = (OpCode32AluUmull)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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
EmitNZFlagsCheck(context, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
|
|
||||||
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Smmla(ArmEmitterContext context)
|
public static void Smmla(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitSmmul(context, MullFlags.SignedAdd);
|
EmitSmmul(context, MullFlags.SignedAdd);
|
||||||
|
@ -117,37 +97,40 @@ namespace ARMeilleure.Instructions
|
||||||
EmitGenericAluStoreA32(context, op.Rd, false, hi);
|
EmitGenericAluStoreA32(context, op.Rd, false, hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Smlab(ArmEmitterContext context)
|
public static void Smla__(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||||
|
|
||||||
Operand n = GetIntA32(context, op.Rn);
|
Operand n = GetIntA32(context, op.Rn);
|
||||||
Operand m = GetIntA32(context, op.Rm);
|
Operand m = GetIntA32(context, op.Rm);
|
||||||
|
Operand a = GetIntA32(context, op.Ra);
|
||||||
|
|
||||||
if (op.NHigh)
|
if (op.NHigh)
|
||||||
{
|
{
|
||||||
n = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
n = context.SignExtend16(OperandType.I32, n);
|
n = context.SignExtend16(OperandType.I64, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.MHigh)
|
if (op.MHigh)
|
||||||
{
|
{
|
||||||
m = context.SignExtend16(OperandType.I32, context.ShiftRightUI(m, Const(16)));
|
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m = context.SignExtend16(OperandType.I32, m);
|
m = context.SignExtend16(OperandType.I64, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand res = context.Multiply(n, m);
|
Operand res = context.Multiply(n, m);
|
||||||
|
|
||||||
Operand a = GetIntA32(context, op.Ra);
|
Operand toAdd = context.SignExtend32(OperandType.I64, a);
|
||||||
res = context.Add(res, a);
|
res = context.Add(res, toAdd);
|
||||||
|
Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
|
||||||
|
res = context.ConvertI64ToI32(res);
|
||||||
|
|
||||||
// TODO: set Q flag when last addition overflows (saturation)?
|
UpdateQFlag(context, q);
|
||||||
|
|
||||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +140,7 @@ namespace ARMeilleure.Instructions
|
||||||
EmitMlal(context, true);
|
EmitMlal(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Smlalh(ArmEmitterContext context)
|
public static void Smlal__(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||||
|
|
||||||
|
@ -195,7 +178,37 @@ namespace ARMeilleure.Instructions
|
||||||
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
|
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Smulh(ArmEmitterContext context)
|
public static void Smlaw_(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluMla op = (OpCode32AluMla)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)
|
||||||
{
|
{
|
||||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||||
|
|
||||||
|
@ -225,6 +238,51 @@ namespace ARMeilleure.Instructions
|
||||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Smull(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluUmull op = (OpCode32AluUmull)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 (op.SetFlags)
|
||||||
|
{
|
||||||
|
EmitNZFlagsCheck(context, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
|
||||||
|
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Smulw_(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32AluMla op = (OpCode32AluMla)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 Umlal(ArmEmitterContext context)
|
public static void Umlal(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitMlal(context, false);
|
EmitMlal(context, false);
|
||||||
|
@ -286,5 +344,16 @@ namespace ARMeilleure.Instructions
|
||||||
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
|
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
|
||||||
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
|
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,11 @@ namespace ARMeilleure.Instructions
|
||||||
Sdiv,
|
Sdiv,
|
||||||
Smaddl,
|
Smaddl,
|
||||||
Smsubl,
|
Smsubl,
|
||||||
Smulh,
|
Smul__,
|
||||||
Smull,
|
Smull,
|
||||||
|
Smulw_,
|
||||||
|
Ssat,
|
||||||
|
Ssat16,
|
||||||
Stlr,
|
Stlr,
|
||||||
Stlxp,
|
Stlxp,
|
||||||
Stlxr,
|
Stlxr,
|
||||||
|
@ -491,9 +494,10 @@ namespace ARMeilleure.Instructions
|
||||||
Rsb,
|
Rsb,
|
||||||
Rsc,
|
Rsc,
|
||||||
Sbfx,
|
Sbfx,
|
||||||
Smlab,
|
Smla__,
|
||||||
Smlal,
|
Smlal,
|
||||||
Smlalh,
|
Smlal__,
|
||||||
|
Smlaw_,
|
||||||
Smmla,
|
Smmla,
|
||||||
Smmls,
|
Smmls,
|
||||||
Smmul,
|
Smmul,
|
||||||
|
@ -519,6 +523,8 @@ namespace ARMeilleure.Instructions
|
||||||
Ubfx,
|
Ubfx,
|
||||||
Umlal,
|
Umlal,
|
||||||
Umull,
|
Umull,
|
||||||
|
Usat,
|
||||||
|
Usat16,
|
||||||
Uxtb,
|
Uxtb,
|
||||||
Uxtb16,
|
Uxtb16,
|
||||||
Uxth,
|
Uxth,
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
TFlag = 5,
|
TFlag = 5,
|
||||||
EFlag = 9,
|
EFlag = 9,
|
||||||
|
QFlag = 27,
|
||||||
VFlag = 28,
|
VFlag = 28,
|
||||||
CFlag = 29,
|
CFlag = 29,
|
||||||
ZFlag = 30,
|
ZFlag = 30,
|
||||||
|
|
|
@ -45,10 +45,10 @@ namespace Ryujinx.Tests.Unicorn
|
||||||
set => SetRegister(Arm32Register.PC, value);
|
set => SetRegister(Arm32Register.PC, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint APSR
|
public uint CPSR
|
||||||
{
|
{
|
||||||
get => (uint)GetRegister(Arm32Register.APSR);
|
get => (uint)GetRegister(Arm32Register.CPSR);
|
||||||
set => SetRegister(Arm32Register.APSR, (uint)value);
|
set => SetRegister(Arm32Register.CPSR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpscr
|
public int Fpscr
|
||||||
|
@ -57,28 +57,34 @@ namespace Ryujinx.Tests.Unicorn
|
||||||
set => SetRegister(Arm32Register.FPSCR, (uint)value);
|
set => SetRegister(Arm32Register.FPSCR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool QFlag
|
||||||
|
{
|
||||||
|
get => (CPSR & 0x8000000u) != 0;
|
||||||
|
set => CPSR = (CPSR & ~0x8000000u) | (value ? 0x8000000u : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
public bool OverflowFlag
|
public bool OverflowFlag
|
||||||
{
|
{
|
||||||
get => (APSR & 0x10000000u) != 0;
|
get => (CPSR & 0x10000000u) != 0;
|
||||||
set => APSR = (APSR & ~0x10000000u) | (value ? 0x10000000u : 0u);
|
set => CPSR = (CPSR & ~0x10000000u) | (value ? 0x10000000u : 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CarryFlag
|
public bool CarryFlag
|
||||||
{
|
{
|
||||||
get => (APSR & 0x20000000u) != 0;
|
get => (CPSR & 0x20000000u) != 0;
|
||||||
set => APSR = (APSR & ~0x20000000u) | (value ? 0x20000000u : 0u);
|
set => CPSR = (CPSR & ~0x20000000u) | (value ? 0x20000000u : 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ZeroFlag
|
public bool ZeroFlag
|
||||||
{
|
{
|
||||||
get => (APSR & 0x40000000u) != 0;
|
get => (CPSR & 0x40000000u) != 0;
|
||||||
set => APSR = (APSR & ~0x40000000u) | (value ? 0x40000000u : 0u);
|
set => CPSR = (CPSR & ~0x40000000u) | (value ? 0x40000000u : 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NegativeFlag
|
public bool NegativeFlag
|
||||||
{
|
{
|
||||||
get => (APSR & 0x80000000u) != 0;
|
get => (CPSR & 0x80000000u) != 0;
|
||||||
set => APSR = (APSR & ~0x80000000u) | (value ? 0x80000000u : 0u);
|
set => CPSR = (CPSR & ~0x80000000u) | (value ? 0x80000000u : 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnicornAArch32()
|
public UnicornAArch32()
|
||||||
|
|
|
@ -348,6 +348,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
Assert.That((int)_context.Fpcr | ((int)_context.Fpsr & (int)fpsrMask), Is.EqualTo(_unicornEmu.Fpscr));
|
Assert.That((int)_context.Fpcr | ((int)_context.Fpsr & (int)fpsrMask), Is.EqualTo(_unicornEmu.Fpscr));
|
||||||
|
|
||||||
|
Assert.That(_context.GetPstateFlag(PState.QFlag), Is.EqualTo(_unicornEmu.QFlag));
|
||||||
Assert.That(_context.GetPstateFlag(PState.VFlag), Is.EqualTo(_unicornEmu.OverflowFlag));
|
Assert.That(_context.GetPstateFlag(PState.VFlag), Is.EqualTo(_unicornEmu.OverflowFlag));
|
||||||
Assert.That(_context.GetPstateFlag(PState.CFlag), Is.EqualTo(_unicornEmu.CarryFlag));
|
Assert.That(_context.GetPstateFlag(PState.CFlag), Is.EqualTo(_unicornEmu.CarryFlag));
|
||||||
Assert.That(_context.GetPstateFlag(PState.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag));
|
Assert.That(_context.GetPstateFlag(PState.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag));
|
||||||
|
|
|
@ -11,6 +11,26 @@ namespace Ryujinx.Tests.Cpu
|
||||||
#if Alu32
|
#if Alu32
|
||||||
|
|
||||||
#region "ValueSource (Opcodes)"
|
#region "ValueSource (Opcodes)"
|
||||||
|
private static uint[] _Ssat_Usat_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe6a00010u, // SSAT R0, #1, R0, LSL #0
|
||||||
|
0xe6a00050u, // SSAT R0, #1, R0, ASR #32
|
||||||
|
0xe6e00010u, // USAT R0, #0, R0, LSL #0
|
||||||
|
0xe6e00050u // USAT R0, #0, R0, ASR #32
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Ssat16_Usat16_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe6a00f30u, // SSAT16 R0, #1, R0
|
||||||
|
0xe6e00f30u, // USAT16 R0, #0, R0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static uint[] _Lsr_Lsl_Asr_Ror_()
|
private static uint[] _Lsr_Lsl_Asr_Ror_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new uint[]
|
||||||
|
@ -56,6 +76,41 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void Ssat_Usat([ValueSource("_Ssat_Usat_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rd,
|
||||||
|
[Values(1u, 0xdu)] uint rn,
|
||||||
|
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint sat,
|
||||||
|
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint shift,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((shift & 31) << 7) | ((rd & 15) << 12) | ((sat & 31) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r1: wn, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void Ssat16_Usat16([ValueSource("_Ssat16_Usat16_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rd,
|
||||||
|
[Values(1u, 0xdu)] uint rn,
|
||||||
|
[Values(0u, 7u, 8u, 0xfu)] uint sat,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((rd & 15) << 12) | ((sat & 15) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r1: wn, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
140
Ryujinx.Tests/Cpu/CpuTestMul32.cs
Normal file
140
Ryujinx.Tests/Cpu/CpuTestMul32.cs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#define Mul32
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Cpu
|
||||||
|
{
|
||||||
|
[Category("Mul32")]
|
||||||
|
public sealed class CpuTestMul32 : CpuTest32
|
||||||
|
{
|
||||||
|
#if Mul32
|
||||||
|
|
||||||
|
#region "ValueSource (Opcodes)"
|
||||||
|
private static uint[] _Smlabb_Smlabt_Smlatb_Smlatt_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe1000080u, // SMLABB R0, R0, R0, R0
|
||||||
|
0xe10000C0u, // SMLABT R0, R0, R0, R0
|
||||||
|
0xe10000A0u, // SMLATB R0, R0, R0, R0
|
||||||
|
0xe10000E0u, // SMLATT R0, R0, R0, R0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Smlawb_Smlawt_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe1200080u, // SMLAWB R0, R0, R0, R0
|
||||||
|
0xe12000C0u, // SMLAWT R0, R0, R0, R0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Smulbb_Smulbt_Smultb_Smultt_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe1600080u, // SMULBB R0, R0, R0
|
||||||
|
0xe16000C0u, // SMULBT R0, R0, R0
|
||||||
|
0xe16000A0u, // SMULTB R0, R0, R0
|
||||||
|
0xe16000E0u, // SMULTT R0, R0, R0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Smulwb_Smulwt_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe12000a0u, // SMULWB R0, R0, R0
|
||||||
|
0xe12000e0u, // SMULWT R0, R0, R0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private const int RndCnt = 2;
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SMLA<x><y> <Rd>, <Rn>, <Rm>, <Ra>")]
|
||||||
|
public void Smla___32bit([ValueSource("_Smlabb_Smlabt_Smlatb_Smlatt_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rn,
|
||||||
|
[Values(1u, 0xdu)] uint rm,
|
||||||
|
[Values(2u, 0xdu)] uint ra,
|
||||||
|
[Values(3u, 0xdu)] uint rd,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((ra & 15) << 12) | ((rd & 15) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r0: wn, r1: wm, r2: wa, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SMLAW<x> <Rd>, <Rn>, <Rm>, <Ra>")]
|
||||||
|
public void Smlaw__32bit([ValueSource("_Smlawb_Smlawt_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rn,
|
||||||
|
[Values(1u, 0xdu)] uint rm,
|
||||||
|
[Values(2u, 0xdu)] uint ra,
|
||||||
|
[Values(3u, 0xdu)] uint rd,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((ra & 15) << 12) | ((rd & 15) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r0: wn, r1: wm, r2: wa, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SMUL<x><y> <Rd>, <Rn>, <Rm>")]
|
||||||
|
public void Smul___32bit([ValueSource("_Smulbb_Smulbt_Smultb_Smultt_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rn,
|
||||||
|
[Values(1u, 0xdu)] uint rm,
|
||||||
|
[Values(2u, 0xdu)] uint rd,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((rd & 15) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r0: wn, r1: wm, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SMULW<x> <Rd>, <Rn>, <Rm>")]
|
||||||
|
public void Smulw__32bit([ValueSource("_Smulwb_Smulwt_")] uint opcode,
|
||||||
|
[Values(0u, 0xdu)] uint rn,
|
||||||
|
[Values(1u, 0xdu)] uint rm,
|
||||||
|
[Values(2u, 0xdu)] uint rd,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
||||||
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
|
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm)
|
||||||
|
{
|
||||||
|
opcode |= ((rn & 15) << 0) | ((rm & 15) << 8) | ((rd & 15) << 16);
|
||||||
|
|
||||||
|
uint w31 = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r0: wn, r1: wm, sp: w31);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue