Add SSAT, SSAT16, USAT and USAT16 ARM32 instructions
This commit is contained in:
parent
81b67449a5
commit
7a4eeee034
7 changed files with 254 additions and 3 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -701,6 +701,8 @@ namespace ARMeilleure.Decoders
|
|||
SetA32("<<<<00010110xxxxxxxxxxxx1xx0xxxx", InstName.Smul__, InstEmit32.Smul__, typeof(OpCode32AluMla));
|
||||
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("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
|
||||
|
@ -739,6 +741,8 @@ namespace ARMeilleure.Decoders
|
|||
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, 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("<<<<01101100xxxxxxxxxx000111xxxx", InstName.Uxtb16, InstEmit32.Uxtb16, typeof(OpCode32AluUx));
|
||||
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
|
||||
|
|
|
@ -389,6 +389,20 @@ namespace ARMeilleure.Instructions
|
|||
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)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
@ -460,6 +474,20 @@ namespace ARMeilleure.Instructions
|
|||
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)
|
||||
{
|
||||
EmitSignExtend(context, false, 8);
|
||||
|
@ -553,7 +581,7 @@ namespace ARMeilleure.Instructions
|
|||
lowAdd = context.ZeroExtend16(OperandType.I32, n);
|
||||
highAdd = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
|
||||
|
||||
low16 = context.Add(low16, lowAdd);
|
||||
high16 = context.Add(high16, highAdd);
|
||||
}
|
||||
|
@ -615,9 +643,116 @@ namespace ARMeilleure.Instructions
|
|||
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.BitwiseAnd(value, Const(0xffff)));
|
||||
}
|
||||
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)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,6 +297,21 @@ namespace ARMeilleure.Instructions
|
|||
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)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
@ -328,7 +343,7 @@ namespace ARMeilleure.Instructions
|
|||
if (expected)
|
||||
{
|
||||
context.BranchIfFalse(endLabel, boolValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfTrue(endLabel, boolValue);
|
||||
|
@ -411,7 +426,7 @@ namespace ARMeilleure.Instructions
|
|||
SetFlag(context, PState.CFlag, cOut);
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,8 @@ namespace ARMeilleure.Instructions
|
|||
Smul__,
|
||||
Smull,
|
||||
Smulw_,
|
||||
Ssat,
|
||||
Ssat16,
|
||||
Stlr,
|
||||
Stlxp,
|
||||
Stlxr,
|
||||
|
@ -517,6 +519,8 @@ namespace ARMeilleure.Instructions
|
|||
Ubfx,
|
||||
Umlal,
|
||||
Umull,
|
||||
Usat,
|
||||
Usat16,
|
||||
Uxtb,
|
||||
Uxtb16,
|
||||
Uxth,
|
||||
|
|
|
@ -11,6 +11,26 @@ namespace Ryujinx.Tests.Cpu
|
|||
#if Alu32
|
||||
|
||||
#region "ValueSource (Opcodes)"
|
||||
private static uint[] _Ssat_Usat_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xe6a00010u, // SSAT R0, #1, R0, LSL #0
|
||||
0xe6a00050u, // SSAT R0, #1, R0, ASR #0
|
||||
0xe6e10010u, // USAT R0, #1, R0, LSL #0
|
||||
0xe6e10050u // USAT R0, #1, R0, ASR #0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Ssat16_Usat16_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xe6a00f30u, // SSAT16 R0, #1, R0
|
||||
0xe6e10f30u, // USAT16 R0, #1, R0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Lsr_Lsl_Asr_Ror_()
|
||||
{
|
||||
return new uint[]
|
||||
|
@ -56,6 +76,41 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue