diff --git a/ARMeilleure/Decoders/IOpCode32Simd.cs b/ARMeilleure/Decoders/IOpCode32Simd.cs new file mode 100644 index 0000000000..9c43906426 --- /dev/null +++ b/ARMeilleure/Decoders/IOpCode32Simd.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + interface IOpCode32Simd : IOpCode32, IOpCodeSimd + { + int Elems { get; } + } +} diff --git a/ARMeilleure/Decoders/IOpCode32SimdImm.cs b/ARMeilleure/Decoders/IOpCode32SimdImm.cs new file mode 100644 index 0000000000..9c5f59b845 --- /dev/null +++ b/ARMeilleure/Decoders/IOpCode32SimdImm.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + interface IOpCode32SimdImm : IOpCode32Simd + { + public int Vd { get; } + public long Immediate { get; } + } +} diff --git a/ARMeilleure/Decoders/OpCode.cs b/ARMeilleure/Decoders/OpCode.cs index 0bfc2456bc..b466dfc7a0 100644 --- a/ARMeilleure/Decoders/OpCode.cs +++ b/ARMeilleure/Decoders/OpCode.cs @@ -33,6 +33,7 @@ namespace ARMeilleure.Decoders { case RegisterSize.Int32: return 32; case RegisterSize.Int64: return 64; + case RegisterSize.Simd32: return 32; case RegisterSize.Simd64: return 64; case RegisterSize.Simd128: return 128; } diff --git a/ARMeilleure/Decoders/OpCode32AluBf.cs b/ARMeilleure/Decoders/OpCode32AluBf.cs index 4a833a223b..315e887187 100644 --- a/ARMeilleure/Decoders/OpCode32AluBf.cs +++ b/ARMeilleure/Decoders/OpCode32AluBf.cs @@ -14,7 +14,7 @@ namespace ARMeilleure.Decoders public int Lsb { get; private set; } public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb)); - public int DestMask => SourceMask << Lsb; + public int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb); public OpCode32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { diff --git a/ARMeilleure/Decoders/OpCode32AluMla.cs b/ARMeilleure/Decoders/OpCode32AluMla.cs index d19232e870..d774666d79 100644 --- a/ARMeilleure/Decoders/OpCode32AluMla.cs +++ b/ARMeilleure/Decoders/OpCode32AluMla.cs @@ -11,6 +11,8 @@ namespace ARMeilleure.Decoders public int Ra { get; private set; } public int Rd { get; private set; } + public bool NHigh { get; private set; } + public bool MHigh { get; private set; } public bool SetFlags { get; private set; } public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) @@ -20,6 +22,8 @@ namespace ARMeilleure.Decoders Ra = (opCode >> 12) & 0xf; Rd = (opCode >> 16) & 0xf; + NHigh = ((opCode >> 5) * 0x1) == 1; + MHigh = ((opCode >> 6) * 0x1) == 1; SetFlags = ((opCode >> 20) & 1) != 0; } } diff --git a/ARMeilleure/Decoders/OpCode32AluRsReg.cs b/ARMeilleure/Decoders/OpCode32AluRsReg.cs new file mode 100644 index 0000000000..740b8a8b51 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32AluRsReg.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32AluRsReg : OpCode32Alu + { + public int Rm { get; private set; } + public int Rs { get; private set; } + + public ShiftType ShiftType { get; private set; } + + public OpCode32AluRsReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Rm = (opCode >> 0) & 0xf; + Rs = (opCode >> 8) & 0xf; + + ShiftType = (ShiftType)((opCode >> 5) & 3); + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32AluUmull.cs b/ARMeilleure/Decoders/OpCode32AluUmull.cs index 8255a97ca3..007b7507b6 100644 --- a/ARMeilleure/Decoders/OpCode32AluUmull.cs +++ b/ARMeilleure/Decoders/OpCode32AluUmull.cs @@ -11,6 +11,9 @@ namespace ARMeilleure.Decoders public int Rn { get; private set; } public int Rm { get; private set; } + public bool NHigh { get; private set; } + public bool MHigh { get; private set; } + public bool SetFlags { get; private set; } public DataOp DataOp { get; private set; } @@ -21,6 +24,9 @@ namespace ARMeilleure.Decoders Rm = (opCode >> 8) & 0xf; Rn = (opCode >> 0) & 0xf; + NHigh = ((opCode >> 5) * 0x1) == 1; + MHigh = ((opCode >> 6) * 0x1) == 1; + SetFlags = ((opCode >> 20) & 1) != 0; DataOp = DataOp.Arithmetic; } diff --git a/ARMeilleure/Decoders/OpCode32MemReg.cs b/ARMeilleure/Decoders/OpCode32MemReg.cs new file mode 100644 index 0000000000..2266047d82 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32MemReg.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32MemReg : OpCode32Mem + { + public int Rm { get; private set; } + public OpCode32MemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Rm = (opCode >> 0) & 0xf; + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32Simd.cs b/ARMeilleure/Decoders/OpCode32Simd.cs new file mode 100644 index 0000000000..af729eee8a --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32Simd.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32Simd : OpCode32, IOpCode32Simd + { + public int Vd { get; private set; } + public int Vm { get; private set; } + public int Opc { get; private set; } + public int Size { get; protected set; } + public bool Q { get; private set; } + public bool F { get; private set; } + public int Elems => GetBytesCount() >> ((Size == 1) ? 1 : 2); + + public OpCode32Simd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Size = (opCode >> 20) & 0x1; //fvector size: 1 for 16 bit + Q = ((opCode >> 6) & 0x1) != 0; + F = ((opCode >> 10) & 0x1) != 0; + + RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64; + + Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf); + Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf); + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs b/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs new file mode 100644 index 0000000000..f58a233f60 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdCvtFI : OpCode32SimdS + { + public int Opc2 { get; private set; } + public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Opc2 = (opCode >> 16) & 0x7; + Opc = (opCode >> 7) & 0x1; + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdImm.cs b/ARMeilleure/Decoders/OpCode32SimdImm.cs new file mode 100644 index 0000000000..9929369ac4 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdImm.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdImm : OpCode32, IOpCode32SimdImm + { + public int Vd { get; private set; } + public bool Q { get; private set; } + public long Immediate { get; private set; } + public int Size { get; private set; } + public int Elems => GetBytesCount() >> Size; + public OpCode32SimdImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Vd = (opCode >> 12) & 0xf; + Vd |= (opCode >> 18) & 0x10; + + Q = ((opCode >> 6) & 0x1) > 0; + + int cMode = (opCode >> 8) & 0xf; + int op = (opCode >> 5) & 0x1; + + long imm; + + imm = ((uint)opCode >> 0) & 0xf; + imm |= ((uint)opCode >> 12) & 0x70; + imm |= ((uint)opCode >> 17) & 0x80; + + Tuple parsedImm = OpCodeSimdHelper.GetSimdImmediateAndSize(cMode, op, imm, fpBaseSize: 2); + + Immediate = parsedImm.Item1; + Size = parsedImm.Item2; + + RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64; + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdImm44.cs b/ARMeilleure/Decoders/OpCode32SimdImm44.cs new file mode 100644 index 0000000000..a74c8da56b --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdImm44.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdImm44 : OpCode32, IOpCode32SimdImm + { + public int Vd { get; private set; } + public long Immediate { get; private set; } + public int Size { get; private set; } + public int Elems { get; private set; } + public OpCode32SimdImm44(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Vd = (opCode >> 12) & 0xf; + Vd |= (opCode >> 18) & 0x10; + + Size = ((opCode >> 8) & 0x3) + 1; + + long imm; + + imm = ((uint)opCode >> 0) & 0xf; + imm |= ((uint)opCode >> 12) & 0xf0; + + Immediate = OpCodeSimdHelper.VFPExpandImm(imm, 8 << (Size)); + + RegisterSize = (Size == 3) ? RegisterSize.Simd64 : RegisterSize.Simd32; + Elems = 1; + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdMemImm.cs b/ARMeilleure/Decoders/OpCode32SimdMemImm.cs new file mode 100644 index 0000000000..0482679506 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdMemImm.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdMemImm : OpCode32, IOpCode32Simd + { + public int Vd { get; private set; } + public int Rn { get; private set; } + public int Size { get; private set; } + public bool Add { get; private set; } + public int Immediate { get; private set; } + public int Elems => 1; + + public OpCode32SimdMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Immediate = opCode & 0xff; + + Rn = (opCode >> 16) & 0xf; + Size = (opCode >> 8) & 0x3; + + bool u = (opCode & (1 << 23)) != 0; + Add = u; + + var single = Size != 0b11; + + //RegisterSize = single ? RegisterSize.Simd32 : RegisterSize.Simd64; + + if (single) + { + Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e); + } + else + { + Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf); + } + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdMemPair.cs b/ARMeilleure/Decoders/OpCode32SimdMemPair.cs new file mode 100644 index 0000000000..867da92380 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdMemPair.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdMemPair : OpCode32, IOpCode32Simd + { + private static Dictionary RegsMap = new Dictionary() + { + { 0b0111, 1 }, + { 0b1010, 2 }, + { 0b0110, 3 }, + { 0b0010, 4 }, + + { 0b1000, 1 }, + { 0b1001, 1 }, + { 0b0011, 2 }, + }; + public int Vd { get; private set; } + public int Rn { get; private set; } + public int Rm { get; private set; } + public int Align { get; private set; } + public bool WBack { get; private set; } + public bool RegisterIndex { get; private set; } + public int Size { get; private set; } + public int Elems => GetBytesCount() >> Size; + public int Regs { get; private set; } + public int Increment { get; private set; } + public OpCode32SimdMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Vd = (opCode >> 12) & 0xf; + Vd |= (opCode >> 18) & 0x10; + + Size = (opCode >> 6) & 0x3; + + Align = (opCode >> 4) & 0x3; + Rm = (opCode >> 0) & 0xf; + Rn = (opCode >> 16) & 0xf; + + WBack = Rm != 15; + RegisterIndex = (Rm != 15 && Rm != 13); + + int regs; + if (!RegsMap.TryGetValue((opCode >> 8) & 0xf, out regs)) + { + regs = 1; + } + Regs = regs; + + Increment = Math.Min(Regs, ((opCode >> 8) & 0x1) + 1); + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs b/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs new file mode 100644 index 0000000000..cad0badc63 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdMemSingle.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdMemSingle : OpCode32, IOpCode32Simd + { + public int Vd { get; private set; } + public int Rn { get; private set; } + public int Rm { get; private set; } + public int IndexAlign { get; private set; } + public int Index => IndexAlign >> (1 + Size); + public bool WBack { get; private set; } + public bool RegisterIndex { get; private set; } + public int Size { get; private set; } + public int Elems => GetBytesCount() >> Size; + + public int Increment => (((IndexAlign >> Size) & 1) == 0) ? 1 : 2; + public OpCode32SimdMemSingle(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Vd = (opCode >> 12) & 0xf; + Vd |= (opCode >> 18) & 0x10; + + Size = (opCode >> 10) & 0x3; + + IndexAlign = (opCode >> 4) & 0xf; + Rm = (opCode >> 0) & 0xf; + Rn = (opCode >> 16) & 0xf; + + WBack = Rm != 15; + RegisterIndex = (Rm != 15 && Rm != 13); + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdMovGp.cs b/ARMeilleure/Decoders/OpCode32SimdMovGp.cs new file mode 100644 index 0000000000..446fb127f5 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdMovGp.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdMovGp : OpCode32, IOpCode32Simd + { + public int Size => 2; + public int Elems => 1; + + public int Vn { get; private set; } + public int Rt { get; private set; } + public int Op { get; private set; } + + public int Opc1 { get; private set; } + public int Opc2 { get; private set; } + public OpCode32SimdMovGp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + // which one is used is instruction dependant + Op = ((opCode >> 20) & 0x1); + + Opc1 = ((opCode >> 21) & 0x3); + Opc2 = ((opCode >> 5) & 0x3); + + Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf); + Rt = (opCode >> 12) & 0xf; + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdReg.cs b/ARMeilleure/Decoders/OpCode32SimdReg.cs new file mode 100644 index 0000000000..e24f259dcd --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdReg.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdReg : OpCode32Simd + { + public int Vn { get; private set; } + + public OpCode32SimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf); + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdRegS.cs b/ARMeilleure/Decoders/OpCode32SimdRegS.cs new file mode 100644 index 0000000000..f6cb508862 --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdRegS.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdRegS : OpCode32SimdS + { + public int Vn { get; private set; } + + public OpCode32SimdRegS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + var single = Size != 0b11; + if (single) + { + Vn = ((opCode >> 7) & 0x1) | ((opCode >> 15) & 0x1e); + } + else + { + Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf); + } + } + } +} diff --git a/ARMeilleure/Decoders/OpCode32SimdS.cs b/ARMeilleure/Decoders/OpCode32SimdS.cs new file mode 100644 index 0000000000..4e4bae55ff --- /dev/null +++ b/ARMeilleure/Decoders/OpCode32SimdS.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + class OpCode32SimdS : OpCode32, IOpCode32Simd + { + public int Vd { get; private set; } + public int Vm { get; private set; } + public int Opc { get; protected set; } + public int Size { get; protected set; } + public bool Q { get; private set; } + public int Elems => 1; + + public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) + { + Opc = (opCode >> 15) & 0x3; + Size = (opCode >> 8) & 0x3; + + var single = Size != 0b11; + + RegisterSize = single ? RegisterSize.Simd32 : RegisterSize.Simd64; + + if (single) + { + Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e); + Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e); + } + else + { + Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf); + Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf); + } + } + } +} diff --git a/ARMeilleure/Decoders/OpCodeSimdHelper.cs b/ARMeilleure/Decoders/OpCodeSimdHelper.cs new file mode 100644 index 0000000000..c5547acd69 --- /dev/null +++ b/ARMeilleure/Decoders/OpCodeSimdHelper.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ARMeilleure.Decoders +{ + public static class OpCodeSimdHelper + { + public static Tuple GetSimdImmediateAndSize(int cMode, int op, long imm, int fpBaseSize = 0) + { + int modeLow = cMode & 1; + int modeHigh = cMode >> 1; + int size = 0; + + if (modeHigh == 0b111) + { + switch (op | (modeLow << 1)) + { + case 0: + // 64-bits Immediate. + // Transform abcd efgh into abcd efgh abcd efgh ... + size = 3; + imm = (long)((ulong)imm * 0x0101010101010101); + break; + + case 1: + // 64-bits Immediate. + // Transform abcd efgh into aaaa aaaa bbbb bbbb ... + size = 3; + imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4; + imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2; + imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1; + + imm = (long)((ulong)imm * 0x8040201008040201); + imm = (long)((ulong)imm & 0x8080808080808080); + + imm |= imm >> 4; + imm |= imm >> 2; + imm |= imm >> 1; + break; + + case 2: + // 2 x 32-bits floating point Immediate. + size = 0 + fpBaseSize; + imm = (long)DecoderHelper.Imm8ToFP32Table[(int)imm]; + imm |= imm << 32; + break; + + case 3: + // 64-bits floating point Immediate. + size = 1 + fpBaseSize; + imm = (long)DecoderHelper.Imm8ToFP64Table[(int)imm]; + break; + } + } + else if ((modeHigh & 0b110) == 0b100) + { + // 16-bits shifted Immediate. + size = 1; imm <<= (modeHigh & 1) << 3; + } + else if ((modeHigh & 0b100) == 0b000) + { + // 32-bits shifted Immediate. + size = 2; imm <<= modeHigh << 3; + } + else if ((modeHigh & 0b111) == 0b110) + { + // 32-bits shifted Immediate (fill with ones). + size = 2; imm = ShlOnes(imm, 8 << modeLow); + } + else + { + // 8-bits without shift. + size = 0; + } + + return new Tuple(imm, size); + } + + public static long VFPExpandImm(long imm, int n) + { + int e = (n == 16) ? 5 : ((n == 32) ? 8 : 11); + int f = (n) * 8 - e - 1; + long sign = (imm & 0x80) << (n - 8); + + var bit6 = (imm >> 6) & 0x1; + + long exp = ((imm >> 4) & 0x3); + if (bit6 == 1) exp |= ShlOnes(0, e - 3) << 2; + if (bit6 == 0) exp |= (long)1 << (e - 1); + + long frac = (imm & 0xf) << (f - 4); + + return sign | (exp << f) | frac; + } + + private static long ShlOnes(long value, int shift) + { + if (shift != 0) + { + return value << shift | (long)(ulong.MaxValue >> (64 - shift)); + } + else + { + return value; + } + } + } +} diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index cd04b6a87b..5cf0116c1f 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -599,18 +599,21 @@ namespace ARMeilleure.Decoders #region "OpCode Table (AArch32)" // Base + SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluImm)); + SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsImm)); + SetA32("<<<<0000101xxxxxxxxxxxxx0xx1xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsReg)); SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluImm)); SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0000100xxxxxxxxxxxxx0xx1xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsReg)); SetA32("<<<<0010000xxxxxxxxxxxxxxxxxxxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluImm)); SetA32("<<<<0000000xxxxxxxxxxxxxxxx0xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0000000xxxxxxxxxxxxx0xx1xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsReg)); SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstName.B, InstEmit32.B, typeof(OpCode32BImm)); SetA32("<<<<0111110xxxxxxxxxxxxxx0011111", InstName.Bfc, InstEmit32.Bfc, typeof(OpCode32AluBf)); SetA32("<<<<0111110xxxxxxxxxxxxxx001xxxx", InstName.Bfi, InstEmit32.Bfi, typeof(OpCode32AluBf)); SetA32("<<<<0011110xxxxxxxxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluImm)); SetA32("<<<<0001110xxxxxxxxxxxxxxxx0xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0001110xxxxxxxxxxxxx0xx1xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsReg)); SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, typeof(OpCode32BImm)); SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, typeof(OpCode32BImm)); SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, typeof(OpCode32BReg)); @@ -620,12 +623,14 @@ namespace ARMeilleure.Decoders SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, typeof(OpCode32AluReg)); SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluImm)); SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<00010101xxxx0000xxxx0xx1xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsReg)); SetA32("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluImm)); SetA32("<<<<00010111xxxx0000xxxxxxx0xxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluRsImm)); + SetA32("1111010101111111111100000101xxxx", InstName.Dmb, InstEmit32.Dmb, typeof(OpCode32)); + SetA32("1111010101111111111100000100xxxx", InstName.Dsb, InstEmit32.Dsb, typeof(OpCode32)); SetA32("<<<<0010001xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluImm)); SetA32("<<<<0000001xxxxxxxxxxxxxxxx0xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0000001xxxxxxxxxxxxx0xx1xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsReg)); SetA32("<<<<00011001xxxxxxxx110010011111", InstName.Lda, InstEmit32.Lda, typeof(OpCode32MemLdEx)); SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx)); SetA32("<<<<00011001xxxxxxxx111010011111", InstName.Ldaex, InstEmit32.Ldaex, typeof(OpCode32MemLdEx)); @@ -639,34 +644,47 @@ namespace ARMeilleure.Decoders SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemImm)); SetA32("<<<<011xx1x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemRsImm)); SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemImm8)); - //SetA32("<<<<000xx0x0xxxxxxxx00001101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemReg)); //??? wip + SetA32("<<<<000xx0x0xxxxxxxx00001101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemReg)); SetA32("<<<<00011001xxxxxxxx111110011111", InstName.Ldrex, InstEmit32.Ldrex, typeof(OpCode32MemLdEx)); SetA32("<<<<00011101xxxxxxxx111110011111", InstName.Ldrexb,InstEmit32.Ldrexb,typeof(OpCode32MemLdEx)); SetA32("<<<<00011011xxxxxxxx111110011111", InstName.Ldrexd,InstEmit32.Ldrexd,typeof(OpCode32MemLdEx)); SetA32("<<<<00011111xxxxxxxx111110011111", InstName.Ldrexh,InstEmit32.Ldrexh,typeof(OpCode32MemLdEx)); SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemImm8)); - //SetA32("<<<<000xx0x1xxxxxxxx00001011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemReg)); //??? wip + SetA32("<<<<000xx0x1xxxxxxxx00001011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemReg)); SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstName.Ldrsb, InstEmit32.Ldrsb, typeof(OpCode32MemImm8)); SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemImm8)); SetA32("<<<<1110xxx0xxxxxxxx111xxxx1xxxx", InstName.Mcr, InstEmit32.Mcr, typeof(OpCode32System)); SetA32("<<<<0000001xxxxxxxxxxxxx1001xxxx", InstName.Mla, InstEmit32.Mla, typeof(OpCode32AluMla)); + SetA32("<<<<00000110xxxxxxxxxxxx1001xxxx", InstName.Mls, InstEmit32.Mls, typeof(OpCode32AluMla)); SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm)); SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsImm)); + SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsReg)); SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm16)); SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCodeT16AluImm8)); SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, typeof(OpCode32AluImm16)); SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, typeof(OpCode32System)); + SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, typeof(OpCode32System)); SetA32("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul, InstEmit32.Mul, typeof(OpCode32AluMla)); SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluImm)); SetA32("<<<<0001111x0000xxxxxxxxxxx0xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0001111x0000xxxxxxxx0xx1xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsReg)); SetA32("<<<<0011100xxxxxxxxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluImm)); SetA32("<<<<0001100xxxxxxxxxxxxxxxx0xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0001100xxxxxxxxxxxxx0xx1xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsReg)); + SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, typeof(OpCode32AluReg)); SetA32("<<<<0010011xxxxxxxxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluImm)); SetA32("<<<<0000011xxxxxxxxxxxxxxxx0xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0000011xxxxxxxxxxxxx0xx1xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsReg)); + SetA32("<<<<0010111xxxxxxxxxxxxxxxxxxxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluImm)); + SetA32("<<<<0000111xxxxxxxxxxxxxxxx0xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsImm)); + SetA32("<<<<0000111xxxxxxxxxxxxx0xx1xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsReg)); SetA32("<<<<01110001xxxx1111xxxx0001xxxx", InstName.Sdiv, InstEmit32.Sdiv, typeof(OpCode32AluMla)); + SetA32("<<<<00010000xxxxxxxxxxxx1xx0xxxx", InstName.Smlab, InstEmit32.Smlab, typeof(OpCode32AluMla)); + SetA32("<<<<00010100xxxxxxxxxxxx1xx0xxxx", InstName.Smlal, InstEmit32.Smlal, typeof(OpCode32AluUmull)); + SetA32("<<<<01110101xxxx1111xxxx00x1xxxx", InstName.Smmul, InstEmit32.Smmul, typeof(OpCode32AluMla)); + SetA32("<<<<0010110xxxxxxxxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluImm)); + SetA32("<<<<0000110xxxxxxxxxxxxxxxx0xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg)); + SetA32("<<<<0000110xxxxxxxxxxxxx0xx1xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg)); SetA32("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx)); SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx)); SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx)); @@ -680,35 +698,137 @@ namespace ARMeilleure.Decoders SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemImm)); SetA32("<<<<011xx1x0xxxxxxxxxxxxxxx0xxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemRsImm)); SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemImm8)); - //SetA32("<<<<000xx0x0xxxxxxxx00001111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemReg)); //??? wip + SetA32("<<<<000xx0x0xxxxxxxx00001111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemReg)); SetA32("<<<<00011000xxxxxxxx11111001xxxx", InstName.Strex, InstEmit32.Strex, typeof(OpCode32MemStEx)); SetA32("<<<<00011100xxxxxxxx11111001xxxx", InstName.Strexb,InstEmit32.Strexb,typeof(OpCode32MemStEx)); SetA32("<<<<00011010xxxxxxxx11111001xxxx", InstName.Strexd,InstEmit32.Strexd,typeof(OpCode32MemStEx)); SetA32("<<<<00011110xxxxxxxx11111001xxxx", InstName.Strexh,InstEmit32.Strexh,typeof(OpCode32MemStEx)); SetA32("<<<<00011110xxxx111111001001xxxx", InstName.Stlh, InstEmit32.Stlh, typeof(OpCode32MemStEx)); SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemImm8)); - //SetA32("<<<<000xx0x0xxxxxxxx00001011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemReg)); //??? wip + SetA32("<<<<000xx0x0xxxxxxxx00001011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemReg)); SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluImm)); SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<0000010xxxxxxxxxxxxx0xx1xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm)); SetA32("<<<<1111xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Svc, InstEmit32.Svc, typeof(OpCode32Exception)); + SetA32("<<<<01101010xxxxxxxxxx000111xxxx", InstName.Sxtb, InstEmit32.Sxtb, typeof(OpCode32AluUx)); + SetA32("<<<<01101011xxxxxxxxxx000111xxxx", InstName.Sxth, InstEmit32.Sxth, typeof(OpCode32AluUx)); SetA32("<<<<00110011xxxx0000xxxxxxxxxxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluImm)); + SetA32("<<<<00010011xxxx0000xxxxxxx0xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsImm)); + SetA32("<<<<00010011xxxx0000xxxx0xx1xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsImm)); + SetA32("<<<<0111111111111101111011111110", InstName.Trap, InstEmit32.Trap, typeof(OpCode32Exception)); SetA32("<<<<00110001xxxx0000xxxxxxxxxxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluImm)); SetA32("<<<<00010001xxxx0000xxxxxxx0xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm)); - //RsReg missing + SetA32("<<<<00010001xxxx0000xxxx0xx1xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm)); SetA32("<<<<0111111xxxxxxxxxxxxxx101xxxx", InstName.Ubfx, InstEmit32.Ubfx, typeof(OpCode32AluBf)); SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla)); SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull)); SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx)); - SetA32("<<<<011011111111xxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx)); + SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx)); // FP & SIMD (AArch32) + + SetA32("<<<<11100x11xxxxxxxx10xxx0x0xxxx", InstName.Vadd, InstEmit32.Vadd_S, typeof(OpCode32SimdRegS)); + SetA32("111100100x0xxxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, typeof(OpCode32SimdReg)); + SetA32("111100100x0xxxxxxxxx1100xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, typeof(OpCode32SimdReg)); + SetA32("<<<<11101x11010xxxxx10xx01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, typeof(OpCode32SimdReg)); + SetA32("<<<<11101x11010xxxxx10xx11x0xxxx", InstName.Vcmpe,InstEmit32.Vcmpe, typeof(OpCode32SimdReg)); + SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); + SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); + SetA32("<<<<11101x00xxxxxxxx10xxx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, typeof(OpCode32SimdRegS)); + + // VLD# missing single to all lanes + + SetA32("111101001x10xxxxxxxx0000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx0100xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx1000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 1 + SetA32("111101000x10xxxxxxxx1010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 2 + SetA32("111101000x10xxxxxxxx0110xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 3 + SetA32("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 4 + + SetA32("111101001x10xxxxxxxx0001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx0101xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx1001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x10xxxxxxxx100xxxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype) + SetA32("111101000x10xxxxxxxx0011xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2 + + SetA32("111101001x10xxxxxxxx0010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx0110xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx1010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x10xxxxxxxx010xxxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype) + + SetA32("111101001x10xxxxxxxx0011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx0111xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x10xxxxxxxx1011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x10xxxxxxxx000xxxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype) + SetA32("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult)); SetA32("<<<<11001x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult)); + SetA32("<<<<1101xx01xxxxxxxx10xxxxxxxxxx", InstName.Vldr, InstEmit32.Vldr, typeof(OpCode32SimdMemImm)); + + SetA32("<<<<11100x00xxxxxxxx10xxx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, typeof(OpCode32SimdRegS)); + SetA32("111100100x0xxxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, typeof(OpCode32SimdReg)); + SetA32("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, typeof(OpCode32SimdReg)); + + SetA32("<<<<11100x00xxxxxxxx10xxx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_S, typeof(OpCode32SimdRegS)); + SetA32("111100100x1xxxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, typeof(OpCode32SimdReg)); + SetA32("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, typeof(OpCode32SimdReg)); + + SetA32("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q vector i32 + SetA32("<<<<11101x11xxxxxxxx10xx0000xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm44)); //scalar f16/32/64 based on size 01 10 11 + SetA32("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q i16 + SetA32("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q (dt - from cmode) + SetA32("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q i64 + SetA32("<<<<11101x110000xxxx101x01x0xxxx", InstName.Vmov, InstEmit32.Vmov_S, typeof(OpCode32SimdS)); + //TODO: VORR SIMD + //SetA32("<<<<11100xx0xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenScal)); //from gen purpose + SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, typeof(OpCode32SimdMovGp)); //to/from gen purpose and single precision + //SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenScal)); //to gen purpose + //SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenSp)); //to/from gen purpose x2 and single precision x2 + SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial)); SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, typeof(OpCode32SimdSpecial)); + + SetA32("<<<<11100x10xxxxxxxx10xxx0x0xxxx", InstName.Vmul, InstEmit32.Vmul_S, typeof(OpCode32SimdRegS)); + SetA32("111100110x0xxxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, typeof(OpCode32SimdReg)); + SetA32("1111001x0xxxxxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, typeof(OpCode32SimdReg)); + + SetA32("111100111x11xx01xxxx0x111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, typeof(OpCode32Simd)); + SetA32("<<<<11101x110001xxxx10xx01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, typeof(OpCode32SimdS)); + + SetA32("111101001x00xxxxxxxx0000xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx0100xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx1000xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 1 + SetA32("111101000x00xxxxxxxx1010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 2 + SetA32("111101000x00xxxxxxxx0110xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 3 + SetA32("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 4 + + SetA32("111101001x00xxxxxxxx0001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx0101xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx1001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x00xxxxxxxx100xxxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype) + SetA32("111101000x00xxxxxxxx0011xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2 + + SetA32("111101001x00xxxxxxxx0010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx0110xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx1010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x00xxxxxxxx010xxxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype) + + SetA32("111101001x00xxxxxxxx0011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx0111xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle)); + SetA32("111101001x00xxxxxxxx1011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle)); + SetA32("111101000x00xxxxxxxx000xxxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype) + SetA32("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult)); SetA32("<<<<11010x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult)); + + SetA32("<<<<1101xx00xxxxxxxx10xxxxxxxxxx", InstName.Vstr, InstEmit32.Vstr, typeof(OpCode32SimdMemImm)); + SetA32("<<<<11101x110001xxxx10xx11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, typeof(OpCode32SimdS)); + + SetA32("<<<<11100x11xxxxxxxx10xxx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, typeof(OpCode32SimdRegS)); + SetA32("111100100x1xxxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, typeof(OpCode32SimdReg)); + SetA32("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, typeof(OpCode32SimdReg)); #endregion FillFastLookupTable(_instA32FastLookup, _allInstA32); diff --git a/ARMeilleure/Decoders/RegisterSize.cs b/ARMeilleure/Decoders/RegisterSize.cs index c9cea03ed2..002552dcae 100644 --- a/ARMeilleure/Decoders/RegisterSize.cs +++ b/ARMeilleure/Decoders/RegisterSize.cs @@ -5,6 +5,7 @@ namespace ARMeilleure.Decoders Int32, Int64, Simd64, - Simd128 + Simd128, + Simd32 } } \ No newline at end of file diff --git a/ARMeilleure/Instructions/InstEmitAlu32.cs b/ARMeilleure/Instructions/InstEmitAlu32.cs index 5aaf1a8cc8..b73d5f70fd 100644 --- a/ARMeilleure/Instructions/InstEmitAlu32.cs +++ b/ARMeilleure/Instructions/InstEmitAlu32.cs @@ -31,6 +31,30 @@ namespace ARMeilleure.Instructions EmitAluStore(context, res); } + public static void Adc(ArmEmitterContext context) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + + Operand n = GetAluN(context); + Operand m = GetAluM(context, setCarry: false); + + Operand res = context.Add(n, m); + + Operand carry = GetFlag(PState.CFlag); + + res = context.Add(res, carry); + + if (op.SetFlags) + { + EmitNZFlagsCheck(context, res); + + EmitAdcsCCheck(context, n, res); + EmitAddsVCheck(context, n, m, res); + } + + EmitAluStore(context, res); + } + public static void Clz(ArmEmitterContext context) { IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp; @@ -105,6 +129,52 @@ namespace ARMeilleure.Instructions EmitAluStore(context, res); } + public static void Sbc(ArmEmitterContext context) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + Operand n = GetAluN(context); + Operand m = GetAluM(context, setCarry: false); + + Operand res = context.Subtract(n, m); + + Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1)); + + res = context.Subtract(res, borrow); + + if (op.SetFlags) + { + EmitNZFlagsCheck(context, res); + + EmitSbcsCCheck(context, n, m); + EmitSubsVCheck(context, n, m, res); + } + + EmitAluStore(context, res); + } + + public static void Rsc(ArmEmitterContext context) + { + IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; + Operand n = GetAluN(context); + Operand m = GetAluM(context, setCarry: false); + + Operand res = context.Subtract(m, n); + + Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1)); + + res = context.Subtract(res, borrow); + + if (op.SetFlags) + { + EmitNZFlagsCheck(context, res); + + EmitSbcsCCheck(context, m, n); + EmitSubsVCheck(context, m, n, res); + } + + EmitAluStore(context, res); + } + public static void Rsb(ArmEmitterContext context) { IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; @@ -216,7 +286,7 @@ namespace ARMeilleure.Instructions EmitNZFlagsCheck(context, res); } - public static void Uxtb(ArmEmitterContext context) + private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits) { IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp; @@ -226,14 +296,23 @@ namespace ARMeilleure.Instructions if (op.RotateBits == 0) { res = m; - } + } else { Operand rotate = Const(op.RotateBits); res = context.RotateRight(m, rotate); } - res = context.ZeroExtend8(OperandType.I32, res); + switch (bits) + { + case 8: + res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res); + break; + case 16: + res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res); + break; + } + if (op.Add) { @@ -243,31 +322,24 @@ namespace ARMeilleure.Instructions EmitAluStore(context, res); } + public static void Uxtb(ArmEmitterContext context) + { + EmitSignExtend(context, false, 8); + } + public static void Uxth(ArmEmitterContext context) { - IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp; + EmitSignExtend(context, false, 16); + } - Operand m = GetAluM(context); - Operand res; + public static void Sxtb(ArmEmitterContext context) + { + EmitSignExtend(context, true, 8); + } - if (op.RotateBits == 0) - { - res = m; - } - else - { - Operand rotate = Const(op.RotateBits); - res = context.RotateRight(m, rotate); - } - - res = context.ZeroExtend16(OperandType.I32, res); - - if (op.Add) - { - res = context.Add(res, GetAluN(context)); - } - - EmitAluStore(context, res); + public static void Sxth(ArmEmitterContext context) + { + EmitSignExtend(context, true, 16); } public static void Mla(ArmEmitterContext context) @@ -288,6 +360,19 @@ namespace ARMeilleure.Instructions EmitAluStore(context, res); } + public static void Mls(ArmEmitterContext context) + { + OpCode32AluMla op = (OpCode32AluMla)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 Udiv(ArmEmitterContext context) { EmitDiv(context, true); @@ -393,6 +478,16 @@ namespace ARMeilleure.Instructions EmitAluStore(context, res); } + public static void Rbit(ArmEmitterContext context) + { + OpCode32Alu op = (OpCode32Alu)context.CurrOp; + + Operand m = GetAluM(context); + + Operand res = context.Call(new _U32_U32(SoftFallback.ReverseBits32), m); + + EmitAluStore(context, res); + } public static void Bfc(ArmEmitterContext context) { @@ -413,7 +508,7 @@ namespace ARMeilleure.Instructions Operand part = context.BitwiseAnd(n, Const(op.SourceMask)); if (op.Lsb != 0) part = context.ShiftLeft(part, Const(op.Lsb)); Operand res = context.BitwiseAnd(d, Const(~op.DestMask)); - res = context.BitwiseOr(res, part); + res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask))); SetIntA32(context, op.Rd, res); } diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs index 215baf714d..02131daa5e 100644 --- a/ARMeilleure/Instructions/InstEmitAluHelper.cs +++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs @@ -119,6 +119,7 @@ namespace ARMeilleure.Instructions case OpCode32AluImm16 op: return Const(op.Immediate); case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry); + case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry); case OpCodeT16AluImm8 op: return Const(op.Immediate); @@ -212,6 +213,70 @@ namespace ARMeilleure.Instructions return m; } + public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry) + { + Operand m = GetIntA32(context, op.Rm); + Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs)); + Operand shiftIsZero = context.ICompareEqual(s, Const(0)); + + Operand zeroResult = m; + Operand shiftResult = m; + + Operand shiftSkip = Label(); + //Operand shiftZeroSkip = Label(); + + setCarry &= op.SetFlags; + + context.BranchIfTrue(shiftSkip, shiftIsZero); + + /* + // if zero, fudge the shift number a little + + switch (op.ShiftType) + { + case ShiftType.Lsr: zeroResult = GetLsrC(context, m, setCarry, 32); break; + case ShiftType.Asr: zeroResult = GetAsrC(context, m, setCarry, 32); break; + case ShiftType.Ror: + // ror 0 is rrx + zeroResult = GetRrxC(context, m, setCarry); + break; + } + + context.Branch(shiftSkip); + context.MarkLabel(shiftZeroSkip); + */ + + switch (op.ShiftType) + { + case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s); break; + case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s); break; + case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s); break; + case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s); break; + } + + context.MarkLabel(shiftSkip); + + return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult); + } + + public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + { + Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); + + Operand result = context.ShiftLeft(m, shift); + if (setCarry) + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift)); + + cOut = context.BitwiseAnd(cOut, Const(1)); + cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); + + SetFlag(context, PState.CFlag, cOut); + } + + return context.ConditionalSelect(shiftLarge, Const(0), result); + } + public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift) { if ((uint)shift > 32) @@ -242,6 +307,22 @@ namespace ARMeilleure.Instructions } } + public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + { + Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); + Operand result = context.ShiftRightUI(m, shift); + if (setCarry) + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); + + cOut = context.BitwiseAnd(cOut, Const(1)); + cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); + + SetFlag(context, PState.CFlag, cOut); + } + return context.ConditionalSelect(shiftLarge, Const(0), result); + } + public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift) { if ((uint)shift > 32) @@ -278,6 +359,43 @@ namespace ARMeilleure.Instructions return Const(0); } + public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + { + Operand normalShift = Label(); + Operand end = Label(); + + Operand l32Result; + Operand ge32Result; + + Operand less32 = context.ICompareLess(shift, Const(32)); + + context.BranchIfTrue(normalShift, less32); + + ge32Result = context.ShiftRightSI(m, Const(31)); + + if (setCarry) + { + SetCarryMLsb(context, ge32Result); + } + + context.Branch(end); + context.MarkLabel(normalShift); + + l32Result = context.ShiftRightSI(m, shift); + if (setCarry) + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); + + cOut = context.BitwiseAnd(cOut, Const(1)); + + SetFlag(context, PState.CFlag, cOut); + } + + context.MarkLabel(end); + + return context.ConditionalSelect(less32, l32Result, ge32Result); + } + public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift) { if ((uint)shift >= 32) @@ -302,6 +420,18 @@ namespace ARMeilleure.Instructions } } + public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + { + shift = context.BitwiseAnd(shift, Const(0x1f)); + m = context.RotateRight(m, shift); + + if (setCarry) + { + SetCarryMMsb(context, m); + } + return m; + } + public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift) { shift &= 0x1f; diff --git a/ARMeilleure/Instructions/InstEmitException32.cs b/ARMeilleure/Instructions/InstEmitException32.cs index f0e09238f4..18bb929372 100644 --- a/ARMeilleure/Instructions/InstEmitException32.cs +++ b/ARMeilleure/Instructions/InstEmitException32.cs @@ -16,6 +16,11 @@ namespace ARMeilleure.Instructions EmitExceptionCall(context, NativeInterface.SupervisorCall); } + public static void Trap(ArmEmitterContext context) + { + EmitExceptionCall(context, NativeInterface.Break); + } + private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func) { OpCode32Exception op = (OpCode32Exception)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitHelper.cs b/ARMeilleure/Instructions/InstEmitHelper.cs index 37cd1f7585..2a26193993 100644 --- a/ARMeilleure/Instructions/InstEmitHelper.cs +++ b/ARMeilleure/Instructions/InstEmitHelper.cs @@ -52,6 +52,32 @@ namespace ARMeilleure.Instructions return Register(regIndex, RegisterType.Vector, OperandType.V128); } + public static Operand GetVecA32(ArmEmitterContext context, int regIndex, int registerSizeLog) + { + // vector registers in A32 all overlap each other - eg. Q0 = D0:D1 = S0:S1:S2:S3 + // so we need to select the relevant part of a quad vector based on register size. + int elemSizeLog = (4 - registerSizeLog); + int quadIndex = regIndex >> elemSizeLog; + int subIndex = regIndex & ((1 << elemSizeLog) - 1); + Operand result = Register(quadIndex, RegisterType.Vector, OperandType.V128); + if (subIndex != 0) + { + result = context.RotateRight(result, Const(subIndex << elemSizeLog)); + } + + switch (registerSizeLog) + { + case 4: // quad word + return result; + case 3: // double word + return context.VectorZeroUpper64(result); + case 2: // single word + return context.VectorZeroUpper96(result); + } + + return result; + } + public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value) { if (regIndex == RegisterAlias.Aarch32Pc) diff --git a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs index 8054943592..9fade9317c 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryEx32.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryEx32.cs @@ -131,6 +131,9 @@ namespace ARMeilleure.Instructions EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered); } + public static void Dmb(ArmEmitterContext context) => EmitBarrier(context); + public static void Dsb(ArmEmitterContext context) => EmitBarrier(context); + private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType) { IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index 082559d2ba..51332b696a 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -514,8 +514,12 @@ namespace ARMeilleure.Instructions // ARM32. case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry); + case OpCode32MemReg op: return GetIntA32(context, op.Rm); + case OpCode32Mem op: return Const(op.Immediate); + case OpCode32SimdMemImm op: return Const(op.Immediate); + default: throw InvalidOpCodeType(context.CurrOp); } } diff --git a/ARMeilleure/Instructions/InstEmitMul32.cs b/ARMeilleure/Instructions/InstEmitMul32.cs index b5cd6d58e3..68380e859a 100644 --- a/ARMeilleure/Instructions/InstEmitMul32.cs +++ b/ARMeilleure/Instructions/InstEmitMul32.cs @@ -6,11 +6,23 @@ using ARMeilleure.Translation; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; +using System; namespace ARMeilleure.Instructions { static partial class InstEmit32 { + [Flags] + private enum MullFlags + { + Subtract = 0, + Add = 1 << 0, + Signed = 1 << 1, + + SignedAdd = Signed | Add, + SignedSubtract = Signed | Subtract + } + public static void Umull(ArmEmitterContext context) { OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp; @@ -32,6 +44,99 @@ namespace ARMeilleure.Instructions EmitGenericStore(context, op.RdLo, op.SetFlags, lo); } + public static void Smmul(ArmEmitterContext context) + { + OpCode32AluMla op = (OpCode32AluMla)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 ((op.RawOpCode & (1 << 5)) != 0) + { + res = context.Add(res, Const(0x80000000L)); + } + + Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32))); + + EmitGenericStore(context, op.Rd, false, hi); + } + + + public static void Smlab(ArmEmitterContext context) + { + OpCode32AluMla op = (OpCode32AluMla)context.CurrOp; + + Operand n = GetIntA32(context, op.Rn); + Operand m = GetIntA32(context, op.Rm); + + if (op.NHigh) + { + n = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16))); + } + else + { + n = context.ZeroExtend16(OperandType.I32, n); + } + + if (op.MHigh) + { + m = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(m, Const(16))); + } + else + { + m = context.ZeroExtend16(OperandType.I32, m); + } + + Operand res = context.Multiply(n, m); + + Operand a = GetIntA32(context, op.Ra); + res = context.Add(res, a); + + //todo: set Q flag when last addition overflows (saturation)? + + EmitGenericStore(context, op.Rd, false, res); + } + + public static void Smlal(ArmEmitterContext context) + { + OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp; + + Operand n = GetIntA32(context, op.Rn); + Operand m = GetIntA32(context, op.Rm); + + if (op.NHigh) + { + n = context.ZeroExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16))); + } + else + { + n = context.ZeroExtend16(OperandType.I64, n); + } + + if (op.MHigh) + { + m = context.ZeroExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16))); + } + else + { + m = context.ZeroExtend16(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); + + EmitGenericStore(context, op.RdHi, false, hi); + EmitGenericStore(context, op.RdLo, false, lo); + } + private static void EmitGenericStore(ArmEmitterContext context, int Rd, bool setFlags, Operand value) { if (Rd == RegisterAlias.Aarch32Pc) diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs new file mode 100644 index 0000000000..dbffd2a61c --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -0,0 +1,224 @@ +using ARMeilleure.Decoders; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Text; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper32; +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Instructions +{ + //TODO: SSE2 path + static partial class InstEmit32 + { + public static void Vadd_S(ArmEmitterContext context) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + } + + public static void Vadd_V(ArmEmitterContext context) + { + EmitVectorBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + } + + public static void Vadd_I(ArmEmitterContext context) + { + EmitVectorBinaryOpZx32(context, (op1, op2) => context.Add(op1, op2)); + } + + public static void Vmov_S(ArmEmitterContext context) + { + EmitScalarUnaryOpF32(context, (op1) => op1); + } + + public static void Vneg_S(ArmEmitterContext context) + { + EmitScalarUnaryOpF32(context, (op1) => context.Negate(op1)); + } + + public static void Vneg_V(ArmEmitterContext context) + { + if ((context.CurrOp as OpCode32Simd).F) + { + EmitVectorUnaryOpF32(context, (op1) => context.Negate(op1)); + } + else + { + EmitVectorUnaryOpSx32(context, (op1) => context.Negate(op1)); + } + } + + public static void Vdiv_S(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Divide(op1, op2)); + } + else + { + EmitScalarBinaryOpF32(context, (op1, op2) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2); + }); + } + } + + public static void Vdiv_V(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitVectorBinaryOpF32(context, (op1, op2) => context.Divide(op1, op2)); + } + else + { + EmitVectorBinaryOpF32(context, (op1, op2) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2); + }); + } + } + + public static void Vmul_S(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2)); + } + else + { + EmitScalarBinaryOpF32(context, (op1, op2) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + }); + } + } + + public static void Vmul_V(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitVectorBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2)); + } + else + { + EmitVectorBinaryOpF32(context, (op1, op2) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2); + }); + } + } + + public static void Vmul_I(ArmEmitterContext context) + { + EmitVectorBinaryOpZx32(context, (op1, op2) => context.Multiply(op1, op2)); + } + + public static void Vmla_S(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return context.Add(op1, context.Multiply(op2, op3)); + }); + } + else + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + }); + } + } + + public static void Vmla_V(ArmEmitterContext context) + { + if (false)//Optimizations.FastFP) + { + EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3))); + } + else + { + EmitVectorTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3); + }); + } + } + + public static void Vmla_I(ArmEmitterContext context) + { + EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3))); + } + + public static void Vmls_S(ArmEmitterContext context) + { + if (Optimizations.FastFP) + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return context.Subtract(op1, context.Multiply(op2, op3)); + }); + } + else + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + }); + } + } + + public static void Vmls_V(ArmEmitterContext context) + { + if (false)//Optimizations.FastFP) + { + EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3))); + } + else + { + EmitVectorTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3); + }); + } + } + + public static void Vmls_I(ArmEmitterContext context) + { + EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3))); + } + + public static void Vsqrt_S(ArmEmitterContext context) + { + /* + if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0) + { + EmitScalarUnaryOpF(context, Intrinsic.X86Rsqrtss, 0); + } */ + + EmitScalarUnaryOpF32(context, (op1) => + { + return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtEstimate, SoftFloat64.FPRSqrtEstimate, op1); + }); + } + + public static void Vsub_S(ArmEmitterContext context) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2)); + } + + public static void Vsub_V(ArmEmitterContext context) + { + EmitVectorBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2)); + } + + public static void Vsub_I(ArmEmitterContext context) + { + EmitVectorBinaryOpZx32(context, (op1, op2) => context.Subtract(op1, op2)); + } + } +} diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp32.cs b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs new file mode 100644 index 0000000000..4332d5f2d4 --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdCmp32.cs @@ -0,0 +1,70 @@ +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Text; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper32; +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Instructions +{ + static partial class InstEmit32 + { + public static void Vcmp(ArmEmitterContext context) + { + EmitVcmpOrVcmpe(context, false); + } + + public static void Vcmpe(ArmEmitterContext context) + { + EmitVcmpOrVcmpe(context, true); + } + + private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + bool cmpWithZero = (op.RawOpCode & (1 << 16)) != 0; + { + int fSize = op.Size & 1; + OperandType type = fSize != 0 ? OperandType.FP64 : OperandType.FP32; + + + Operand ne = ExtractScalar(context, type, op.Vd); + Operand me; + + if (cmpWithZero) + { + me = fSize == 0 ? ConstF(0f) : ConstF(0d); + } + else + { + me = ExtractScalar(context, type, op.Vm); + } + + Delegate dlg = op.Size != 0 + ? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare) + : (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare); + + Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs)); + + EmitSetFPSCRFlags(context, nzcv); + } + } + + private static void EmitSetFPSCRFlags(ArmEmitterContext context, Operand flags) + { + Delegate getDlg = new _U32(NativeInterface.GetFpscr); + Operand fpscr = context.Call(getDlg); + + fpscr = context.BitwiseOr(context.ShiftLeft(flags, Const(28)), context.BitwiseAnd(fpscr, Const(0x0fffffff))); + + Delegate setDlg = new _Void_U32(NativeInterface.SetFpscr); + context.Call(setDlg, fpscr); + } + } +} diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs new file mode 100644 index 0000000000..e9444b508a --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -0,0 +1,137 @@ +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.State; +using ARMeilleure.Translation; +using System; +using System.Diagnostics; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper32; +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Instructions +{ + static partial class InstEmit32 + { + public static void Vcvt_FI(ArmEmitterContext context) + { + OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; + + bool toInteger = (op.Opc2 & 0b100) != 0; + + OperandType floatSize = op.RegisterSize == RegisterSize.Simd64 ? OperandType.FP64 : OperandType.FP32; + + if (toInteger) + { + bool unsigned = (op.Opc2 & 1) == 0; + bool roundWithFpscr = op.Opc == 1; + + Operand toConvert = ExtractScalar(context, floatSize, op.Vm); + + Operand asInteger; + + // TODO: Fast Path + if (roundWithFpscr) + { + // these need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point. + if (floatSize == OperandType.FP64) + { + if (unsigned) + { + asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert); + } + else + { + asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert); + } + + } + else + { + if (unsigned) + { + asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert); + } + else + { + asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert); + } + } + + } + else + { + // round towards zero + if (floatSize == OperandType.FP64) + { + if (unsigned) + { + asInteger = context.Call(new _U32_F64(CastDoubleToUInt32), toConvert); + } + else + { + asInteger = context.Call(new _S32_F64(CastDoubleToInt32), toConvert); + } + + } + else + { + if (unsigned) + { + asInteger = context.Call(new _U32_F32(CastFloatToUInt32), toConvert); + } + else + { + asInteger = context.Call(new _S32_F32(CastFloatToInt32), toConvert); + } + } + } + + InsertScalar(context, op.Vd, asInteger); + } + else + { + bool unsigned = op.Opc == 0; + + Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm); + + Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned); + + InsertScalar(context, op.Vd, asFloat); + } + } + + private static int CastDoubleToInt32(double value) + { + return (int)value; + } + + private static uint CastDoubleToUInt32(double value) + { + return (uint)value; + } + private static int CastFloatToInt32(float value) + { + return (int)value; + } + private static uint CastFloatToUInt32(float value) + { + return (uint)value; + } + + private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed) + { + Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64); + + if (signed) + { + return context.ConvertToFP(type, value); + } + else + { + return context.ConvertToFPUI(type, value); + } + } + } +} diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs new file mode 100644 index 0000000000..4b7fc7aacf --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -0,0 +1,341 @@ +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; +using System.Text; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Instructions +{ + using Func1I = Func; + using Func2I = Func; + using Func3I = Func; + + static class InstEmitSimdHelper32 + { + public static (int, int) GetQuadwordAndSubindex(int index, RegisterSize size) + { + switch (size) + { + case RegisterSize.Simd128: + return (index >> 1, 0); + case RegisterSize.Simd64: + return (index >> 1, index & 1); + case RegisterSize.Simd32: + return (index >> 2, index & 3); + } + + throw new NotImplementedException("Unrecognized Vector Register Size!"); + } + + public static Operand ExtractScalar(ArmEmitterContext context, OperandType type, int reg) + { + if (type == OperandType.FP64) + { + // from dreg + return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1); + } + else + { + // from sreg + return context.VectorExtract(type, GetVecA32(reg >> 2), reg & 3); + } + } + + public static void InsertScalar(ArmEmitterContext context, int reg, Operand value) + { + Operand vec, insert; + if (value.Type == OperandType.FP64) + { + // from dreg + vec = GetVecA32(reg >> 1); + insert = context.VectorInsert(vec, value, reg & 1); + + } + else + { + // from sreg + vec = GetVecA32(reg >> 2); + insert = context.VectorInsert(vec, value, reg & 3); + } + context.Copy(vec, insert); + } + + public static void EmitVectorImmUnaryOp32(ArmEmitterContext context, Func1I emit) + { + IOpCode32SimdImm op = (IOpCode32SimdImm)context.CurrOp; + + Operand imm = Const(op.Immediate); + + int elems = op.Elems; + (int index, int subIndex) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand vec = GetVec(index); + Operand res = vec; + + for (int item = 0; item < elems; item++, subIndex++) + { + res = EmitVectorInsert(context, vec, emit(imm), subIndex, op.Size); + } + + context.Copy(vec, res); + } + + public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Func1I emit) + { + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; + + OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32; + + Operand m = ExtractScalar(context, type, op.Vm); + + InsertScalar(context, op.Vd, emit(m)); + } + + public static void EmitScalarBinaryOpF32(ArmEmitterContext context, Func2I emit) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32; + + Operand n = ExtractScalar(context, type, op.Vn); + Operand m = ExtractScalar(context, type, op.Vm); + + InsertScalar(context, op.Vd, emit(n, m)); + } + public static void EmitScalarTernaryOpF32(ArmEmitterContext context, Func3I emit) + { + OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; + + OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32; + + Operand a = ExtractScalar(context, type, op.Vd); + Operand n = ExtractScalar(context, type, op.Vn); + Operand m = ExtractScalar(context, type, op.Vm); + + InsertScalar(context, op.Vd, emit(a, n, m)); + } + + public static void EmitVectorUnaryOpF32(ArmEmitterContext context, Func1I emit) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + int sizeF = op.Size & 1; + + OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32; + + int elems = op.GetBytesCount() >> sizeF + 2; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + for (int index = 0; index < elems; index++) + { + Operand ne = context.VectorExtract(type, GetVecA32(vm), index + em * elems); + + res = context.VectorInsert(res, emit(ne), index + ed * elems); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorBinaryOpF32(ArmEmitterContext context, Func2I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + int sizeF = op.Size & 1; + + OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32; + + int elems = op.GetBytesCount() >> sizeF + 2; + + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + for (int index = 0; index < elems; index++) + { + Operand ne = context.VectorExtract(type, GetVecA32(vn), index + en * elems); + Operand me = context.VectorExtract(type, GetVecA32(vm), index + em * elems); + + res = context.VectorInsert(res, emit(ne, me), index + ed * elems); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorTernaryOpF32(ArmEmitterContext context, Func3I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + int sizeF = op.Size & 1; + + OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32; + + int elems = op.GetBytesCount() >> sizeF + 2; + + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + for (int index = 0; index < elems; index++) + { + Operand de = context.VectorExtract(type, GetVecA32(vd), index + ed * elems); + Operand ne = context.VectorExtract(type, GetVecA32(vn), index + en * elems); + Operand me = context.VectorExtract(type, GetVecA32(vm), index + em * elems); + + res = context.VectorInsert(res, emit(de, ne, me), index); + } + + context.Copy(GetVecA32(vd), res); + } + + // INTEGER + + public static void EmitVectorUnaryOpSx32(ArmEmitterContext context, Func1I emit) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractSx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(ne), index + ed * elems, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorBinaryOpSx32(ArmEmitterContext context, Func2I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractSx(context, vn, index + en * elems, op.Size); + Operand me = EmitVectorExtractSx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(ne, me), index + ed * elems, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorTernaryOpSx32(ArmEmitterContext context, Func3I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand de = EmitVectorExtractSx(context, vd, index + ed * elems, op.Size); + Operand ne = EmitVectorExtractSx(context, vn, index + en * elems, op.Size); + Operand me = EmitVectorExtractSx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(de, ne, me), index + ed * elems, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorUnaryOpZx32(ArmEmitterContext context, Func1I emit) + { + OpCode32Simd op = (OpCode32Simd)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractZx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(ne), index + ed * elems, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorBinaryOpZx32(ArmEmitterContext context, Func2I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand ne = EmitVectorExtractZx(context, vn, index + en * elems, op.Size); + Operand me = EmitVectorExtractZx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(ne, me), index + ed * elems, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + + public static void EmitVectorTernaryOpZx32(ArmEmitterContext context, Func3I emit) + { + OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp; + + (int vm, int em) = GetQuadwordAndSubindex(op.Vm, op.RegisterSize); + (int vn, int en) = GetQuadwordAndSubindex(op.Vn, op.RegisterSize); + (int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize); + + Operand res = GetVecA32(vd); + + int elems = op.GetBytesCount() >> op.Size; + + for (int index = 0; index < elems; index++) + { + Operand de = EmitVectorExtractZx(context, vd, index + ed * elems, op.Size); + Operand ne = EmitVectorExtractZx(context, vn, index + en * elems, op.Size); + Operand me = EmitVectorExtractZx(context, vm, index + em * elems, op.Size); + + res = EmitVectorInsert(context, res, emit(de, ne, me), index, op.Size); + } + + context.Copy(GetVecA32(vd), res); + } + } +} diff --git a/ARMeilleure/Instructions/InstEmitSimdMemory32.cs b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs index 8f17efb623..a240a01aea 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMemory32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs @@ -12,6 +12,135 @@ namespace ARMeilleure.Instructions { static partial class InstEmit32 { + public static void Vst1(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 1, false); + } + public static void Vst2(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 2, false); + } + public static void Vst3(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 3, false); + } + public static void Vst4(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 4, false); + } + + public static void Vld1(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 1, true); + } + public static void Vld2(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 2, true); + } + public static void Vld3(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 3, true); + } + public static void Vld4(ArmEmitterContext context) + { + EmitVStoreOrLoadN(context, 4, true); + } + + public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load) + { + if (context.CurrOp is OpCode32SimdMemSingle) + { + OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp; + + int eBytes = 1 << op.Size; + + Operand n = GetIntA32(context, op.Rn); + + //check alignment (?) + int offset = 0; + int d = op.Vd; + + for (int i=0; i> 1), d >> 1, index, op.Size); + } + else + { + EmitStoreSimd(context, address, d >> 1, index, op.Size); + } + //TODO: big endian at size == 4 + offset += eBytes; + d += op.Increment; + } + + if (op.WBack) + { + if (op.RegisterIndex) + { + Operand m = GetIntA32(context, op.Rm); + SetIntA32(context, op.Rn, context.Add(n, m)); + } + else + { + SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes))); + } + } + } + else + { + OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp; + + int eBytes = 1 << op.Size; + + Operand n = GetIntA32(context, op.Rn); + int offset = 0; + int d = op.Vd; + + for (int reg = 0; reg < op.Regs; reg++) + { + for (int elem = 0; elem < op.Elems; elem++) + { + int elemD = d + reg; + for (int i = 0; i < count; i++) + { + // write an element from a double simd register + // add ebytes for each element + Operand address = context.Add(n, Const(offset)); + int index = ((d & 1) << (3 - op.Size)) + elem; + if (load) + { + EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size); + } + else + { + EmitStoreSimd(context, address, d >> 1, index, op.Size); + } + + offset += eBytes; + elemD += op.Increment; + } + } + } + + if (op.WBack) + { + if (op.RegisterIndex) + { + Operand m = GetIntA32(context, op.Rm); + SetIntA32(context, op.Rn, context.Add(n, m)); + } + else + { + SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs))); + } + } + } + } public static void Vldm(ArmEmitterContext context) { @@ -77,5 +206,90 @@ namespace ARMeilleure.Instructions offset += byteSize; } } + + public static void Vldr(ArmEmitterContext context) + { + EmitVLoadOrStore(context, AccessType.Load); + } + + public static void Vstr(ArmEmitterContext context) + { + EmitVLoadOrStore(context, AccessType.Store); + } + + private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType) + { + OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp; + + Operand n = context.Copy(GetIntA32(context, op.Rn)); + Operand m = GetMemM(context, setCarry: false); + + Operand address = op.Add + ? context.Add(n, m) + : context.Subtract(n, m); + + int size = op.Size; + + if ((accType & AccessType.Load) != 0) + { + + if (size == DWordSizeLog2) + { + int vecQ = op.Vd >> 1; + int vecSElem = (op.Vd & 1) << 1; + Operand vec = GetVecA32(vecQ); + + Operand lblBigEndian = Label(); + Operand lblEnd = Label(); + + context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); + + EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2); + EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2); + + context.Branch(lblEnd); + + context.MarkLabel(lblBigEndian); + + EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2); + EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2); + + context.MarkLabel(lblEnd); + } + else + { + Operand vec = GetVecA32(op.Vd >> 2); + EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2-size), size); + } + } + else + { + if (size == DWordSizeLog2) + { + int vecQ = op.Vd >> 1; + int vecSElem = (op.Vd & 1) << 1; + Operand lblBigEndian = Label(); + Operand lblEnd = Label(); + + context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag)); + + EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2); + EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2); + + context.Branch(lblEnd); + + context.MarkLabel(lblBigEndian); + + EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2); + EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2); + + context.MarkLabel(lblEnd); + } + else + { + EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size); + } + } + } } } diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/ARMeilleure/Instructions/InstEmitSimdMove32.cs new file mode 100644 index 0000000000..d4a8d76859 --- /dev/null +++ b/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -0,0 +1,40 @@ +using ARMeilleure.Decoders; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Collections.Generic; + +using static ARMeilleure.Instructions.InstEmitHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper; +using static ARMeilleure.Instructions.InstEmitSimdHelper32; +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Instructions +{ + static partial class InstEmit32 + { + public static void Vmov_I(ArmEmitterContext context) + { + EmitVectorImmUnaryOp32(context, (op1) => op1); + } + + public static void Vmov_GS(ArmEmitterContext context) + { + OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp; + Operand vec = GetVecA32(op.Vn >> 2); + if (op.Op == 1) + { + // to general purpose + Operand value = context.VectorExtract(OperandType.I32, vec, op.Vn & 0x3); + SetIntA32(context, op.Rt, value); + } + else + { + // from general purpose + Operand value = GetIntA32(context, op.Rt); + context.Copy(vec, context.VectorInsert(vec, value, op.Vn & 0x3)); + + } + } + } +} diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs index 76c7573dfb..4795ae9b09 100644 --- a/ARMeilleure/Instructions/InstEmitSystem32.cs +++ b/ARMeilleure/Instructions/InstEmitSystem32.cs @@ -146,6 +146,22 @@ namespace ARMeilleure.Instructions throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); } break; + + case 7: + switch (op.CRm) // Cache and Memory barrier + { + case 10: + switch (op.Opc2) + { + case 5: // Data Memory Barrier Register + return; // SUPER TODO: DO NOT KNOW HOW TO IMPLEMENT THIS + default: + throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}."); + } + default: + throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}."); + } + default: throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } @@ -154,8 +170,6 @@ namespace ARMeilleure.Instructions private static void EmitSetNzcv(ArmEmitterContext context, Operand t) { - OpCodeSystem op = (OpCodeSystem)context.CurrOp; - Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag)); v = context.BitwiseAnd(v, Const(1)); @@ -173,5 +187,37 @@ namespace ARMeilleure.Instructions SetFlag(context, PState.ZFlag, z); SetFlag(context, PState.NFlag, n); } + + public static void Mrrc(ArmEmitterContext context) + { + var op = (OpCode32System)context.CurrOp; + + if (op.Coproc != 15) + { + throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}."); + } + + var opc = (op.RawOpCode >> 4) & 0xf; + + Delegate dlg; + switch (op.CRm) + { + case 14: // Timer + switch (opc) + { + case 0: + dlg = new _U64(NativeInterface.GetCntpctEl0); break; + default: + throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}."); + } + break; + default: throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); + } + + Operand result = context.Call(dlg); + + SetIntA32(context, op.Rt, context.ConvertI64ToI32(result)); + SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32)))); + } } } diff --git a/ARMeilleure/Instructions/InstName.cs b/ARMeilleure/Instructions/InstName.cs index 9af0a2c083..ef5d82a56a 100644 --- a/ARMeilleure/Instructions/InstName.cs +++ b/ARMeilleure/Instructions/InstName.cs @@ -92,6 +92,8 @@ namespace ARMeilleure.Instructions Sub, Subs, Svc, + Sxtb, + Sxth, Sys, Tbnz, Tbz, @@ -472,10 +474,16 @@ namespace ARMeilleure.Instructions Ldrsh, Mcr, Mla, + Mls, Mov, Mrc, + Mrrc, Mvn, Rsb, + Rsc, + Smlab, + Smlal, + Smmul, Stl, Stlb, Stlex, @@ -492,6 +500,7 @@ namespace ARMeilleure.Instructions Strexh, Strh, Teq, + Trap, Tst, Ubfx, Umull, @@ -499,9 +508,32 @@ namespace ARMeilleure.Instructions Uxth, // FP & SIMD (AArch32) - Vstm, + Vadd, + Vcmp, + Vcmpe, + Vcvt, + Vdiv, + Vld1, + Vld2, + Vld3, + Vld4, + Vldm, + Vldr, + Vmla, + Vmls, Vmrs, Vmsr, - Vldm + Vmul, + Vneg, + Vst1, + Vst2, + Vst3, + Vst4, + Vstm, + Vstr, + Vsqrt, + Vsub, + + Vmov } } diff --git a/ARMeilleure/Instructions/SoftFallback.cs b/ARMeilleure/Instructions/SoftFallback.cs index 10bb47df54..90a36bf513 100644 --- a/ARMeilleure/Instructions/SoftFallback.cs +++ b/ARMeilleure/Instructions/SoftFallback.cs @@ -420,6 +420,26 @@ namespace ARMeilleure.Instructions return MathF.Truncate(value); } } + + public static int FloatToInt32(float value) + { + return (int)RoundF(value); + } + + public static int DoubleToInt32(double value) + { + return (int)Round(value); + } + + public static uint FloatToUInt32(float value) + { + return (uint)RoundF(value); + } + + public static uint DoubleToUInt32(double value) + { + return (uint)Round(value); + } #endregion #region "Saturation" diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index 12b992742e..9a94394de6 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -85,6 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall _svcFuncs32 = new Dictionary { { 0x06, nameof(SvcHandler.QueryMemory32) }, + { 0x08, nameof(SvcHandler.CreateThread32) }, { 0x27, nameof(SvcHandler.OutputDebugString32) }, { 0x29, nameof(SvcHandler.GetInfo64) } }; @@ -104,6 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall Dictionary funcTable = aarch32 ? _svcFuncs32 : _svcFuncs64; if (funcTable.TryGetValue(svcId, out string svcName)) { + if (svcId == 0x08) { } return table[svcId] = GenerateMethod(svcName, aarch32); } @@ -127,10 +129,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ParameterInfo[] methodArgs = methodInfo.GetParameters(); int maxArgs = aarch32 ? SvcFuncMaxArguments32 : SvcFuncMaxArguments; + int numArgs = methodArgs.Count(x => !x.IsOut); - if (methodArgs.Count(x => !x.IsOut) > maxArgs) + if (numArgs > maxArgs) { - throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {maxArgs}."); + //throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {maxArgs}."); } ILGenerator generator = method.GetILGenerator(); @@ -209,7 +212,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall generator.Emit(OpCodes.Ldc_I4, index); generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); + int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index; + generator.Emit(OpCodes.Ldc_I4, argIndex); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -271,7 +275,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall else { generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); + int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index; + generator.Emit(OpCodes.Ldc_I4, argIndex); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs index 0908de1083..6c8f6ba739 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs @@ -19,6 +19,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle); } + public KernelResult CreateThread32( + ulong entrypoint, + ulong argsPtr, + ulong stackTop, + int cpuCore, + int priority, + + out int handle) + { + return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle); + } + private KernelResult CreateThread( ulong entrypoint, ulong argsPtr, diff --git a/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs b/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs new file mode 100644 index 0000000000..7e33a1229e --- /dev/null +++ b/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Tests.Unicorn.Native +{ + public enum Arm32Register + { + INVALID = 0, + + APSR, + APSR_NZCV, + CPSR, + FPEXC, + FPINST, + FPSCR, + FPSCR_NZCV, + FPSID, + ITSTATE, + LR, + PC, + SP, + SPSR, + D0, + D1, + D2, + D3, + D4, + D5, + D6, + D7, + D8, + D9, + D10, + D11, + D12, + D13, + D14, + D15, + D16, + D17, + D18, + D19, + D20, + D21, + D22, + D23, + D24, + D25, + D26, + D27, + D28, + D29, + D30, + D31, + FPINST2, + MVFR0, + MVFR1, + MVFR2, + Q0, + Q1, + Q2, + Q3, + Q4, + Q5, + Q6, + Q7, + Q8, + Q9, + Q10, + Q11, + Q12, + Q13, + Q14, + Q15, + R0, + R1, + R2, + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, + S0, + S1, + S2, + S3, + S4, + S5, + S6, + S7, + S8, + S9, + S10, + S11, + S12, + S13, + S14, + S15, + S16, + S17, + S18, + S19, + S20, + S21, + S22, + S23, + S24, + S25, + S26, + S27, + S28, + S29, + S30, + S31, + C1_C0_2, + C13_C0_2, + C13_C0_3, + IPSR, + MSP, + PSP, + CONTROL, + ENDING, + + // alias registers + R13 = SP, + R14 = LR, + R15 = PC, + SB = R9, + SL = R10, + FP = R11, + IP = R12, + } +} diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs new file mode 100644 index 0000000000..5209b60da6 --- /dev/null +++ b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs @@ -0,0 +1,276 @@ +using Ryujinx.Tests.Unicorn.Native; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Tests.Unicorn +{ + public class UnicornAArch32 + { + internal readonly IntPtr uc; + + public IndexedProperty R + { + get + { + return new IndexedProperty( + (int i) => GetX(i), + (int i, uint value) => SetX(i, value)); + } + } + + public IndexedProperty Q + { + get + { + return new IndexedProperty( + (int i) => GetQ(i), + (int i, SimdValue value) => SetQ(i, value)); + } + } + + public uint LR + { + get => GetRegister(Arm32Register.LR); + set => SetRegister(Arm32Register.LR, value); + } + + public uint SP + { + get => GetRegister(Arm32Register.SP); + set => SetRegister(Arm32Register.SP, value); + } + + public uint PC + { + get => GetRegister(Arm32Register.PC); + set => SetRegister(Arm32Register.PC, value); + } + + public uint APSR + { + get => (uint)GetRegister(Arm32Register.APSR); + set => SetRegister(Arm32Register.APSR, (uint)value); + } + + public int Fpscr + { + get => (int)GetRegister(Arm32Register.FPSCR); + set => SetRegister(Arm32Register.FPSCR, (uint)value); + } + + public bool OverflowFlag + { + get => (APSR & 0x10000000u) != 0; + set => APSR = (APSR & ~0x10000000u) | (value ? 0x10000000u : 0u); + } + + public bool CarryFlag + { + get => (APSR & 0x20000000u) != 0; + set => APSR = (APSR & ~0x20000000u) | (value ? 0x20000000u : 0u); + } + + public bool ZeroFlag + { + get => (APSR & 0x40000000u) != 0; + set => APSR = (APSR & ~0x40000000u) | (value ? 0x40000000u : 0u); + } + + public bool NegativeFlag + { + get => (APSR & 0x80000000u) != 0; + set => APSR = (APSR & ~0x80000000u) | (value ? 0x80000000u : 0u); + } + + public UnicornAArch32() + { + Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc)); + + //SetRegister(Arm32Register.FPSCR, 0x00300000); + } + + ~UnicornAArch32() + { + Interface.Checked(Native.Interface.uc_close(uc)); + } + + public void RunForCount(ulong count) + { + Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count)); + } + + public void Step() + { + RunForCount(1); + } + + private static Arm32Register[] XRegisters = new Arm32Register[16] + { + Arm32Register.R0, + Arm32Register.R1, + Arm32Register.R2, + Arm32Register.R3, + Arm32Register.R4, + Arm32Register.R5, + Arm32Register.R6, + Arm32Register.R7, + Arm32Register.R8, + Arm32Register.R9, + Arm32Register.R10, + Arm32Register.R11, + Arm32Register.R12, + Arm32Register.R13, + Arm32Register.R14, + Arm32Register.R15, + }; + + private static Arm32Register[] QRegisters = new Arm32Register[16] + { + Arm32Register.Q0, + Arm32Register.Q1, + Arm32Register.Q2, + Arm32Register.Q3, + Arm32Register.Q4, + Arm32Register.Q5, + Arm32Register.Q6, + Arm32Register.Q7, + Arm32Register.Q8, + Arm32Register.Q9, + Arm32Register.Q10, + Arm32Register.Q11, + Arm32Register.Q12, + Arm32Register.Q13, + Arm32Register.Q14, + Arm32Register.Q15 + }; + + public uint GetX(int index) + { + if ((uint)index > 30) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return GetRegister(XRegisters[index]); + } + + public void SetX(int index, uint value) + { + if ((uint)index > 30) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + SetRegister(XRegisters[index], value); + } + + public SimdValue GetQ(int index) + { + if ((uint)index > 31) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return GetVector(QRegisters[index]); + } + + public void SetQ(int index, SimdValue value) + { + if ((uint)index > 31) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + SetVector(QRegisters[index], value); + } + + private uint GetRegister(Arm32Register register) + { + byte[] data = new byte[4]; + + Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, data)); + + return (uint)BitConverter.ToInt32(data, 0); + } + + private void SetRegister(Arm32Register register, uint value) + { + byte[] data = BitConverter.GetBytes(value); + + Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + } + + private SimdValue GetVector(Arm32Register register) + { + byte[] data = new byte[16]; + + Interface.Checked(Interface.uc_reg_read(uc, (int)register, data)); + + return new SimdValue(data); + } + + private void SetVector(Arm32Register register, SimdValue value) + { + byte[] data = value.ToArray(); + + Interface.Checked(Interface.uc_reg_write(uc, (int)register, data)); + } + + public byte[] MemoryRead(ulong address, ulong size) + { + byte[] value = new byte[size]; + + Interface.Checked(Interface.uc_mem_read(uc, address, value, size)); + + return value; + } + + public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0]; + public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); + public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); + public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); + + public void MemoryWrite(ulong address, byte[] value) + { + Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length)); + } + + public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new byte[] { value }); + public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value)); + + public void MemoryMap(ulong address, ulong size, MemoryPermission permissions) + { + Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions)); + } + + public void MemoryUnmap(ulong address, ulong size) + { + Interface.Checked(Interface.uc_mem_unmap(uc, address, size)); + } + + public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions) + { + Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions)); + } + + public static bool IsAvailable() + { + try + { + Interface.uc_version(out _, out _); + + return true; + } + catch (DllNotFoundException) + { + return false; + } + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs new file mode 100644 index 0000000000..df236ffe86 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -0,0 +1,498 @@ +using ARMeilleure.Memory; +using ARMeilleure.State; +using ARMeilleure.Translation; +using NUnit.Framework; +using Ryujinx.Tests.Unicorn; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.Tests.Cpu +{ + [TestFixture] + class CpuTest32 + { + private uint _currAddress; + private long _size; + + private uint _entryPoint; + + private IntPtr _ramPointer; + + private MemoryManager _memory; + + private ExecutionContext _context; + + private Translator _translator; + + private static bool _unicornAvailable; + private UnicornAArch32 _unicornEmu; + + static CpuTest32() + { + _unicornAvailable = UnicornAArch32.IsAvailable(); + + if (!_unicornAvailable) + { + Console.WriteLine("WARNING: Could not find Unicorn."); + } + } + + [SetUp] + public void Setup() + { + _currAddress = 0x1000; + _size = 0x1000; + + _entryPoint = _currAddress; + + _ramPointer = Marshal.AllocHGlobal(new IntPtr(_size)); + _memory = new MemoryManager(_ramPointer); + _memory.Map((long)_currAddress, 0, _size); + + _context = new ExecutionContext(); + _context.IsAarch32 = true; + + _translator = new Translator(_memory); + + if (_unicornAvailable) + { + _unicornEmu = new UnicornAArch32(); + _unicornEmu.MemoryMap(_currAddress, (ulong)_size, MemoryPermission.READ | MemoryPermission.EXEC); + _unicornEmu.PC = _entryPoint; + } + } + + [TearDown] + public void Teardown() + { + Marshal.FreeHGlobal(_ramPointer); + _memory = null; + _context = null; + _translator = null; + _unicornEmu = null; + } + + protected void Reset() + { + Teardown(); + Setup(); + } + + protected void Opcode(uint opcode) + { + _memory.WriteUInt32((long)_currAddress, opcode); + + if (_unicornAvailable) + { + _unicornEmu.MemoryWrite32((ulong)_currAddress, opcode); + } + + _currAddress += 4; + } + + protected ExecutionContext GetContext() => _context; + protected void SetContext(uint r0 = 0, + uint r1 = 0, + uint r2 = 0, + uint r3 = 0, + uint sp = 0, + V128 v0 = default, + V128 v1 = default, + V128 v2 = default, + V128 v3 = default, + V128 v4 = default, + V128 v5 = default, + V128 v14 = default, + V128 v15 = default, + bool overflow = false, + bool carry = false, + bool zero = false, + bool negative = false, + int fpscr = 0) + { + _context.SetX(0, r0); + _context.SetX(1, r1); + _context.SetX(2, r2); + _context.SetX(3, r3); + + _context.SetX(0xd, sp); + + _context.SetV(0, v0); + _context.SetV(1, v1); + _context.SetV(2, v2); + _context.SetV(3, v3); + _context.SetV(4, v4); + _context.SetV(5, v5); + _context.SetV(14, v14); + _context.SetV(15, v15); + + _context.SetPstateFlag(PState.VFlag, overflow); + _context.SetPstateFlag(PState.CFlag, carry); + _context.SetPstateFlag(PState.ZFlag, zero); + _context.SetPstateFlag(PState.NFlag, negative); + + _context.Fpsr = FPSR.A32Mask & (FPSR)fpscr; + _context.Fpcr = FPCR.A32Mask & (FPCR)fpscr; + + if (_unicornAvailable) + { + _unicornEmu.R[0] = r0; + _unicornEmu.R[1] = r1; + _unicornEmu.R[2] = r2; + _unicornEmu.R[3] = r3; + + _unicornEmu.SP = sp; + + _unicornEmu.Q[0] = V128ToSimdValue(v0); + _unicornEmu.Q[1] = V128ToSimdValue(v1); + _unicornEmu.Q[2] = V128ToSimdValue(v2); + _unicornEmu.Q[3] = V128ToSimdValue(v3); + _unicornEmu.Q[4] = V128ToSimdValue(v4); + _unicornEmu.Q[5] = V128ToSimdValue(v5); + _unicornEmu.Q[14] = V128ToSimdValue(v14); + _unicornEmu.Q[15] = V128ToSimdValue(v15); + + _unicornEmu.OverflowFlag = overflow; + _unicornEmu.CarryFlag = carry; + _unicornEmu.ZeroFlag = zero; + _unicornEmu.NegativeFlag = negative; + + _unicornEmu.Fpscr = fpscr; + } + } + + protected void ExecuteOpcodes() + { + _translator.Execute(_context, _entryPoint); + + if (_unicornAvailable) + { + _unicornEmu.RunForCount((ulong)(_currAddress - _entryPoint - 4) / 4); + } + } + + protected ExecutionContext SingleOpcode(uint opcode, + uint r0 = 0, + uint r1 = 0, + uint r2 = 0, + uint r3 = 0, + uint sp = 0, + V128 v0 = default, + V128 v1 = default, + V128 v2 = default, + V128 v3 = default, + V128 v4 = default, + V128 v5 = default, + V128 v14 = default, + V128 v15 = default, + bool overflow = false, + bool carry = false, + bool zero = false, + bool negative = false, + int fpscr = 0) + { + Opcode(opcode); + Opcode(0xe12fff1e); // BX LR + SetContext(r0, r1, r2, r3, sp, v0, v1, v2, v3, v4, v5, v14, v15, overflow, carry, zero, negative, fpscr); + ExecuteOpcodes(); + + return GetContext(); + } + + /// Rounding Mode control field. + public enum RMode + { + /// Round to Nearest mode. + Rn, + /// Round towards Plus Infinity mode. + Rp, + /// Round towards Minus Infinity mode. + Rm, + /// Round towards Zero mode. + Rz + }; + + /// Floating-point Control Register. + protected enum Fpcr + { + /// Rounding Mode control field. + RMode = 22, + /// Flush-to-zero mode control bit. + Fz = 24, + /// Default NaN mode control bit. + Dn = 25, + /// Alternative half-precision control bit. + Ahp = 26 + } + + /// Floating-point Status Register. + [Flags] + protected enum Fpsr + { + None = 0, + + /// Invalid Operation cumulative floating-point exception bit. + Ioc = 1 << 0, + /// Divide by Zero cumulative floating-point exception bit. + Dzc = 1 << 1, + /// Overflow cumulative floating-point exception bit. + Ofc = 1 << 2, + /// Underflow cumulative floating-point exception bit. + Ufc = 1 << 3, + /// Inexact cumulative floating-point exception bit. + Ixc = 1 << 4, + /// Input Denormal cumulative floating-point exception bit. + Idc = 1 << 7, + + /// Cumulative saturation bit. + Qc = 1 << 27 + } + + [Flags] + protected enum FpSkips + { + None = 0, + + IfNaNS = 1, + IfNaND = 2, + + IfUnderflow = 4, + IfOverflow = 8 + } + + protected enum FpTolerances + { + None, + + UpToOneUlpsS, + UpToOneUlpsD + } + + protected void CompareAgainstUnicorn( + Fpsr fpsrMask = Fpsr.None, + FpSkips fpSkips = FpSkips.None, + FpTolerances fpTolerances = FpTolerances.None) + { + if (!_unicornAvailable) + { + return; + } + + if (fpSkips != FpSkips.None) + { + ManageFpSkips(fpSkips); + } + + Assert.That(_context.GetX(0), Is.EqualTo(_unicornEmu.R[0])); + Assert.That(_context.GetX(1), Is.EqualTo(_unicornEmu.R[1])); + Assert.That(_context.GetX(2), Is.EqualTo(_unicornEmu.R[2])); + Assert.That(_context.GetX(3), Is.EqualTo(_unicornEmu.R[3])); + Assert.That(_context.GetX(4), Is.EqualTo(_unicornEmu.R[4])); + Assert.That(_context.GetX(5), Is.EqualTo(_unicornEmu.R[5])); + Assert.That(_context.GetX(6), Is.EqualTo(_unicornEmu.R[6])); + Assert.That(_context.GetX(7), Is.EqualTo(_unicornEmu.R[7])); + Assert.That(_context.GetX(8), Is.EqualTo(_unicornEmu.R[8])); + Assert.That(_context.GetX(9), Is.EqualTo(_unicornEmu.R[9])); + Assert.That(_context.GetX(10), Is.EqualTo(_unicornEmu.R[10])); + Assert.That(_context.GetX(11), Is.EqualTo(_unicornEmu.R[11])); + Assert.That(_context.GetX(12), Is.EqualTo(_unicornEmu.R[12])); + Assert.That(_context.GetX(13), Is.EqualTo(_unicornEmu.R[13])); + Assert.That(_context.GetX(14), Is.EqualTo(_unicornEmu.R[14])); + + if (fpTolerances == FpTolerances.None) + { + Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0])); + } + else + { + ManageFpTolerances(fpTolerances); + } + Assert.That(V128ToSimdValue(_context.GetV(1)), Is.EqualTo(_unicornEmu.Q[1])); + Assert.That(V128ToSimdValue(_context.GetV(2)), Is.EqualTo(_unicornEmu.Q[2])); + Assert.That(V128ToSimdValue(_context.GetV(3)), Is.EqualTo(_unicornEmu.Q[3])); + Assert.That(V128ToSimdValue(_context.GetV(4)), Is.EqualTo(_unicornEmu.Q[4])); + Assert.That(V128ToSimdValue(_context.GetV(5)), Is.EqualTo(_unicornEmu.Q[5])); + Assert.That(V128ToSimdValue(_context.GetV(6)), Is.EqualTo(_unicornEmu.Q[6])); + Assert.That(V128ToSimdValue(_context.GetV(7)), Is.EqualTo(_unicornEmu.Q[7])); + Assert.That(V128ToSimdValue(_context.GetV(8)), Is.EqualTo(_unicornEmu.Q[8])); + Assert.That(V128ToSimdValue(_context.GetV(9)), Is.EqualTo(_unicornEmu.Q[9])); + Assert.That(V128ToSimdValue(_context.GetV(10)), Is.EqualTo(_unicornEmu.Q[10])); + Assert.That(V128ToSimdValue(_context.GetV(11)), Is.EqualTo(_unicornEmu.Q[11])); + Assert.That(V128ToSimdValue(_context.GetV(12)), Is.EqualTo(_unicornEmu.Q[12])); + Assert.That(V128ToSimdValue(_context.GetV(13)), Is.EqualTo(_unicornEmu.Q[13])); + Assert.That(V128ToSimdValue(_context.GetV(14)), Is.EqualTo(_unicornEmu.Q[14])); + Assert.That(V128ToSimdValue(_context.GetV(15)), Is.EqualTo(_unicornEmu.Q[15])); + + Assert.That((int)_context.Fpcr | ((int)_context.Fpsr & (int)fpsrMask), Is.EqualTo(_unicornEmu.Fpscr)); + + 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.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag)); + Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag)); + } + + private void ManageFpSkips(FpSkips fpSkips) + { + if (fpSkips.HasFlag(FpSkips.IfNaNS)) + { + if (float.IsNaN(_unicornEmu.Q[0].AsFloat())) + { + Assert.Ignore("NaN test."); + } + } + else if (fpSkips.HasFlag(FpSkips.IfNaND)) + { + if (double.IsNaN(_unicornEmu.Q[0].AsDouble())) + { + Assert.Ignore("NaN test."); + } + } + + if (fpSkips.HasFlag(FpSkips.IfUnderflow)) + { + if ((_unicornEmu.Fpscr & (int)Fpsr.Ufc) != 0) + { + Assert.Ignore("Underflow test."); + } + } + + if (fpSkips.HasFlag(FpSkips.IfOverflow)) + { + if ((_unicornEmu.Fpscr & (int)Fpsr.Ofc) != 0) + { + Assert.Ignore("Overflow test."); + } + } + } + + private void ManageFpTolerances(FpTolerances fpTolerances) + { + bool IsNormalOrSubnormalS(float f) => float.IsNormal(f) || float.IsSubnormal(f); + bool IsNormalOrSubnormalD(double d) => double.IsNormal(d) || double.IsSubnormal(d); + + if (!Is.EqualTo(_unicornEmu.Q[0]).ApplyTo(V128ToSimdValue(_context.GetV(0))).IsSuccess) + { + if (fpTolerances == FpTolerances.UpToOneUlpsS) + { + if (IsNormalOrSubnormalS(_unicornEmu.Q[0].AsFloat()) && + IsNormalOrSubnormalS(_context.GetV(0).AsFloat())) + { + Assert.That(_context.GetV(0).GetFloat(0), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(0)).Within(1).Ulps); + Assert.That(_context.GetV(0).GetFloat(1), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(1)).Within(1).Ulps); + Assert.That(_context.GetV(0).GetFloat(2), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(2)).Within(1).Ulps); + Assert.That(_context.GetV(0).GetFloat(3), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(3)).Within(1).Ulps); + + Console.WriteLine(fpTolerances); + } + else + { + Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0])); + } + } + + if (fpTolerances == FpTolerances.UpToOneUlpsD) + { + if (IsNormalOrSubnormalD(_unicornEmu.Q[0].AsDouble()) && + IsNormalOrSubnormalD(_context.GetV(0).AsDouble())) + { + Assert.That(_context.GetV(0).GetDouble(0), + Is.EqualTo(_unicornEmu.Q[0].GetDouble(0)).Within(1).Ulps); + Assert.That(_context.GetV(0).GetDouble(1), + Is.EqualTo(_unicornEmu.Q[0].GetDouble(1)).Within(1).Ulps); + + Console.WriteLine(fpTolerances); + } + else + { + Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0])); + } + } + } + } + + private static SimdValue V128ToSimdValue(V128 value) + { + return new SimdValue(value.GetUInt64(0), value.GetUInt64(1)); + } + + protected static V128 MakeVectorScalar(float value) => new V128(value); + protected static V128 MakeVectorScalar(double value) => new V128(value); + + protected static V128 MakeVectorE0(ulong e0) => new V128(e0, 0); + protected static V128 MakeVectorE1(ulong e1) => new V128(0, e1); + + protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new V128(e0, e1); + + protected static ulong GetVectorE0(V128 vector) => vector.GetUInt64(0); + protected static ulong GetVectorE1(V128 vector) => vector.GetUInt64(1); + + protected static ushort GenNormalH() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUShort(); + while ((rnd & 0x7C00u) == 0u || + (~rnd & 0x7C00u) == 0u); + + return (ushort)rnd; + } + + protected static ushort GenSubnormalH() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUShort(); + while ((rnd & 0x03FFu) == 0u); + + return (ushort)(rnd & 0x83FFu); + } + + protected static uint GenNormalS() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUInt(); + while ((rnd & 0x7F800000u) == 0u || + (~rnd & 0x7F800000u) == 0u); + + return rnd; + } + + protected static uint GenSubnormalS() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUInt(); + while ((rnd & 0x007FFFFFu) == 0u); + + return rnd & 0x807FFFFFu; + } + + protected static ulong GenNormalD() + { + ulong rnd; + + do rnd = TestContext.CurrentContext.Random.NextULong(); + while ((rnd & 0x7FF0000000000000ul) == 0ul || + (~rnd & 0x7FF0000000000000ul) == 0ul); + + return rnd; + } + + protected static ulong GenSubnormalD() + { + ulong rnd; + + do rnd = TestContext.CurrentContext.Random.NextULong(); + while ((rnd & 0x000FFFFFFFFFFFFFul) == 0ul); + + return rnd & 0x800FFFFFFFFFFFFFul; + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/Ryujinx.Tests/Cpu/CpuTestAlu32.cs new file mode 100644 index 0000000000..a07c9d27de --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestAlu32.cs @@ -0,0 +1,93 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Tests.Cpu +{ + [Category("Alu32")] + class CpuTestAlu32 : CpuTest32 + { + private const int RndCnt = 2; + + [Test, Pairwise, Description("RBIT , ")] + public void Rbit_32bit([Values(0u, 0xdu)] uint rd, + [Values(1u, 0xdu)] uint rm, + [Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + { + uint opcode = 0xe6ff0f30; // RBIT R0, R0 + opcode |= ((rm & 15) << 0) | ((rd & 15) << 12); + + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + + SingleOpcode(opcode, r1: wn, sp: w31); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("LSRS {,} , ")] + public void Lsr([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue, + [Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount) + { + uint opcode = 0xe1b00030; // LSRS R0, R0, R0 + uint rd = 0; + uint rm = 1; + uint rs = 2; + opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rs & 15) << 8); + + SingleOpcode(opcode, r1: shiftValue, r2: (uint)shiftAmount); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("LSLS {,} , ")] + public void Lsl([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue, + [Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount) + { + uint opcode = 0xe1b00010; // LSLS R0, R0, R0 + uint rd = 0; + uint rm = 1; + uint rs = 2; + opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rs & 15) << 8); + + SingleOpcode(opcode, r1: shiftValue, r2: (uint)shiftAmount); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("ASRS {,} , ")] + public void Asr([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue, + [Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount) + { + uint opcode = 0xe1b00050; // ASRS R0, R0, R0 + uint rd = 0; + uint rm = 1; + uint rs = 2; + opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rs & 15) << 8); + + SingleOpcode(opcode, r1: shiftValue, r2: (uint)shiftAmount); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("RORS {,} , ")] + public void Ror([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue, + [Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount) + { + uint opcode = 0xe1b00070; // RORS R0, R0, R0 + uint rd = 0; + uint rm = 1; + uint rs = 2; + opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rs & 15) << 8); + + SingleOpcode(opcode, r1: shiftValue, r2: (uint)shiftAmount); + + CompareAgainstUnicorn(); + } + } +}