Many more instructions, start on SIMD and testing framework.
This commit is contained in:
parent
7af22d43e5
commit
600a8b5e9e
44 changed files with 3216 additions and 50 deletions
11
ARMeilleure/Decoders/IOpCode32Simd.cs
Normal file
11
ARMeilleure/Decoders/IOpCode32Simd.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32Simd : IOpCode32, IOpCodeSimd
|
||||
{
|
||||
int Elems { get; }
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/IOpCode32SimdImm.cs
Normal file
12
ARMeilleure/Decoders/IOpCode32SimdImm.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ namespace ARMeilleure.Decoders
|
|||
{
|
||||
case RegisterSize.Int32: return 32;
|
||||
case RegisterSize.Int64: return 64;
|
||||
case RegisterSize.Simd32: return 32;
|
||||
case RegisterSize.Simd64: return 64;
|
||||
case RegisterSize.Simd128: return 128;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace ARMeilleure.Decoders
|
|||
public int Lsb { get; private set; }
|
||||
|
||||
public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb));
|
||||
public int DestMask => SourceMask << Lsb;
|
||||
public int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb);
|
||||
|
||||
public OpCode32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace ARMeilleure.Decoders
|
|||
public int Ra { get; private set; }
|
||||
public int Rd { get; private set; }
|
||||
|
||||
public bool NHigh { get; private set; }
|
||||
public bool MHigh { get; private set; }
|
||||
public bool SetFlags { get; private set; }
|
||||
|
||||
public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
|
@ -20,6 +22,8 @@ namespace ARMeilleure.Decoders
|
|||
Ra = (opCode >> 12) & 0xf;
|
||||
Rd = (opCode >> 16) & 0xf;
|
||||
|
||||
NHigh = ((opCode >> 5) * 0x1) == 1;
|
||||
MHigh = ((opCode >> 6) * 0x1) == 1;
|
||||
SetFlags = ((opCode >> 20) & 1) != 0;
|
||||
}
|
||||
}
|
||||
|
|
22
ARMeilleure/Decoders/OpCode32AluRsReg.cs
Normal file
22
ARMeilleure/Decoders/OpCode32AluRsReg.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,9 @@ namespace ARMeilleure.Decoders
|
|||
public int Rn { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public bool NHigh { get; private set; }
|
||||
public bool MHigh { get; private set; }
|
||||
|
||||
public bool SetFlags { get; private set; }
|
||||
public DataOp DataOp { get; private set; }
|
||||
|
||||
|
@ -21,6 +24,9 @@ namespace ARMeilleure.Decoders
|
|||
Rm = (opCode >> 8) & 0xf;
|
||||
Rn = (opCode >> 0) & 0xf;
|
||||
|
||||
NHigh = ((opCode >> 5) * 0x1) == 1;
|
||||
MHigh = ((opCode >> 6) * 0x1) == 1;
|
||||
|
||||
SetFlags = ((opCode >> 20) & 1) != 0;
|
||||
DataOp = DataOp.Arithmetic;
|
||||
}
|
||||
|
|
15
ARMeilleure/Decoders/OpCode32MemReg.cs
Normal file
15
ARMeilleure/Decoders/OpCode32MemReg.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
29
ARMeilleure/Decoders/OpCode32Simd.cs
Normal file
29
ARMeilleure/Decoders/OpCode32Simd.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCode32SimdCvtFI.cs
Normal file
16
ARMeilleure/Decoders/OpCode32SimdCvtFI.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
38
ARMeilleure/Decoders/OpCode32SimdImm.cs
Normal file
38
ARMeilleure/Decoders/OpCode32SimdImm.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
ARMeilleure/Decoders/OpCode32SimdImm44.cs
Normal file
31
ARMeilleure/Decoders/OpCode32SimdImm44.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
40
ARMeilleure/Decoders/OpCode32SimdMemImm.cs
Normal file
40
ARMeilleure/Decoders/OpCode32SimdMemImm.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
ARMeilleure/Decoders/OpCode32SimdMemPair.cs
Normal file
54
ARMeilleure/Decoders/OpCode32SimdMemPair.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
35
ARMeilleure/Decoders/OpCode32SimdMemSingle.cs
Normal file
35
ARMeilleure/Decoders/OpCode32SimdMemSingle.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
30
ARMeilleure/Decoders/OpCode32SimdMovGp.cs
Normal file
30
ARMeilleure/Decoders/OpCode32SimdMovGp.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCode32SimdReg.cs
Normal file
16
ARMeilleure/Decoders/OpCode32SimdReg.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
24
ARMeilleure/Decoders/OpCode32SimdRegS.cs
Normal file
24
ARMeilleure/Decoders/OpCode32SimdRegS.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
ARMeilleure/Decoders/OpCode32SimdS.cs
Normal file
37
ARMeilleure/Decoders/OpCode32SimdS.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
109
ARMeilleure/Decoders/OpCodeSimdHelper.cs
Normal file
109
ARMeilleure/Decoders/OpCodeSimdHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -599,18 +599,21 @@ namespace ARMeilleure.Decoders
|
|||
|
||||
#region "OpCode Table (AArch32)"
|
||||
// Base
|
||||
SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<0000101xxxxxxxxxxxxx0xx1xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0000100xxxxxxxxxxxxx0xx1xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<0010000xxxxxxxxxxxxxxxxxxxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000000xxxxxxxxxxxxxxxx0xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0000000xxxxxxxxxxxxx0xx1xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstName.B, InstEmit32.B, typeof(OpCode32BImm));
|
||||
SetA32("<<<<0111110xxxxxxxxxxxxxx0011111", InstName.Bfc, InstEmit32.Bfc, typeof(OpCode32AluBf));
|
||||
SetA32("<<<<0111110xxxxxxxxxxxxxx001xxxx", InstName.Bfi, InstEmit32.Bfi, typeof(OpCode32AluBf));
|
||||
SetA32("<<<<0011110xxxxxxxxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0001110xxxxxxxxxxxxxxxx0xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0001110xxxxxxxxxxxxx0xx1xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, typeof(OpCode32BImm));
|
||||
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, typeof(OpCode32BImm));
|
||||
SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, typeof(OpCode32BReg));
|
||||
|
@ -620,12 +623,14 @@ namespace ARMeilleure.Decoders
|
|||
SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, typeof(OpCode32AluReg));
|
||||
SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<00010101xxxx0000xxxx0xx1xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<00010111xxxx0000xxxxxxx0xxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluRsImm));
|
||||
SetA32("1111010101111111111100000101xxxx", InstName.Dmb, InstEmit32.Dmb, typeof(OpCode32));
|
||||
SetA32("1111010101111111111100000100xxxx", InstName.Dsb, InstEmit32.Dsb, typeof(OpCode32));
|
||||
SetA32("<<<<0010001xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000001xxxxxxxxxxxxxxxx0xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0000001xxxxxxxxxxxxx0xx1xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<00011001xxxxxxxx110010011111", InstName.Lda, InstEmit32.Lda, typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<00011001xxxxxxxx111010011111", InstName.Ldaex, InstEmit32.Ldaex, typeof(OpCode32MemLdEx));
|
||||
|
@ -639,34 +644,47 @@ namespace ARMeilleure.Decoders
|
|||
SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemImm));
|
||||
SetA32("<<<<011xx1x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemRsImm));
|
||||
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemImm8));
|
||||
//SetA32("<<<<000xx0x0xxxxxxxx00001101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemReg)); //??? wip
|
||||
SetA32("<<<<000xx0x0xxxxxxxx00001101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemReg));
|
||||
SetA32("<<<<00011001xxxxxxxx111110011111", InstName.Ldrex, InstEmit32.Ldrex, typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<00011101xxxxxxxx111110011111", InstName.Ldrexb,InstEmit32.Ldrexb,typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<00011011xxxxxxxx111110011111", InstName.Ldrexd,InstEmit32.Ldrexd,typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<00011111xxxxxxxx111110011111", InstName.Ldrexh,InstEmit32.Ldrexh,typeof(OpCode32MemLdEx));
|
||||
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemImm8));
|
||||
//SetA32("<<<<000xx0x1xxxxxxxx00001011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemReg)); //??? wip
|
||||
SetA32("<<<<000xx0x1xxxxxxxx00001011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemReg));
|
||||
SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstName.Ldrsb, InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
|
||||
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
|
||||
SetA32("<<<<1110xxx0xxxxxxxx111xxxx1xxxx", InstName.Mcr, InstEmit32.Mcr, typeof(OpCode32System));
|
||||
SetA32("<<<<0000001xxxxxxxxxxxxx1001xxxx", InstName.Mla, InstEmit32.Mla, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<00000110xxxxxxxxxxxx1001xxxx", InstName.Mls, InstEmit32.Mls, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm16));
|
||||
SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCodeT16AluImm8));
|
||||
SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, typeof(OpCode32AluImm16));
|
||||
SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, typeof(OpCode32System));
|
||||
SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, typeof(OpCode32System));
|
||||
SetA32("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul, InstEmit32.Mul, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0001111x0000xxxxxxxxxxx0xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0001111x0000xxxxxxxx0xx1xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<0011100xxxxxxxxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0001100xxxxxxxxxxxxxxxx0xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0001100xxxxxxxxxxxxx0xx1xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, typeof(OpCode32AluReg));
|
||||
SetA32("<<<<0010011xxxxxxxxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000011xxxxxxxxxxxxxxxx0xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0000011xxxxxxxxxxxxx0xx1xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<0010111xxxxxxxxxxxxxxxxxxxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000111xxxxxxxxxxxxxxxx0xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<0000111xxxxxxxxxxxxx0xx1xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<01110001xxxx1111xxxx0001xxxx", InstName.Sdiv, InstEmit32.Sdiv, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<00010000xxxxxxxxxxxx1xx0xxxx", InstName.Smlab, InstEmit32.Smlab, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<00010100xxxxxxxxxxxx1xx0xxxx", InstName.Smlal, InstEmit32.Smlal, typeof(OpCode32AluUmull));
|
||||
SetA32("<<<<01110101xxxx1111xxxx00x1xxxx", InstName.Smmul, InstEmit32.Smmul, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<0010110xxxxxxxxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000110xxxxxxxxxxxxxxxx0xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<0000110xxxxxxxxxxxxx0xx1xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg));
|
||||
SetA32("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
|
||||
|
@ -680,35 +698,137 @@ namespace ARMeilleure.Decoders
|
|||
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemImm));
|
||||
SetA32("<<<<011xx1x0xxxxxxxxxxxxxxx0xxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemRsImm));
|
||||
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemImm8));
|
||||
//SetA32("<<<<000xx0x0xxxxxxxx00001111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemReg)); //??? wip
|
||||
SetA32("<<<<000xx0x0xxxxxxxx00001111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemReg));
|
||||
SetA32("<<<<00011000xxxxxxxx11111001xxxx", InstName.Strex, InstEmit32.Strex, typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011100xxxxxxxx11111001xxxx", InstName.Strexb,InstEmit32.Strexb,typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011010xxxxxxxx11111001xxxx", InstName.Strexd,InstEmit32.Strexd,typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011110xxxxxxxx11111001xxxx", InstName.Strexh,InstEmit32.Strexh,typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<00011110xxxx111111001001xxxx", InstName.Stlh, InstEmit32.Stlh, typeof(OpCode32MemStEx));
|
||||
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemImm8));
|
||||
//SetA32("<<<<000xx0x0xxxxxxxx00001011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemReg)); //??? wip
|
||||
SetA32("<<<<000xx0x0xxxxxxxx00001011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemReg));
|
||||
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<0000010xxxxxxxxxxxxx0xx1xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<1111xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Svc, InstEmit32.Svc, typeof(OpCode32Exception));
|
||||
SetA32("<<<<01101010xxxxxxxxxx000111xxxx", InstName.Sxtb, InstEmit32.Sxtb, typeof(OpCode32AluUx));
|
||||
SetA32("<<<<01101011xxxxxxxxxx000111xxxx", InstName.Sxth, InstEmit32.Sxth, typeof(OpCode32AluUx));
|
||||
SetA32("<<<<00110011xxxx0000xxxxxxxxxxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<00010011xxxx0000xxxxxxx0xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<00010011xxxx0000xxxx0xx1xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<0111111111111101111011111110", InstName.Trap, InstEmit32.Trap, typeof(OpCode32Exception));
|
||||
SetA32("<<<<00110001xxxx0000xxxxxxxxxxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluImm));
|
||||
SetA32("<<<<00010001xxxx0000xxxxxxx0xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm));
|
||||
//RsReg missing
|
||||
SetA32("<<<<00010001xxxx0000xxxx0xx1xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm));
|
||||
SetA32("<<<<0111111xxxxxxxxxxxxxx101xxxx", InstName.Ubfx, InstEmit32.Ubfx, typeof(OpCode32AluBf));
|
||||
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
|
||||
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull));
|
||||
SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx));
|
||||
SetA32("<<<<011011111111xxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
|
||||
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
|
||||
|
||||
// FP & SIMD (AArch32)
|
||||
|
||||
SetA32("<<<<11100x11xxxxxxxx10xxx0x0xxxx", InstName.Vadd, InstEmit32.Vadd_S, typeof(OpCode32SimdRegS));
|
||||
SetA32("111100100x0xxxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, typeof(OpCode32SimdReg));
|
||||
SetA32("111100100x0xxxxxxxxx1100xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, typeof(OpCode32SimdReg));
|
||||
SetA32("<<<<11101x11010xxxxx10xx01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, typeof(OpCode32SimdReg));
|
||||
SetA32("<<<<11101x11010xxxxx10xx11x0xxxx", InstName.Vcmpe,InstEmit32.Vcmpe, typeof(OpCode32SimdReg));
|
||||
SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI));
|
||||
SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI));
|
||||
SetA32("<<<<11101x00xxxxxxxx10xxx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, typeof(OpCode32SimdRegS));
|
||||
|
||||
// VLD# missing single to all lanes
|
||||
|
||||
SetA32("111101001x10xxxxxxxx0000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx0100xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx1000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 1
|
||||
SetA32("111101000x10xxxxxxxx1010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 2
|
||||
SetA32("111101000x10xxxxxxxx0110xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 3
|
||||
SetA32("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 4
|
||||
|
||||
SetA32("111101001x10xxxxxxxx0001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx0101xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx1001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x10xxxxxxxx100xxxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype)
|
||||
SetA32("111101000x10xxxxxxxx0011xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2
|
||||
|
||||
SetA32("111101001x10xxxxxxxx0010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx0110xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx1010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x10xxxxxxxx010xxxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
|
||||
|
||||
SetA32("111101001x10xxxxxxxx0011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx0111xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x10xxxxxxxx1011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x10xxxxxxxx000xxxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
|
||||
|
||||
SetA32("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
|
||||
SetA32("<<<<11001x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
|
||||
SetA32("<<<<1101xx01xxxxxxxx10xxxxxxxxxx", InstName.Vldr, InstEmit32.Vldr, typeof(OpCode32SimdMemImm));
|
||||
|
||||
SetA32("<<<<11100x00xxxxxxxx10xxx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, typeof(OpCode32SimdRegS));
|
||||
SetA32("111100100x0xxxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, typeof(OpCode32SimdReg));
|
||||
SetA32("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, typeof(OpCode32SimdReg));
|
||||
|
||||
SetA32("<<<<11100x00xxxxxxxx10xxx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_S, typeof(OpCode32SimdRegS));
|
||||
SetA32("111100100x1xxxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, typeof(OpCode32SimdReg));
|
||||
SetA32("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, typeof(OpCode32SimdReg));
|
||||
|
||||
SetA32("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q vector i32
|
||||
SetA32("<<<<11101x11xxxxxxxx10xx0000xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm44)); //scalar f16/32/64 based on size 01 10 11
|
||||
SetA32("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q i16
|
||||
SetA32("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q (dt - from cmode)
|
||||
SetA32("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); //d/q i64
|
||||
SetA32("<<<<11101x110000xxxx101x01x0xxxx", InstName.Vmov, InstEmit32.Vmov_S, typeof(OpCode32SimdS));
|
||||
//TODO: VORR SIMD
|
||||
//SetA32("<<<<11100xx0xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenScal)); //from gen purpose
|
||||
SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, typeof(OpCode32SimdMovGp)); //to/from gen purpose and single precision
|
||||
//SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenScal)); //to gen purpose
|
||||
//SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov, typeof(OpCode32SimdGenSp)); //to/from gen purpose x2 and single precision x2
|
||||
|
||||
SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial));
|
||||
SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, typeof(OpCode32SimdSpecial));
|
||||
|
||||
SetA32("<<<<11100x10xxxxxxxx10xxx0x0xxxx", InstName.Vmul, InstEmit32.Vmul_S, typeof(OpCode32SimdRegS));
|
||||
SetA32("111100110x0xxxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, typeof(OpCode32SimdReg));
|
||||
SetA32("1111001x0xxxxxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, typeof(OpCode32SimdReg));
|
||||
|
||||
SetA32("111100111x11xx01xxxx0x111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, typeof(OpCode32Simd));
|
||||
SetA32("<<<<11101x110001xxxx10xx01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, typeof(OpCode32SimdS));
|
||||
|
||||
SetA32("111101001x00xxxxxxxx0000xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx0100xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx1000xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 1
|
||||
SetA32("111101000x00xxxxxxxx1010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 2
|
||||
SetA32("111101000x00xxxxxxxx0110xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 3
|
||||
SetA32("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 4
|
||||
|
||||
SetA32("111101001x00xxxxxxxx0001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx0101xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx1001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x00xxxxxxxx100xxxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype)
|
||||
SetA32("111101000x00xxxxxxxx0011xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2
|
||||
|
||||
SetA32("111101001x00xxxxxxxx0010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx0110xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx1010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x00xxxxxxxx010xxxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
|
||||
|
||||
SetA32("111101001x00xxxxxxxx0011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx0111xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101001x00xxxxxxxx1011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
|
||||
SetA32("111101000x00xxxxxxxx000xxxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
|
||||
|
||||
SetA32("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
|
||||
SetA32("<<<<11010x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
|
||||
|
||||
SetA32("<<<<1101xx00xxxxxxxx10xxxxxxxxxx", InstName.Vstr, InstEmit32.Vstr, typeof(OpCode32SimdMemImm));
|
||||
SetA32("<<<<11101x110001xxxx10xx11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, typeof(OpCode32SimdS));
|
||||
|
||||
SetA32("<<<<11100x11xxxxxxxx10xxx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, typeof(OpCode32SimdRegS));
|
||||
SetA32("111100100x1xxxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, typeof(OpCode32SimdReg));
|
||||
SetA32("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, typeof(OpCode32SimdReg));
|
||||
#endregion
|
||||
|
||||
FillFastLookupTable(_instA32FastLookup, _allInstA32);
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace ARMeilleure.Decoders
|
|||
Int32,
|
||||
Int64,
|
||||
Simd64,
|
||||
Simd128
|
||||
Simd128,
|
||||
Simd32
|
||||
}
|
||||
}
|
|
@ -31,6 +31,30 @@ namespace ARMeilleure.Instructions
|
|||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Adc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
Operand carry = GetFlag(PState.CFlag);
|
||||
|
||||
res = context.Add(res, carry);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAdcsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Clz(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
@ -105,6 +129,52 @@ namespace ARMeilleure.Instructions
|
|||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sbc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rsc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(m, n);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, m, n);
|
||||
EmitSubsVCheck(context, m, n, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rsb(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
@ -216,7 +286,7 @@ namespace ARMeilleure.Instructions
|
|||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
public static void Uxtb(ArmEmitterContext context)
|
||||
private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
|
||||
|
@ -226,14 +296,23 @@ namespace ARMeilleure.Instructions
|
|||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
res = context.ZeroExtend8(OperandType.I32, res);
|
||||
switch (bits)
|
||||
{
|
||||
case 8:
|
||||
res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res);
|
||||
break;
|
||||
case 16:
|
||||
res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
|
@ -243,31 +322,24 @@ namespace ARMeilleure.Instructions
|
|||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Uxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, false, 8);
|
||||
}
|
||||
|
||||
public static void Uxth(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
EmitSignExtend(context, false, 16);
|
||||
}
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
Operand res;
|
||||
public static void Sxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 8);
|
||||
}
|
||||
|
||||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
res = context.ZeroExtend16(OperandType.I32, res);
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
res = context.Add(res, GetAluN(context));
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
public static void Sxth(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 16);
|
||||
}
|
||||
|
||||
public static void Mla(ArmEmitterContext context)
|
||||
|
@ -288,6 +360,19 @@ namespace ARMeilleure.Instructions
|
|||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mls(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
|
||||
Operand res = context.Subtract(a, context.Multiply(n, m));
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Udiv(ArmEmitterContext context)
|
||||
{
|
||||
EmitDiv(context, true);
|
||||
|
@ -393,6 +478,16 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
public static void Rbit(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Alu op = (OpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.Call(new _U32_U32(SoftFallback.ReverseBits32), m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Bfc(ArmEmitterContext context)
|
||||
{
|
||||
|
@ -413,7 +508,7 @@ namespace ARMeilleure.Instructions
|
|||
Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
|
||||
if (op.Lsb != 0) part = context.ShiftLeft(part, Const(op.Lsb));
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
res = context.BitwiseOr(res, part);
|
||||
res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask)));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ namespace ARMeilleure.Instructions
|
|||
case OpCode32AluImm16 op: return Const(op.Immediate);
|
||||
|
||||
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
||||
|
||||
case OpCodeT16AluImm8 op: return Const(op.Immediate);
|
||||
|
||||
|
@ -212,6 +213,70 @@ namespace ARMeilleure.Instructions
|
|||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
|
||||
Operand shiftIsZero = context.ICompareEqual(s, Const(0));
|
||||
|
||||
Operand zeroResult = m;
|
||||
Operand shiftResult = m;
|
||||
|
||||
Operand shiftSkip = Label();
|
||||
//Operand shiftZeroSkip = Label();
|
||||
|
||||
setCarry &= op.SetFlags;
|
||||
|
||||
context.BranchIfTrue(shiftSkip, shiftIsZero);
|
||||
|
||||
/*
|
||||
// if zero, fudge the shift number a little
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsr: zeroResult = GetLsrC(context, m, setCarry, 32); break;
|
||||
case ShiftType.Asr: zeroResult = GetAsrC(context, m, setCarry, 32); break;
|
||||
case ShiftType.Ror:
|
||||
// ror 0 is rrx
|
||||
zeroResult = GetRrxC(context, m, setCarry);
|
||||
break;
|
||||
}
|
||||
|
||||
context.Branch(shiftSkip);
|
||||
context.MarkLabel(shiftZeroSkip);
|
||||
*/
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s); break;
|
||||
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s); break;
|
||||
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s); break;
|
||||
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s); break;
|
||||
}
|
||||
|
||||
context.MarkLabel(shiftSkip);
|
||||
|
||||
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
|
||||
}
|
||||
|
||||
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
|
||||
{
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
|
||||
Operand result = context.ShiftLeft(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift > 32)
|
||||
|
@ -242,6 +307,22 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
}
|
||||
|
||||
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
|
||||
{
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
Operand result = context.ShiftRightUI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift > 32)
|
||||
|
@ -278,6 +359,43 @@ namespace ARMeilleure.Instructions
|
|||
return Const(0);
|
||||
}
|
||||
|
||||
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
|
||||
{
|
||||
Operand normalShift = Label();
|
||||
Operand end = Label();
|
||||
|
||||
Operand l32Result;
|
||||
Operand ge32Result;
|
||||
|
||||
Operand less32 = context.ICompareLess(shift, Const(32));
|
||||
|
||||
context.BranchIfTrue(normalShift, less32);
|
||||
|
||||
ge32Result = context.ShiftRightSI(m, Const(31));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, ge32Result);
|
||||
}
|
||||
|
||||
context.Branch(end);
|
||||
context.MarkLabel(normalShift);
|
||||
|
||||
l32Result = context.ShiftRightSI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
context.MarkLabel(end);
|
||||
|
||||
return context.ConditionalSelect(less32, l32Result, ge32Result);
|
||||
}
|
||||
|
||||
public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift >= 32)
|
||||
|
@ -302,6 +420,18 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
}
|
||||
|
||||
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
|
||||
{
|
||||
shift = context.BitwiseAnd(shift, Const(0x1f));
|
||||
m = context.RotateRight(m, shift);
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
shift &= 0x1f;
|
||||
|
|
|
@ -16,6 +16,11 @@ namespace ARMeilleure.Instructions
|
|||
EmitExceptionCall(context, NativeInterface.SupervisorCall);
|
||||
}
|
||||
|
||||
public static void Trap(ArmEmitterContext context)
|
||||
{
|
||||
EmitExceptionCall(context, NativeInterface.Break);
|
||||
}
|
||||
|
||||
private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func)
|
||||
{
|
||||
OpCode32Exception op = (OpCode32Exception)context.CurrOp;
|
||||
|
|
|
@ -52,6 +52,32 @@ namespace ARMeilleure.Instructions
|
|||
return Register(regIndex, RegisterType.Vector, OperandType.V128);
|
||||
}
|
||||
|
||||
public static Operand GetVecA32(ArmEmitterContext context, int regIndex, int registerSizeLog)
|
||||
{
|
||||
// vector registers in A32 all overlap each other - eg. Q0 = D0:D1 = S0:S1:S2:S3
|
||||
// so we need to select the relevant part of a quad vector based on register size.
|
||||
int elemSizeLog = (4 - registerSizeLog);
|
||||
int quadIndex = regIndex >> elemSizeLog;
|
||||
int subIndex = regIndex & ((1 << elemSizeLog) - 1);
|
||||
Operand result = Register(quadIndex, RegisterType.Vector, OperandType.V128);
|
||||
if (subIndex != 0)
|
||||
{
|
||||
result = context.RotateRight(result, Const(subIndex << elemSizeLog));
|
||||
}
|
||||
|
||||
switch (registerSizeLog)
|
||||
{
|
||||
case 4: // quad word
|
||||
return result;
|
||||
case 3: // double word
|
||||
return context.VectorZeroUpper64(result);
|
||||
case 2: // single word
|
||||
return context.VectorZeroUpper96(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
|
||||
{
|
||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
||||
|
|
|
@ -131,6 +131,9 @@ namespace ARMeilleure.Instructions
|
|||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
|
||||
private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
|
||||
{
|
||||
IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp;
|
||||
|
|
|
@ -514,8 +514,12 @@ namespace ARMeilleure.Instructions
|
|||
// ARM32.
|
||||
case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
|
||||
case OpCode32MemReg op: return GetIntA32(context, op.Rm);
|
||||
|
||||
case OpCode32Mem op: return Const(op.Immediate);
|
||||
|
||||
case OpCode32SimdMemImm op: return Const(op.Immediate);
|
||||
|
||||
default: throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,23 @@ using ARMeilleure.Translation;
|
|||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
[Flags]
|
||||
private enum MullFlags
|
||||
{
|
||||
Subtract = 0,
|
||||
Add = 1 << 0,
|
||||
Signed = 1 << 1,
|
||||
|
||||
SignedAdd = Signed | Add,
|
||||
SignedSubtract = Signed | Subtract
|
||||
}
|
||||
|
||||
public static void Umull(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
|
@ -32,6 +44,99 @@ namespace ARMeilleure.Instructions
|
|||
EmitGenericStore(context, op.RdLo, op.SetFlags, lo);
|
||||
}
|
||||
|
||||
public static void Smmul(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
if ((op.RawOpCode & (1 << 5)) != 0)
|
||||
{
|
||||
res = context.Add(res, Const(0x80000000L));
|
||||
}
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
|
||||
|
||||
EmitGenericStore(context, op.Rd, false, hi);
|
||||
}
|
||||
|
||||
|
||||
public static void Smlab(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (op.NHigh)
|
||||
{
|
||||
n = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ZeroExtend16(OperandType.I32, n);
|
||||
}
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.ZeroExtend16(OperandType.I32, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
res = context.Add(res, a);
|
||||
|
||||
//todo: set Q flag when last addition overflows (saturation)?
|
||||
|
||||
EmitGenericStore(context, op.Rd, false, res);
|
||||
}
|
||||
|
||||
public static void Smlal(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (op.NHigh)
|
||||
{
|
||||
n = context.ZeroExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ZeroExtend16(OperandType.I64, n);
|
||||
}
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.ZeroExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.ZeroExtend16(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
|
||||
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
|
||||
res = context.Add(res, toAdd);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
EmitGenericStore(context, op.RdHi, false, hi);
|
||||
EmitGenericStore(context, op.RdLo, false, lo);
|
||||
}
|
||||
|
||||
private static void EmitGenericStore(ArmEmitterContext context, int Rd, bool setFlags, Operand value)
|
||||
{
|
||||
if (Rd == RegisterAlias.Aarch32Pc)
|
||||
|
|
224
ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
Normal file
224
ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
70
ARMeilleure/Instructions/InstEmitSimdCmp32.cs
Normal file
70
ARMeilleure/Instructions/InstEmitSimdCmp32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
137
ARMeilleure/Instructions/InstEmitSimdCvt32.cs
Normal file
137
ARMeilleure/Instructions/InstEmitSimdCvt32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
341
ARMeilleure/Instructions/InstEmitSimdHelper32.cs
Normal file
341
ARMeilleure/Instructions/InstEmitSimdHelper32.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,135 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Vst1(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 1, false);
|
||||
}
|
||||
public static void Vst2(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 2, false);
|
||||
}
|
||||
public static void Vst3(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 3, false);
|
||||
}
|
||||
public static void Vst4(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 4, false);
|
||||
}
|
||||
|
||||
public static void Vld1(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 1, true);
|
||||
}
|
||||
public static void Vld2(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 2, true);
|
||||
}
|
||||
public static void Vld3(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 3, true);
|
||||
}
|
||||
public static void Vld4(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 4, true);
|
||||
}
|
||||
|
||||
public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
|
||||
{
|
||||
if (context.CurrOp is OpCode32SimdMemSingle)
|
||||
{
|
||||
OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
|
||||
|
||||
int eBytes = 1 << op.Size;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
//check alignment (?)
|
||||
int offset = 0;
|
||||
int d = op.Vd;
|
||||
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
//write an element from a double simd register
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
int index = ((d & 1) << (3 - op.Size)) + op.Index;
|
||||
if (load)
|
||||
{
|
||||
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, d >> 1, index, op.Size);
|
||||
}
|
||||
//TODO: big endian at size == 4
|
||||
offset += eBytes;
|
||||
d += op.Increment;
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
if (op.RegisterIndex)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
SetIntA32(context, op.Rn, context.Add(n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
|
||||
|
||||
int eBytes = 1 << op.Size;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
int offset = 0;
|
||||
int d = op.Vd;
|
||||
|
||||
for (int reg = 0; reg < op.Regs; reg++)
|
||||
{
|
||||
for (int elem = 0; elem < op.Elems; elem++)
|
||||
{
|
||||
int elemD = d + reg;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// write an element from a double simd register
|
||||
// add ebytes for each element
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
int index = ((d & 1) << (3 - op.Size)) + elem;
|
||||
if (load)
|
||||
{
|
||||
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, d >> 1, index, op.Size);
|
||||
}
|
||||
|
||||
offset += eBytes;
|
||||
elemD += op.Increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
if (op.RegisterIndex)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
SetIntA32(context, op.Rn, context.Add(n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vldm(ArmEmitterContext context)
|
||||
{
|
||||
|
@ -77,5 +206,90 @@ namespace ARMeilleure.Instructions
|
|||
offset += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vldr(ArmEmitterContext context)
|
||||
{
|
||||
EmitVLoadOrStore(context, AccessType.Load);
|
||||
}
|
||||
|
||||
public static void Vstr(ArmEmitterContext context)
|
||||
{
|
||||
EmitVLoadOrStore(context, AccessType.Store);
|
||||
}
|
||||
|
||||
private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
Operand m = GetMemM(context, setCarry: false);
|
||||
|
||||
Operand address = op.Add
|
||||
? context.Add(n, m)
|
||||
: context.Subtract(n, m);
|
||||
|
||||
int size = op.Size;
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
int vecQ = op.Vd >> 1;
|
||||
int vecSElem = (op.Vd & 1) << 1;
|
||||
Operand vec = GetVecA32(vecQ);
|
||||
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
|
||||
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand vec = GetVecA32(op.Vd >> 2);
|
||||
EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2-size), size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
int vecQ = op.Vd >> 1;
|
||||
int vecSElem = (op.Vd & 1) << 1;
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
|
||||
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
40
ARMeilleure/Instructions/InstEmitSimdMove32.cs
Normal file
40
ARMeilleure/Instructions/InstEmitSimdMove32.cs
Normal 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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -146,6 +146,22 @@ namespace ARMeilleure.Instructions
|
|||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
switch (op.CRm) // Cache and Memory barrier
|
||||
{
|
||||
case 10:
|
||||
switch (op.Opc2)
|
||||
{
|
||||
case 5: // Data Memory Barrier Register
|
||||
return; // SUPER TODO: DO NOT KNOW HOW TO IMPLEMENT THIS
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
|
@ -154,8 +170,6 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
|
||||
v = context.BitwiseAnd(v, Const(1));
|
||||
|
||||
|
@ -173,5 +187,37 @@ namespace ARMeilleure.Instructions
|
|||
SetFlag(context, PState.ZFlag, z);
|
||||
SetFlag(context, PState.NFlag, n);
|
||||
}
|
||||
|
||||
public static void Mrrc(ArmEmitterContext context)
|
||||
{
|
||||
var op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
var opc = (op.RawOpCode >> 4) & 0xf;
|
||||
|
||||
Delegate dlg;
|
||||
switch (op.CRm)
|
||||
{
|
||||
case 14: // Timer
|
||||
switch (opc)
|
||||
{
|
||||
case 0:
|
||||
dlg = new _U64(NativeInterface.GetCntpctEl0); break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
break;
|
||||
default: throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
Operand result = context.Call(dlg);
|
||||
|
||||
SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
|
||||
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ namespace ARMeilleure.Instructions
|
|||
Sub,
|
||||
Subs,
|
||||
Svc,
|
||||
Sxtb,
|
||||
Sxth,
|
||||
Sys,
|
||||
Tbnz,
|
||||
Tbz,
|
||||
|
@ -472,10 +474,16 @@ namespace ARMeilleure.Instructions
|
|||
Ldrsh,
|
||||
Mcr,
|
||||
Mla,
|
||||
Mls,
|
||||
Mov,
|
||||
Mrc,
|
||||
Mrrc,
|
||||
Mvn,
|
||||
Rsb,
|
||||
Rsc,
|
||||
Smlab,
|
||||
Smlal,
|
||||
Smmul,
|
||||
Stl,
|
||||
Stlb,
|
||||
Stlex,
|
||||
|
@ -492,6 +500,7 @@ namespace ARMeilleure.Instructions
|
|||
Strexh,
|
||||
Strh,
|
||||
Teq,
|
||||
Trap,
|
||||
Tst,
|
||||
Ubfx,
|
||||
Umull,
|
||||
|
@ -499,9 +508,32 @@ namespace ARMeilleure.Instructions
|
|||
Uxth,
|
||||
|
||||
// FP & SIMD (AArch32)
|
||||
Vstm,
|
||||
Vadd,
|
||||
Vcmp,
|
||||
Vcmpe,
|
||||
Vcvt,
|
||||
Vdiv,
|
||||
Vld1,
|
||||
Vld2,
|
||||
Vld3,
|
||||
Vld4,
|
||||
Vldm,
|
||||
Vldr,
|
||||
Vmla,
|
||||
Vmls,
|
||||
Vmrs,
|
||||
Vmsr,
|
||||
Vldm
|
||||
Vmul,
|
||||
Vneg,
|
||||
Vst1,
|
||||
Vst2,
|
||||
Vst3,
|
||||
Vst4,
|
||||
Vstm,
|
||||
Vstr,
|
||||
Vsqrt,
|
||||
Vsub,
|
||||
|
||||
Vmov
|
||||
}
|
||||
}
|
||||
|
|
|
@ -420,6 +420,26 @@ namespace ARMeilleure.Instructions
|
|||
return MathF.Truncate(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int FloatToInt32(float value)
|
||||
{
|
||||
return (int)RoundF(value);
|
||||
}
|
||||
|
||||
public static int DoubleToInt32(double value)
|
||||
{
|
||||
return (int)Round(value);
|
||||
}
|
||||
|
||||
public static uint FloatToUInt32(float value)
|
||||
{
|
||||
return (uint)RoundF(value);
|
||||
}
|
||||
|
||||
public static uint DoubleToUInt32(double value)
|
||||
{
|
||||
return (uint)Round(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
|
|
|
@ -85,6 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
_svcFuncs32 = new Dictionary<int, string>
|
||||
{
|
||||
{ 0x06, nameof(SvcHandler.QueryMemory32) },
|
||||
{ 0x08, nameof(SvcHandler.CreateThread32) },
|
||||
{ 0x27, nameof(SvcHandler.OutputDebugString32) },
|
||||
{ 0x29, nameof(SvcHandler.GetInfo64) }
|
||||
};
|
||||
|
@ -104,6 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
Dictionary<int, string> funcTable = aarch32 ? _svcFuncs32 : _svcFuncs64;
|
||||
if (funcTable.TryGetValue(svcId, out string svcName))
|
||||
{
|
||||
if (svcId == 0x08) { }
|
||||
return table[svcId] = GenerateMethod(svcName, aarch32);
|
||||
}
|
||||
|
||||
|
@ -127,10 +129,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
ParameterInfo[] methodArgs = methodInfo.GetParameters();
|
||||
|
||||
int maxArgs = aarch32 ? SvcFuncMaxArguments32 : SvcFuncMaxArguments;
|
||||
int numArgs = methodArgs.Count(x => !x.IsOut);
|
||||
|
||||
if (methodArgs.Count(x => !x.IsOut) > maxArgs)
|
||||
if (numArgs > maxArgs)
|
||||
{
|
||||
throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {maxArgs}.");
|
||||
//throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {maxArgs}.");
|
||||
}
|
||||
|
||||
ILGenerator generator = method.GetILGenerator();
|
||||
|
@ -209,7 +212,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
generator.Emit(OpCodes.Ldc_I4, index);
|
||||
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index);
|
||||
int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index;
|
||||
generator.Emit(OpCodes.Ldc_I4, argIndex);
|
||||
|
||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
|
||||
|
||||
|
@ -271,7 +275,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
else
|
||||
{
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index);
|
||||
int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index;
|
||||
generator.Emit(OpCodes.Ldc_I4, argIndex);
|
||||
|
||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
|
||||
|
||||
|
|
|
@ -19,6 +19,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle);
|
||||
}
|
||||
|
||||
public KernelResult CreateThread32(
|
||||
ulong entrypoint,
|
||||
ulong argsPtr,
|
||||
ulong stackTop,
|
||||
int cpuCore,
|
||||
int priority,
|
||||
|
||||
out int handle)
|
||||
{
|
||||
return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle);
|
||||
}
|
||||
|
||||
private KernelResult CreateThread(
|
||||
ulong entrypoint,
|
||||
ulong argsPtr,
|
||||
|
|
139
Ryujinx.Tests.Unicorn/Native/Arm32Register.cs
Normal file
139
Ryujinx.Tests.Unicorn/Native/Arm32Register.cs
Normal 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,
|
||||
}
|
||||
}
|
276
Ryujinx.Tests.Unicorn/UnicornAArch32.cs
Normal file
276
Ryujinx.Tests.Unicorn/UnicornAArch32.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
498
Ryujinx.Tests/Cpu/CpuTest32.cs
Normal file
498
Ryujinx.Tests/Cpu/CpuTest32.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
93
Ryujinx.Tests/Cpu/CpuTestAlu32.cs
Normal file
93
Ryujinx.Tests/Cpu/CpuTestAlu32.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue