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
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.Int32: return 32;
case RegisterSize.Int64: return 64; case RegisterSize.Int64: return 64;
case RegisterSize.Simd32: return 32;
case RegisterSize.Simd64: return 64; case RegisterSize.Simd64: return 64;
case RegisterSize.Simd128: return 128; case RegisterSize.Simd128: return 128;
} }

View file

@ -14,7 +14,7 @@ namespace ARMeilleure.Decoders
public int Lsb { get; private set; } public int Lsb { get; private set; }
public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb)); 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) 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 Ra { get; private set; }
public int Rd { 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 bool SetFlags { get; private set; }
public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
@ -20,6 +22,8 @@ namespace ARMeilleure.Decoders
Ra = (opCode >> 12) & 0xf; Ra = (opCode >> 12) & 0xf;
Rd = (opCode >> 16) & 0xf; Rd = (opCode >> 16) & 0xf;
NHigh = ((opCode >> 5) * 0x1) == 1;
MHigh = ((opCode >> 6) * 0x1) == 1;
SetFlags = ((opCode >> 20) & 1) != 0; 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 Rn { get; private set; }
public int Rm { 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 bool SetFlags { get; private set; }
public DataOp DataOp { get; private set; } public DataOp DataOp { get; private set; }
@ -21,6 +24,9 @@ namespace ARMeilleure.Decoders
Rm = (opCode >> 8) & 0xf; Rm = (opCode >> 8) & 0xf;
Rn = (opCode >> 0) & 0xf; Rn = (opCode >> 0) & 0xf;
NHigh = ((opCode >> 5) * 0x1) == 1;
MHigh = ((opCode >> 6) * 0x1) == 1;
SetFlags = ((opCode >> 20) & 1) != 0; SetFlags = ((opCode >> 20) & 1) != 0;
DataOp = DataOp.Arithmetic; 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)" #region "OpCode Table (AArch32)"
// Base // 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("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluImm));
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsImm)); 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("<<<<0010000xxxxxxxxxxxxxxxxxxxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluImm));
SetA32("<<<<0000000xxxxxxxxxxxxxxxx0xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsImm)); 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("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstName.B, InstEmit32.B, typeof(OpCode32BImm));
SetA32("<<<<0111110xxxxxxxxxxxxxx0011111", InstName.Bfc, InstEmit32.Bfc, typeof(OpCode32AluBf)); SetA32("<<<<0111110xxxxxxxxxxxxxx0011111", InstName.Bfc, InstEmit32.Bfc, typeof(OpCode32AluBf));
SetA32("<<<<0111110xxxxxxxxxxxxxx001xxxx", InstName.Bfi, InstEmit32.Bfi, typeof(OpCode32AluBf)); SetA32("<<<<0111110xxxxxxxxxxxxxx001xxxx", InstName.Bfi, InstEmit32.Bfi, typeof(OpCode32AluBf));
SetA32("<<<<0011110xxxxxxxxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluImm)); SetA32("<<<<0011110xxxxxxxxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluImm));
SetA32("<<<<0001110xxxxxxxxxxxxxxxx0xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsImm)); 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("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, typeof(OpCode32BImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, typeof(OpCode32BImm)); SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, typeof(OpCode32BImm));
SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, typeof(OpCode32BReg)); SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, typeof(OpCode32BReg));
@ -620,12 +623,14 @@ namespace ARMeilleure.Decoders
SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, typeof(OpCode32AluReg)); SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, typeof(OpCode32AluReg));
SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluImm)); SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluImm));
SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsImm)); 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("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluImm));
SetA32("<<<<00010111xxxx0000xxxxxxx0xxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluRsImm)); 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("<<<<0010001xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluImm));
SetA32("<<<<0000001xxxxxxxxxxxxxxxx0xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsImm)); 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("<<<<00011001xxxxxxxx110010011111", InstName.Lda, InstEmit32.Lda, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx)); SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx));
SetA32("<<<<00011001xxxxxxxx111010011111", InstName.Ldaex, InstEmit32.Ldaex, 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("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemImm));
SetA32("<<<<011xx1x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemRsImm)); SetA32("<<<<011xx1x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemRsImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemImm8)); 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("<<<<00011001xxxxxxxx111110011111", InstName.Ldrex, InstEmit32.Ldrex, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx111110011111", InstName.Ldrexb,InstEmit32.Ldrexb,typeof(OpCode32MemLdEx)); SetA32("<<<<00011101xxxxxxxx111110011111", InstName.Ldrexb,InstEmit32.Ldrexb,typeof(OpCode32MemLdEx));
SetA32("<<<<00011011xxxxxxxx111110011111", InstName.Ldrexd,InstEmit32.Ldrexd,typeof(OpCode32MemLdEx)); SetA32("<<<<00011011xxxxxxxx111110011111", InstName.Ldrexd,InstEmit32.Ldrexd,typeof(OpCode32MemLdEx));
SetA32("<<<<00011111xxxxxxxx111110011111", InstName.Ldrexh,InstEmit32.Ldrexh,typeof(OpCode32MemLdEx)); SetA32("<<<<00011111xxxxxxxx111110011111", InstName.Ldrexh,InstEmit32.Ldrexh,typeof(OpCode32MemLdEx));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemImm8)); 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("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstName.Ldrsb, InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemImm8)); SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
SetA32("<<<<1110xxx0xxxxxxxx111xxxx1xxxx", InstName.Mcr, InstEmit32.Mcr, typeof(OpCode32System)); SetA32("<<<<1110xxx0xxxxxxxx111xxxx1xxxx", InstName.Mcr, InstEmit32.Mcr, typeof(OpCode32System));
SetA32("<<<<0000001xxxxxxxxxxxxx1001xxxx", InstName.Mla, InstEmit32.Mla, typeof(OpCode32AluMla)); 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("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsImm)); SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsImm));
SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsReg));
SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm16)); SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm16));
SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCodeT16AluImm8)); SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCodeT16AluImm8));
SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, typeof(OpCode32AluImm16)); SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, typeof(OpCode32AluImm16));
SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, typeof(OpCode32System)); 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("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul, InstEmit32.Mul, typeof(OpCode32AluMla));
SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluImm)); SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluImm));
SetA32("<<<<0001111x0000xxxxxxxxxxx0xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsImm)); 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("<<<<0011100xxxxxxxxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluImm));
SetA32("<<<<0001100xxxxxxxxxxxxxxxx0xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsImm)); 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("<<<<0010011xxxxxxxxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluImm));
SetA32("<<<<0000011xxxxxxxxxxxxxxxx0xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsImm)); 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("<<<<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("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx));
SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx)); SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx)); SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
@ -680,35 +698,137 @@ namespace ARMeilleure.Decoders
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemImm)); SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemImm));
SetA32("<<<<011xx1x0xxxxxxxxxxxxxxx0xxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemRsImm)); SetA32("<<<<011xx1x0xxxxxxxxxxxxxxx0xxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemRsImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemImm8)); 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("<<<<00011000xxxxxxxx11111001xxxx", InstName.Strex, InstEmit32.Strex, typeof(OpCode32MemStEx));
SetA32("<<<<00011100xxxxxxxx11111001xxxx", InstName.Strexb,InstEmit32.Strexb,typeof(OpCode32MemStEx)); SetA32("<<<<00011100xxxxxxxx11111001xxxx", InstName.Strexb,InstEmit32.Strexb,typeof(OpCode32MemStEx));
SetA32("<<<<00011010xxxxxxxx11111001xxxx", InstName.Strexd,InstEmit32.Strexd,typeof(OpCode32MemStEx)); SetA32("<<<<00011010xxxxxxxx11111001xxxx", InstName.Strexd,InstEmit32.Strexd,typeof(OpCode32MemStEx));
SetA32("<<<<00011110xxxxxxxx11111001xxxx", InstName.Strexh,InstEmit32.Strexh,typeof(OpCode32MemStEx)); SetA32("<<<<00011110xxxxxxxx11111001xxxx", InstName.Strexh,InstEmit32.Strexh,typeof(OpCode32MemStEx));
SetA32("<<<<00011110xxxx111111001001xxxx", InstName.Stlh, InstEmit32.Stlh, typeof(OpCode32MemStEx)); SetA32("<<<<00011110xxxx111111001001xxxx", InstName.Stlh, InstEmit32.Stlh, typeof(OpCode32MemStEx));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemImm8)); 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("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm)); 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("<<<<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("<<<<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("<<<<00110001xxxx0000xxxxxxxxxxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluImm));
SetA32("<<<<00010001xxxx0000xxxxxxx0xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm)); 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("<<<<0111111xxxxxxxxxxxxxx101xxxx", InstName.Ubfx, InstEmit32.Ubfx, typeof(OpCode32AluBf));
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla)); SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull)); SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull));
SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx)); 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) // 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("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x11xxxxxxxx1010xxxxxxxx", 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("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial));
SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, 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("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11010x10xxxxxxxx1010xxxxxxxx", 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 #endregion
FillFastLookupTable(_instA32FastLookup, _allInstA32); FillFastLookupTable(_instA32FastLookup, _allInstA32);

View file

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

View file

@ -31,6 +31,30 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res); 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) public static void Clz(ArmEmitterContext context)
{ {
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp; IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
@ -105,6 +129,52 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res); 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) public static void Rsb(ArmEmitterContext context)
{ {
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp; IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@ -216,7 +286,7 @@ namespace ARMeilleure.Instructions
EmitNZFlagsCheck(context, res); EmitNZFlagsCheck(context, res);
} }
public static void Uxtb(ArmEmitterContext context) private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
{ {
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp; IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
@ -233,7 +303,16 @@ namespace ARMeilleure.Instructions
res = context.RotateRight(m, rotate); 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) if (op.Add)
{ {
@ -243,31 +322,24 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res); EmitAluStore(context, res);
} }
public static void Uxtb(ArmEmitterContext context)
{
EmitSignExtend(context, false, 8);
}
public static void Uxth(ArmEmitterContext context) public static void Uxth(ArmEmitterContext context)
{ {
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp; EmitSignExtend(context, false, 16);
Operand m = GetAluM(context);
Operand res;
if (op.RotateBits == 0)
{
res = m;
}
else
{
Operand rotate = Const(op.RotateBits);
res = context.RotateRight(m, rotate);
} }
res = context.ZeroExtend16(OperandType.I32, res); public static void Sxtb(ArmEmitterContext context)
if (op.Add)
{ {
res = context.Add(res, GetAluN(context)); EmitSignExtend(context, true, 8);
} }
EmitAluStore(context, res); public static void Sxth(ArmEmitterContext context)
{
EmitSignExtend(context, true, 16);
} }
public static void Mla(ArmEmitterContext context) public static void Mla(ArmEmitterContext context)
@ -288,6 +360,19 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res); 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) public static void Udiv(ArmEmitterContext context)
{ {
EmitDiv(context, true); EmitDiv(context, true);
@ -393,6 +478,16 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res); 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) public static void Bfc(ArmEmitterContext context)
{ {
@ -413,7 +508,7 @@ namespace ARMeilleure.Instructions
Operand part = context.BitwiseAnd(n, Const(op.SourceMask)); Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
if (op.Lsb != 0) part = context.ShiftLeft(part, Const(op.Lsb)); if (op.Lsb != 0) part = context.ShiftLeft(part, Const(op.Lsb));
Operand res = context.BitwiseAnd(d, Const(~op.DestMask)); 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); SetIntA32(context, op.Rd, res);
} }

View file

@ -119,6 +119,7 @@ namespace ARMeilleure.Instructions
case OpCode32AluImm16 op: return Const(op.Immediate); case OpCode32AluImm16 op: return Const(op.Immediate);
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry); case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
case OpCodeT16AluImm8 op: return Const(op.Immediate); case OpCodeT16AluImm8 op: return Const(op.Immediate);
@ -212,6 +213,70 @@ namespace ARMeilleure.Instructions
return m; 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) public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{ {
if ((uint)shift > 32) 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) public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{ {
if ((uint)shift > 32) if ((uint)shift > 32)
@ -278,6 +359,43 @@ namespace ARMeilleure.Instructions
return Const(0); 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) public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{ {
if ((uint)shift >= 32) 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) public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{ {
shift &= 0x1f; shift &= 0x1f;

View file

@ -16,6 +16,11 @@ namespace ARMeilleure.Instructions
EmitExceptionCall(context, NativeInterface.SupervisorCall); EmitExceptionCall(context, NativeInterface.SupervisorCall);
} }
public static void Trap(ArmEmitterContext context)
{
EmitExceptionCall(context, NativeInterface.Break);
}
private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func) private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func)
{ {
OpCode32Exception op = (OpCode32Exception)context.CurrOp; OpCode32Exception op = (OpCode32Exception)context.CurrOp;

View file

@ -52,6 +52,32 @@ namespace ARMeilleure.Instructions
return Register(regIndex, RegisterType.Vector, OperandType.V128); 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) public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
{ {
if (regIndex == RegisterAlias.Aarch32Pc) if (regIndex == RegisterAlias.Aarch32Pc)

View file

@ -131,6 +131,9 @@ namespace ARMeilleure.Instructions
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered); 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) private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
{ {
IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp; IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp;

View file

@ -514,8 +514,12 @@ namespace ARMeilleure.Instructions
// ARM32. // ARM32.
case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry); case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32MemReg op: return GetIntA32(context, op.Rm);
case OpCode32Mem op: return Const(op.Immediate); case OpCode32Mem op: return Const(op.Immediate);
case OpCode32SimdMemImm op: return Const(op.Immediate);
default: throw InvalidOpCodeType(context.CurrOp); default: throw InvalidOpCodeType(context.CurrOp);
} }
} }

View file

@ -6,11 +6,23 @@ using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitAluHelper; using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using System;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {
static partial class InstEmit32 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) public static void Umull(ArmEmitterContext context)
{ {
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp; OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
@ -32,6 +44,99 @@ namespace ARMeilleure.Instructions
EmitGenericStore(context, op.RdLo, op.SetFlags, lo); 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) private static void EmitGenericStore(ArmEmitterContext context, int Rd, bool setFlags, Operand value)
{ {
if (Rd == RegisterAlias.Aarch32Pc) 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 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) public static void Vldm(ArmEmitterContext context)
{ {
@ -77,5 +206,90 @@ namespace ARMeilleure.Instructions
offset += byteSize; 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}."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
} }
break; 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}."); 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) private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag)); Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
v = context.BitwiseAnd(v, Const(1)); v = context.BitwiseAnd(v, Const(1));
@ -173,5 +187,37 @@ namespace ARMeilleure.Instructions
SetFlag(context, PState.ZFlag, z); SetFlag(context, PState.ZFlag, z);
SetFlag(context, PState.NFlag, n); 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, Sub,
Subs, Subs,
Svc, Svc,
Sxtb,
Sxth,
Sys, Sys,
Tbnz, Tbnz,
Tbz, Tbz,
@ -472,10 +474,16 @@ namespace ARMeilleure.Instructions
Ldrsh, Ldrsh,
Mcr, Mcr,
Mla, Mla,
Mls,
Mov, Mov,
Mrc, Mrc,
Mrrc,
Mvn, Mvn,
Rsb, Rsb,
Rsc,
Smlab,
Smlal,
Smmul,
Stl, Stl,
Stlb, Stlb,
Stlex, Stlex,
@ -492,6 +500,7 @@ namespace ARMeilleure.Instructions
Strexh, Strexh,
Strh, Strh,
Teq, Teq,
Trap,
Tst, Tst,
Ubfx, Ubfx,
Umull, Umull,
@ -499,9 +508,32 @@ namespace ARMeilleure.Instructions
Uxth, Uxth,
// FP & SIMD (AArch32) // FP & SIMD (AArch32)
Vstm, Vadd,
Vcmp,
Vcmpe,
Vcvt,
Vdiv,
Vld1,
Vld2,
Vld3,
Vld4,
Vldm,
Vldr,
Vmla,
Vmls,
Vmrs, Vmrs,
Vmsr, 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); 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 #endregion
#region "Saturation" #region "Saturation"

View file

@ -85,6 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
_svcFuncs32 = new Dictionary<int, string> _svcFuncs32 = new Dictionary<int, string>
{ {
{ 0x06, nameof(SvcHandler.QueryMemory32) }, { 0x06, nameof(SvcHandler.QueryMemory32) },
{ 0x08, nameof(SvcHandler.CreateThread32) },
{ 0x27, nameof(SvcHandler.OutputDebugString32) }, { 0x27, nameof(SvcHandler.OutputDebugString32) },
{ 0x29, nameof(SvcHandler.GetInfo64) } { 0x29, nameof(SvcHandler.GetInfo64) }
}; };
@ -104,6 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Dictionary<int, string> funcTable = aarch32 ? _svcFuncs32 : _svcFuncs64; Dictionary<int, string> funcTable = aarch32 ? _svcFuncs32 : _svcFuncs64;
if (funcTable.TryGetValue(svcId, out string svcName)) if (funcTable.TryGetValue(svcId, out string svcName))
{ {
if (svcId == 0x08) { }
return table[svcId] = GenerateMethod(svcName, aarch32); return table[svcId] = GenerateMethod(svcName, aarch32);
} }
@ -127,10 +129,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
ParameterInfo[] methodArgs = methodInfo.GetParameters(); ParameterInfo[] methodArgs = methodInfo.GetParameters();
int maxArgs = aarch32 ? SvcFuncMaxArguments32 : SvcFuncMaxArguments; 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(); ILGenerator generator = method.GetILGenerator();
@ -209,7 +212,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
generator.Emit(OpCodes.Ldc_I4, index); generator.Emit(OpCodes.Ldc_I4, index);
generator.Emit(OpCodes.Ldarg_1); 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)); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
@ -271,7 +275,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
else else
{ {
generator.Emit(OpCodes.Ldarg_1); 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)); 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); 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( private KernelResult CreateThread(
ulong entrypoint, ulong entrypoint,
ulong argsPtr, 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();
}
}
}