Many more instructions, start on SIMD and testing framework.

This commit is contained in:
riperiperi 2020-01-03 17:47:40 +00:00 committed by Thog
parent 7af22d43e5
commit 600a8b5e9e
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
44 changed files with 3216 additions and 50 deletions

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ARMeilleure.Decoders
{
interface IOpCode32Simd : IOpCode32, IOpCodeSimd
{
int Elems { get; }
}
}

View file

@ -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; }
}
}

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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<long, int> parsedImm = OpCodeSimdHelper.GetSimdImmediateAndSize(cMode, op, imm, fpBaseSize: 2);
Immediate = parsedImm.Item1;
Size = parsedImm.Item2;
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ARMeilleure.Decoders
{
class OpCode32SimdMemPair : OpCode32, IOpCode32Simd
{
private static Dictionary<int, int> RegsMap = new Dictionary<int, int>()
{
{ 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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ARMeilleure.Decoders
{
public static class OpCodeSimdHelper
{
public static Tuple<long, int> 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<long, int>(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;
}
}
}
}

View file

@ -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);

View file

@ -5,6 +5,7 @@ namespace ARMeilleure.Decoders
Int32,
Int64,
Simd64,
Simd128
Simd128,
Simd32
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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)

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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<Operand, Operand>;
using Func2I = Func<Operand, Operand, Operand>;
using Func3I = Func<Operand, Operand, Operand, Operand>;
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);
}
}
}

View file

@ -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<count; i++)
{
//write an element from a double simd register
Operand address = context.Add(n, Const(offset));
int index = ((d & 1) << (3 - op.Size)) + op.Index;
if (load)
{
EmitLoadSimd(context, address, GetVecA32(d >> 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);
}
}
}
}
}

View file

@ -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));
}
}
}
}

View file

@ -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))));
}
}
}

View file

@ -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
}
}

View file

@ -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"

View file

@ -85,6 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
_svcFuncs32 = new Dictionary<int, string>
{
{ 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<int, string> 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));

View file

@ -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,

View file

@ -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,
}
}

View file

@ -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<int, uint> R
{
get
{
return new IndexedProperty<int, uint>(
(int i) => GetX(i),
(int i, uint value) => SetX(i, value));
}
}
public IndexedProperty<int, SimdValue> Q
{
get
{
return new IndexedProperty<int, SimdValue>(
(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;
}
}
}
}

View file

@ -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();
}
/// <summary>Rounding Mode control field.</summary>
public enum RMode
{
/// <summary>Round to Nearest mode.</summary>
Rn,
/// <summary>Round towards Plus Infinity mode.</summary>
Rp,
/// <summary>Round towards Minus Infinity mode.</summary>
Rm,
/// <summary>Round towards Zero mode.</summary>
Rz
};
/// <summary>Floating-point Control Register.</summary>
protected enum Fpcr
{
/// <summary>Rounding Mode control field.</summary>
RMode = 22,
/// <summary>Flush-to-zero mode control bit.</summary>
Fz = 24,
/// <summary>Default NaN mode control bit.</summary>
Dn = 25,
/// <summary>Alternative half-precision control bit.</summary>
Ahp = 26
}
/// <summary>Floating-point Status Register.</summary>
[Flags]
protected enum Fpsr
{
None = 0,
/// <summary>Invalid Operation cumulative floating-point exception bit.</summary>
Ioc = 1 << 0,
/// <summary>Divide by Zero cumulative floating-point exception bit.</summary>
Dzc = 1 << 1,
/// <summary>Overflow cumulative floating-point exception bit.</summary>
Ofc = 1 << 2,
/// <summary>Underflow cumulative floating-point exception bit.</summary>
Ufc = 1 << 3,
/// <summary>Inexact cumulative floating-point exception bit.</summary>
Ixc = 1 << 4,
/// <summary>Input Denormal cumulative floating-point exception bit.</summary>
Idc = 1 << 7,
/// <summary>Cumulative saturation bit.</summary>
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;
}
}
}

View file

@ -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 <Rd>, <Rn>")]
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 {<Rd>,} <Rm>, <Rs>")]
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 {<Rd>,} <Rm>, <Rs>")]
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 {<Rd>,} <Rm>, <Rs>")]
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 {<Rd>,} <Rm>, <Rs>")]
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();
}
}
}