Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
2492e7e808 | |||
36172ab43b | |||
4d69286a9c | |||
1529e6cf0d | |||
f468db7602 | |||
c5f1d1749a | |||
7dd69f2d0e | |||
c646638680 | |||
65f2a82b97 | |||
93dd6d525a | |||
96d4ad952c | |||
6a07f80b76 | |||
22214ac664 | |||
45e520a27c | |||
5b5810a46a | |||
619ac86bd0 | |||
7a1ab71c73 | |||
dc4ba3993b | |||
81f1a4dc31 | |||
c64524a240 | |||
db45688aa8 |
@ -251,6 +251,13 @@ namespace ARMeilleure.Decoders
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare and branch instructions are always conditional.
|
||||
if (opCode.Instruction.Name == InstName.Cbz ||
|
||||
opCode.Instruction.Name == InstName.Cbnz)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: On ARM32, most instructions have conditional execution,
|
||||
// so there's no "Always" (unconditional) branch like on ARM64.
|
||||
// We need to check if the condition is "Always" instead.
|
||||
|
@ -151,7 +151,7 @@ namespace ARMeilleure.Decoders
|
||||
|
||||
public static bool VectorArgumentsInvalid(bool q, params int[] args)
|
||||
{
|
||||
if (q)
|
||||
if (q)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
|
@ -7,5 +7,8 @@
|
||||
|
||||
int Msb { get; }
|
||||
int Lsb { get; }
|
||||
|
||||
int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb));
|
||||
int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb);
|
||||
}
|
||||
}
|
||||
|
7
ARMeilleure/Decoders/IOpCode32AluImm16.cs
Normal file
7
ARMeilleure/Decoders/IOpCode32AluImm16.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32AluImm16 : IOpCode32Alu
|
||||
{
|
||||
int Immediate { get; }
|
||||
}
|
||||
}
|
11
ARMeilleure/Decoders/IOpCode32AluMla.cs
Normal file
11
ARMeilleure/Decoders/IOpCode32AluMla.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32AluMla : IOpCode32AluReg
|
||||
{
|
||||
int Ra { get; }
|
||||
|
||||
bool NHigh { get; }
|
||||
bool MHigh { get; }
|
||||
bool R { get; }
|
||||
}
|
||||
}
|
13
ARMeilleure/Decoders/IOpCode32AluUmull.cs
Normal file
13
ARMeilleure/Decoders/IOpCode32AluUmull.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32AluUmull : IOpCode32, IOpCode32HasSetFlags
|
||||
{
|
||||
int RdLo { get; }
|
||||
int RdHi { get; }
|
||||
int Rn { get; }
|
||||
int Rm { get; }
|
||||
|
||||
bool NHigh { get; }
|
||||
bool MHigh { get; }
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ namespace ARMeilleure.Decoders
|
||||
interface IOpCode32Mem : IOpCode32
|
||||
{
|
||||
int Rt { get; }
|
||||
int Rt2 => Rt | 1;
|
||||
int Rn { get; }
|
||||
|
||||
bool WBack { get; }
|
||||
|
@ -13,16 +13,13 @@ namespace ARMeilleure.Decoders
|
||||
Cond = (Condition)((uint)opCode >> 28);
|
||||
}
|
||||
|
||||
public bool IsThumb()
|
||||
{
|
||||
return this is OpCodeT16 || this is OpCodeT32;
|
||||
}
|
||||
public bool IsThumb { get; protected init; } = false;
|
||||
|
||||
public uint GetPc()
|
||||
{
|
||||
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
||||
// the PC actually points 2 instructions ahead.
|
||||
if (IsThumb())
|
||||
if (IsThumb)
|
||||
{
|
||||
// PC is ahead by 4 in thumb mode whether or not the current instruction
|
||||
// is 16 or 32 bit.
|
||||
|
@ -6,12 +6,8 @@
|
||||
public int Rn { get; }
|
||||
|
||||
public int Msb { get; }
|
||||
|
||||
public int Lsb { get; }
|
||||
|
||||
public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb));
|
||||
public int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb);
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluBf(inst, address, opCode);
|
||||
|
||||
public OpCode32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluImm16 : OpCode32Alu
|
||||
class OpCode32AluImm16 : OpCode32Alu, IOpCode32AluImm16
|
||||
{
|
||||
public int Immediate { get; }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluMla : OpCode32, IOpCode32AluReg
|
||||
class OpCode32AluMla : OpCode32, IOpCode32AluMla
|
||||
{
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluUmull : OpCode32, IOpCode32HasSetFlags
|
||||
class OpCode32AluUmull : OpCode32, IOpCode32AluUmull
|
||||
{
|
||||
public int RdLo { get; }
|
||||
public int RdHi { get; }
|
||||
@ -11,7 +11,6 @@
|
||||
public bool MHigh { get; }
|
||||
|
||||
public bool? SetFlags { get; }
|
||||
public DataOp DataOp { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluUmull(inst, address, opCode);
|
||||
|
||||
@ -26,7 +25,6 @@
|
||||
MHigh = ((opCode >> 6) & 0x1) == 1;
|
||||
|
||||
SetFlags = ((opCode >> 20) & 0x1) != 0;
|
||||
DataOp = DataOp.Arithmetic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
{
|
||||
class OpCode32SimdCvtFI : OpCode32SimdS
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFI(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFI(inst, address, opCode, false);
|
||||
public new static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFI(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
||||
{
|
||||
Opc = (opCode >> 7) & 0x1;
|
||||
|
||||
|
@ -7,10 +7,13 @@
|
||||
public int Rt { get; }
|
||||
public bool Q { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdDupGP(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdDupGP(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdDupGP(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdDupGP(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdDupGP(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Size = 2 - (((opCode >> 21) & 0x2) | ((opCode >> 5) & 0x1)); // B:E - 0 for 32, 16 then 8.
|
||||
if (Size == -1)
|
||||
{
|
||||
|
@ -7,10 +7,13 @@
|
||||
public int Size { get; }
|
||||
public int Elems { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdImm44(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdImm44(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdImm44(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdImm44(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdImm44(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Size = (opCode >> 8) & 0x3;
|
||||
|
||||
bool single = Size != 3;
|
||||
|
@ -8,10 +8,13 @@
|
||||
public bool Add { get; }
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemImm(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemImm(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemImm(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdMemImm(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Immediate = opCode & 0xff;
|
||||
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
@ -12,10 +12,13 @@
|
||||
public bool DoubleWidth { get; }
|
||||
public bool Add { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemMult(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemMult(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemMult(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdMemMult(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdMemMult(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
bool isLoad = (opCode & (1 << 20)) != 0;
|
||||
|
@ -11,10 +11,13 @@
|
||||
public int Opc1 { get; }
|
||||
public int Opc2 { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGp(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGp(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGp(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdMovGp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdMovGp(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
// Which one is used is instruction dependant.
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
|
||||
|
@ -9,10 +9,13 @@
|
||||
public int Rt2 { get; }
|
||||
public int Op { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpDouble(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpDouble(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpDouble(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdMovGpDouble(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdMovGpDouble(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
// Which one is used is instruction dependant.
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
|
||||
|
@ -11,10 +11,13 @@
|
||||
|
||||
public int Index { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpElem(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpElem(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpElem(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdMovGpElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdMovGpElem(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
U = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
|
12
ARMeilleure/Decoders/OpCode32SimdMovn.cs
Normal file
12
ARMeilleure/Decoders/OpCode32SimdMovn.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMovn : OpCode32Simd
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovn(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMovn(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = (opCode >> 18) & 0x3;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,9 +4,10 @@
|
||||
{
|
||||
public int Vn { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegS(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegS(inst, address, opCode, false);
|
||||
public new static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegS(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdRegS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdRegS(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
||||
{
|
||||
bool single = Size != 3;
|
||||
if (single)
|
||||
|
@ -8,10 +8,13 @@
|
||||
public int Opc2 { get; } // opc2 or RM (opc2<1:0>) [Vcvt, Vrint].
|
||||
public int Size { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdS(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdS(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdS(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Opc = (opCode >> 15) & 0x3;
|
||||
Opc2 = (opCode >> 16) & 0x7;
|
||||
|
||||
|
@ -4,9 +4,10 @@
|
||||
{
|
||||
public OpCode32SimdSelMode Cc { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSel(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSel(inst, address, opCode, false);
|
||||
public new static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSel(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdSel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdSel(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode, isThumb)
|
||||
{
|
||||
Cc = (OpCode32SimdSelMode)((opCode >> 20) & 3);
|
||||
}
|
||||
|
@ -5,10 +5,13 @@
|
||||
public int Rt { get; }
|
||||
public int Sreg { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSpecial(inst, address, opCode);
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSpecial(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSpecial(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdSpecial(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
public OpCode32SimdSpecial(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Sreg = (opCode >> 16) & 0xf;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ namespace ARMeilleure.Decoders
|
||||
{
|
||||
Cond = Condition.Al;
|
||||
|
||||
IsThumb = true;
|
||||
OpCodeSizeInBytes = 2;
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,13 @@ namespace ARMeilleure.Decoders
|
||||
{
|
||||
public int Rd { get; }
|
||||
|
||||
public bool Add => true;
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16Adr(inst, address, opCode);
|
||||
|
||||
public OpCodeT16Adr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 8) & 7;
|
||||
Rd = (opCode >> 8) & 7;
|
||||
|
||||
int imm = (opCode & 0xff) << 2;
|
||||
Immediate = (int)(GetPc() & 0xfffffffc) + imm;
|
||||
|
@ -8,6 +8,7 @@
|
||||
{
|
||||
Cond = Condition.Al;
|
||||
|
||||
IsThumb = true;
|
||||
OpCodeSizeInBytes = 4;
|
||||
}
|
||||
}
|
||||
|
22
ARMeilleure/Decoders/OpCodeT32AluBf.cs
Normal file
22
ARMeilleure/Decoders/OpCodeT32AluBf.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluBf : OpCodeT32, IOpCode32AluBf
|
||||
{
|
||||
public int Rd { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public int Msb { get; }
|
||||
public int Lsb { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluBf(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 8) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
Msb = (opCode >> 0) & 0x1f;
|
||||
Lsb = ((opCode >> 6) & 0x3) | ((opCode >> 10) & 0x1c);
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCodeT32AluImm12.cs
Normal file
16
ARMeilleure/Decoders/OpCodeT32AluImm12.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluImm12 : OpCodeT32Alu, IOpCode32AluImm
|
||||
{
|
||||
public int Immediate { get; }
|
||||
|
||||
public bool IsRotated => false;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluImm12(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluImm12(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Immediate = (opCode & 0xff) | ((opCode >> 4) & 0x700) | ((opCode >> 15) & 0x800);
|
||||
}
|
||||
}
|
||||
}
|
29
ARMeilleure/Decoders/OpCodeT32AluMla.cs
Normal file
29
ARMeilleure/Decoders/OpCodeT32AluMla.cs
Normal file
@ -0,0 +1,29 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluMla : OpCodeT32, IOpCode32AluMla
|
||||
{
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
public int Ra { get; }
|
||||
public int Rd { get; }
|
||||
|
||||
public bool NHigh { get; }
|
||||
public bool MHigh { get; }
|
||||
public bool R { get; }
|
||||
public bool? SetFlags => false;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluMla(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
Rd = (opCode >> 8) & 0xf;
|
||||
Ra = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
R = (opCode & (1 << 4)) != 0;
|
||||
|
||||
MHigh = ((opCode >> 4) & 0x1) == 1;
|
||||
NHigh = ((opCode >> 5) & 0x1) == 1;
|
||||
}
|
||||
}
|
||||
}
|
14
ARMeilleure/Decoders/OpCodeT32AluReg.cs
Normal file
14
ARMeilleure/Decoders/OpCodeT32AluReg.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluReg : OpCodeT32Alu, IOpCode32AluReg
|
||||
{
|
||||
public int Rm { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluReg(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
28
ARMeilleure/Decoders/OpCodeT32AluUmull.cs
Normal file
28
ARMeilleure/Decoders/OpCodeT32AluUmull.cs
Normal file
@ -0,0 +1,28 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluUmull : OpCodeT32, IOpCode32AluUmull
|
||||
{
|
||||
public int RdLo { get; }
|
||||
public int RdHi { get; }
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
|
||||
public bool NHigh { get; }
|
||||
public bool MHigh { get; }
|
||||
|
||||
public bool? SetFlags => false;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluUmull(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluUmull(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
RdHi = (opCode >> 8) & 0xf;
|
||||
RdLo = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
MHigh = ((opCode >> 4) & 0x1) == 1;
|
||||
NHigh = ((opCode >> 5) & 0x1) == 1;
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCodeT32AluUx.cs
Normal file
18
ARMeilleure/Decoders/OpCodeT32AluUx.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using ARMeilleure.State;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32AluUx : OpCodeT32AluReg, IOpCode32AluUx
|
||||
{
|
||||
public int Rotate { get; }
|
||||
public int RotateBits => Rotate * 8;
|
||||
public bool Add => Rn != RegisterAlias.Aarch32Pc;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluUx(inst, address, opCode);
|
||||
|
||||
public OpCodeT32AluUx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rotate = (opCode >> 4) & 0x3;
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ namespace ARMeilleure.Decoders
|
||||
int i2 = j2 ^ s ^ 1;
|
||||
|
||||
int imm32 = imm11 | (imm10 << 11) | (i2 << 21) | (i1 << 22) | (s << 23);
|
||||
imm32 = (imm32 << 9) >> 8;
|
||||
imm32 = (imm32 << 8) >> 7;
|
||||
|
||||
Immediate = pc + imm32;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ namespace ARMeilleure.Decoders
|
||||
Add = ((opCode >> 23) & 1) != 0;
|
||||
WBack = ((opCode >> 21) & 1) != 0;
|
||||
|
||||
Immediate = opCode & 0xff;
|
||||
Immediate = (opCode & 0xff) << 2;
|
||||
|
||||
IsLoad = ((opCode >> 20) & 1) != 0;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace ARMeilleure.Decoders
|
||||
{
|
||||
public int Rd => 0;
|
||||
public int Rt { get; }
|
||||
public int Rt2 { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public bool WBack => false;
|
||||
@ -17,6 +18,7 @@ namespace ARMeilleure.Decoders
|
||||
|
||||
public OpCodeT32MemLdEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt2 = (opCode >> 8) & 0xf;
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace ARMeilleure.Decoders
|
||||
{
|
||||
public int Rd { get; }
|
||||
public int Rt { get; }
|
||||
public int Rt2 { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public bool WBack => false;
|
||||
@ -18,6 +19,7 @@ namespace ARMeilleure.Decoders
|
||||
public OpCodeT32MemStEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 0) & 0xf;
|
||||
Rt2 = (opCode >> 8) & 0xf;
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
}
|
||||
|
16
ARMeilleure/Decoders/OpCodeT32MovImm16.cs
Normal file
16
ARMeilleure/Decoders/OpCodeT32MovImm16.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32MovImm16 : OpCodeT32Alu, IOpCode32AluImm16
|
||||
{
|
||||
public int Immediate { get; }
|
||||
|
||||
public bool IsRotated => false;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MovImm16(inst, address, opCode);
|
||||
|
||||
public OpCodeT32MovImm16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Immediate = (opCode & 0xff) | ((opCode >> 4) & 0x700) | ((opCode >> 15) & 0x800) | ((opCode >> 4) & 0xf000);
|
||||
}
|
||||
}
|
||||
}
|
19
ARMeilleure/Decoders/OpCodeT32ShiftReg.cs
Normal file
19
ARMeilleure/Decoders/OpCodeT32ShiftReg.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32ShiftReg : OpCodeT32Alu, IOpCode32AluRsReg
|
||||
{
|
||||
public int Rm => Rn;
|
||||
public int Rs { get; }
|
||||
|
||||
public ShiftType ShiftType { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32ShiftReg(inst, address, opCode);
|
||||
|
||||
public OpCodeT32ShiftReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rs = (opCode >> 0) & 0xf;
|
||||
|
||||
ShiftType = (ShiftType)((opCode >> 21) & 3);
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCodeT32Tb.cs
Normal file
16
ARMeilleure/Decoders/OpCodeT32Tb.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32Tb : OpCodeT32, IOpCode32BReg
|
||||
{
|
||||
public int Rm { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32Tb(inst, address, opCode);
|
||||
|
||||
public OpCodeT32Tb(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
@ -792,7 +792,61 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("<<<<01101100xxxxxxxxxx000111xxxx", InstName.Uxtb16, InstEmit32.Uxtb16, OpCode32AluUx.Create);
|
||||
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, OpCode32AluUx.Create);
|
||||
|
||||
// FP & SIMD
|
||||
// VFP
|
||||
SetVfp("<<<<11101x110000xxxx101x11x0xxxx", InstName.Vabs, InstEmit32.Vabs_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11100x11xxxxxxxx101xx0x0xxxx", InstName.Vadd, InstEmit32.Vadd_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101x11010xxxxx101x01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101x11010xxxxx101x11x0xxxx", InstName.Vcmpe, InstEmit32.Vcmpe, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, OpCode32SimdS.Create, OpCode32SimdS.CreateT32); // FP 32 and 64, scalar.
|
||||
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
|
||||
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
|
||||
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
|
||||
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
|
||||
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101x10xxxxxxxx101xx1x0xxxx", InstName.Vfms, InstEmit32.Vfms_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101x01xxxxxxxx101xx1x0xxxx", InstName.Vfnma, InstEmit32.Vfnma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101x01xxxxxxxx101xx0x0xxxx", InstName.Vfnms, InstEmit32.Vfnms_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11001x01xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11010x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x01xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11010x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<1101xx01xxxxxxxx101xxxxxxxxx", InstName.Vldr, InstEmit32.Vldr, OpCode32SimdMemImm.Create, OpCode32SimdMemImm.CreateT32);
|
||||
SetVfp("111111101x00xxxxxxxx10>>x0x0xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("111111101x00xxxxxxxx10>>x1x0xxxx", InstName.Vminnm, InstEmit32.Vminnm_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11100x00xxxxxxxx101xx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11100x00xxxxxxxx101xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11100xx0xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, OpCode32SimdMovGpElem.Create, OpCode32SimdMovGpElem.CreateT32); // From gen purpose.
|
||||
SetVfp("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, OpCode32SimdMovGpElem.Create, OpCode32SimdMovGpElem.CreateT32); // To gen purpose.
|
||||
SetVfp("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov_G2, OpCode32SimdMovGpDouble.Create, OpCode32SimdMovGpDouble.CreateT32); // To/from gen purpose x2 and single precision x2.
|
||||
SetVfp("<<<<1100010xxxxxxxxx101100x1xxxx", InstName.Vmov, InstEmit32.Vmov_GD, OpCode32SimdMovGpDouble.Create, OpCode32SimdMovGpDouble.CreateT32); // To/from gen purpose x2 and double precision.
|
||||
SetVfp("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, OpCode32SimdMovGp.Create, OpCode32SimdMovGp.CreateT32); // To/from gen purpose and single precision.
|
||||
SetVfp("<<<<11101x11xxxxxxxx101x0000xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm44.Create, OpCode32SimdImm44.CreateT32); // Scalar f16/32/64 based on size 01 10 11.
|
||||
SetVfp("<<<<11101x110000xxxx101x01x0xxxx", InstName.Vmov, InstEmit32.Vmov_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, OpCode32SimdSpecial.Create, OpCode32SimdSpecial.CreateT32);
|
||||
SetVfp("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, OpCode32SimdSpecial.Create, OpCode32SimdSpecial.CreateT32);
|
||||
SetVfp("<<<<11100x10xxxxxxxx101xx0x0xxxx", InstName.Vmul, InstEmit32.Vmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101x110001xxxx101x01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11100x01xxxxxxxx101xx1x0xxxx", InstName.Vnmla, InstEmit32.Vnmla_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11100x01xxxxxxxx101xx0x0xxxx", InstName.Vnmls, InstEmit32.Vnmls_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
||||
SetVfp("<<<<11001x00xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x00xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11001x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<11010x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create, OpCode32SimdMemMult.CreateT32);
|
||||
SetVfp("<<<<1101xx00xxxxxxxx101xxxxxxxxx", InstName.Vstr, InstEmit32.Vstr, OpCode32SimdMemImm.Create, OpCode32SimdMemImm.CreateT32);
|
||||
SetVfp("<<<<11100x11xxxxxxxx101xx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
|
||||
// ASIMD
|
||||
SetA32("111100111x110000xxx0001101x0xxx0", InstName.Aesd_V, InstEmit32.Aesd_V, OpCode32Simd.Create);
|
||||
SetA32("111100111x110000xxx0001100x0xxx0", InstName.Aese_V, InstEmit32.Aese_V, OpCode32Simd.Create);
|
||||
SetA32("111100111x110000xxx0001111x0xxx0", InstName.Aesimc_V, InstEmit32.Aesimc_V, OpCode32Simd.Create);
|
||||
@ -803,14 +857,12 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("111100110x10xxx0xxx01100x1x0xxx0", InstName.Sha256su1_V, InstEmit32.Sha256su1_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0111xxx0xxxx", InstName.Vabd, InstEmit32.Vabd_I, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx0111x0x0xxxx", InstName.Vabdl, InstEmit32.Vabdl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("<<<<11101x110000xxxx101x11x0xxxx", InstName.Vabs, InstEmit32.Vabs_S, OpCode32SimdS.Create);
|
||||
SetA32("111100111x11<<01xxxx00110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x111001xxxx01110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100100xxxxxxxxxxx1000xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11100x11xxxxxxxx101xx0x0xxxx", InstName.Vadd, InstEmit32.Vadd_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x00xxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx0000x0x0xxxx", InstName.Vaddl, InstEmit32.Vaddl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx0001x0x0xxxx", InstName.Vaddw, InstEmit32.Vaddw_I, OpCode32SimdRegWide.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx00000x0x0xxxx", InstName.Vaddl, InstEmit32.Vaddl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx00001x0x0xxxx", InstName.Vaddw, InstEmit32.Vaddw_I, OpCode32SimdRegWide.Create);
|
||||
SetA32("111100100x00xxxxxxxx0001xxx1xxxx", InstName.Vand, InstEmit32.Vand_I, OpCode32SimdBinary.Create);
|
||||
SetA32("111100100x01xxxxxxxx0001xxx1xxxx", InstName.Vbic, InstEmit32.Vbic_I, OpCode32SimdBinary.Create);
|
||||
SetA32("1111001x1x000xxxxxxx<<x10x11xxxx", InstName.Vbic, InstEmit32.Vbic_II, OpCode32SimdImm.Create);
|
||||
@ -828,25 +880,13 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("111100111x11xx01xxxx0x000xx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_Z, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x11xx01xxxx0x011xx0xxxx", InstName.Vcle, InstEmit32.Vcle_Z, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, OpCode32SimdCmpZ.Create);
|
||||
SetA32("<<<<11101x11010xxxxx101x01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, OpCode32SimdS.Create);
|
||||
SetA32("<<<<11101x11010xxxxx101x11x0xxxx", InstName.Vcmpe, InstEmit32.Vcmpe, OpCode32SimdS.Create);
|
||||
SetA32("111100111x110000xxxx01010xx0xxxx", InstName.Vcnt, InstEmit32.Vcnt, OpCode32SimdCmpZ.Create);
|
||||
SetA32("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, OpCode32SimdS.Create); // FP 32 and 64, scalar.
|
||||
SetA32("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create); // FP32 to int.
|
||||
SetA32("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create); // Int to FP32.
|
||||
SetA32("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create); // The many FP32 to int encodings (fp).
|
||||
SetA32("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, OpCode32SimdCmpZ.Create); // FP and integer, vector.
|
||||
SetA32("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create);
|
||||
SetA32("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create);
|
||||
SetA32("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, OpCode32SimdDupElem.Create);
|
||||
SetA32("111100110x00xxxxxxxx0001xxx1xxxx", InstName.Veor, InstEmit32.Veor_I, OpCode32SimdBinary.Create);
|
||||
SetA32("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, OpCode32SimdExt.Create);
|
||||
SetA32("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x00xxxxxxxx1100xxx1xxxx", InstName.Vfma, InstEmit32.Vfma_V, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11101x10xxxxxxxx101xx1x0xxxx", InstName.Vfms, InstEmit32.Vfms_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x10xxxxxxxx1100xxx1xxxx", InstName.Vfms, InstEmit32.Vfms_V, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11101x01xxxxxxxx101xx1x0xxxx", InstName.Vfnma, InstEmit32.Vfnma_S, OpCode32SimdRegS.Create);
|
||||
SetA32("<<<<11101x01xxxxxxxx101xx0x0xxxx", InstName.Vfnms, InstEmit32.Vfnms_S, OpCode32SimdRegS.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0000xxx0xxxx", InstName.Vhadd, InstEmit32.Vhadd, OpCode32SimdReg.Create);
|
||||
SetA32("111101001x10xxxxxxxxxx00xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemSingle.Create);
|
||||
SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, OpCode32SimdMemPair.Create); // Regs = 1.
|
||||
@ -860,51 +900,31 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("111101000x10xxxxxxxx010xxxxxxxxx", InstName.Vld3, InstEmit32.Vld3, OpCode32SimdMemPair.Create); // Inc = 1/2 (itype).
|
||||
SetA32("111101001x10xxxxxxxxxx11xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemSingle.Create);
|
||||
SetA32("111101000x10xxxxxxxx000xxxxxxxxx", InstName.Vld4, InstEmit32.Vld4, OpCode32SimdMemPair.Create); // Inc = 1/2 (itype).
|
||||
SetA32("<<<<11001x01xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11010x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x01xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11010x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<1101xx01xxxxxxxx101xxxxxxxxx", InstName.Vldr, InstEmit32.Vldr, OpCode32SimdMemImm.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0110xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100100x00xxxxxxxx1111xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0110xxx1xxxx", InstName.Vmin, InstEmit32.Vmin_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100100x10xxxxxxxx1111xxx0xxxx", InstName.Vmin, InstEmit32.Vmin_V, OpCode32SimdReg.Create);
|
||||
SetA32("111111101x00xxxxxxxx10>>x0x0xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100110x0xxxxxxxxx1111xxx1xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_V, OpCode32SimdReg.Create);
|
||||
SetA32("111111101x00xxxxxxxx10>>x1x0xxxx", InstName.Vminnm, InstEmit32.Vminnm_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100110x1xxxxxxxxx1111xxx1xxxx", InstName.Vminnm, InstEmit32.Vminnm_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx000xx1x0xxxx", InstName.Vmla, InstEmit32.Vmla_1, OpCode32SimdRegElem.Create);
|
||||
SetA32("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11100x00xxxxxxxx101xx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x00xxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx01000x0x0xxxx", InstName.Vmlal, InstEmit32.Vmlal_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx010xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_1, OpCode32SimdRegElem.Create);
|
||||
SetA32("<<<<11100x00xxxxxxxx101xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x10xxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, OpCode32SimdReg.Create);
|
||||
SetA32("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx01010x0x0xxxx", InstName.Vmlsl, InstEmit32.Vmlsl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("<<<<11100xx0xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, OpCode32SimdMovGpElem.Create); // From gen purpose.
|
||||
SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, OpCode32SimdMovGpElem.Create); // To gen purpose.
|
||||
SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov_G2, OpCode32SimdMovGpDouble.Create); // To/from gen purpose x2 and single precision x2.
|
||||
SetA32("<<<<1100010xxxxxxxxx101100x1xxxx", InstName.Vmov, InstEmit32.Vmov_GD, OpCode32SimdMovGpDouble.Create); // To/from gen purpose x2 and double precision.
|
||||
SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, OpCode32SimdMovGp.Create); // To/from gen purpose and single precision.
|
||||
SetA32("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create); // D/Q vector I32.
|
||||
SetA32("<<<<11101x11xxxxxxxx101x0000xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm44.Create); // Scalar f16/32/64 based on size 01 10 11.
|
||||
SetA32("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create); // D/Q I16.
|
||||
SetA32("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create); // D/Q (dt - from cmode).
|
||||
SetA32("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, OpCode32SimdImm.Create); // D/Q I64.
|
||||
SetA32("<<<<11101x110000xxxx101x01x0xxxx", InstName.Vmov, InstEmit32.Vmov_S, OpCode32SimdS.Create);
|
||||
SetA32("1111001x1x001000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create);
|
||||
SetA32("1111001x1x010000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create);
|
||||
SetA32("1111001x1x100000xxx0101000x1xxxx", InstName.Vmovl, InstEmit32.Vmovl, OpCode32SimdLong.Create);
|
||||
SetA32("111100111x11xx10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, OpCode32SimdCmpZ.Create);
|
||||
SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, OpCode32SimdSpecial.Create);
|
||||
SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, OpCode32SimdSpecial.Create);
|
||||
SetA32("111100111x11<<10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, OpCode32SimdMovn.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx100xx1x0xxxx", InstName.Vmul, InstEmit32.Vmul_1, OpCode32SimdRegElem.Create);
|
||||
SetA32("111100100x<<xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100110x00xxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11100x10xxxxxxxx101xx0x0xxxx", InstName.Vmul, InstEmit32.Vmul_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100110x00xxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx01010x1x0xxxx", InstName.Vmull, InstEmit32.Vmull_1, OpCode32SimdRegElemLong.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx01100x0x0xxxx", InstName.Vmull, InstEmit32.Vmull_I, OpCode32SimdRegLong.Create);
|
||||
@ -913,40 +933,46 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("1111001x1x000xxxxxxx0xx00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create); // D/Q vector I32.
|
||||
SetA32("1111001x1x000xxxxxxx10x00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create);
|
||||
SetA32("1111001x1x000xxxxxxx110x0x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_II, OpCode32SimdImm.Create);
|
||||
SetA32("<<<<11101x110001xxxx101x01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, OpCode32SimdS.Create);
|
||||
SetA32("111100111x11<<01xxxx00111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x111001xxxx01111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("<<<<11100x01xxxxxxxx101xx1x0xxxx", InstName.Vnmla, InstEmit32.Vnmla_S, OpCode32SimdRegS.Create);
|
||||
SetA32("<<<<11100x01xxxxxxxx101xx0x0xxxx", InstName.Vnmls, InstEmit32.Vnmls_S, OpCode32SimdRegS.Create);
|
||||
SetA32("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x11xxxxxxxx0001xxx1xxxx", InstName.Vorn, InstEmit32.Vorn_I, OpCode32SimdBinary.Create);
|
||||
SetA32("111100100x10xxxxxxxx0001xxx1xxxx", InstName.Vorr, InstEmit32.Vorr_I, OpCode32SimdBinary.Create);
|
||||
SetA32("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create);
|
||||
SetA32("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx1010x0x1xxxx", InstName.Vpmin, InstEmit32.Vpmin_I, OpCode32SimdReg.Create);
|
||||
SetA32("111100110x10xxxxxxxx1111x0x0xxxx", InstName.Vpmin, InstEmit32.Vpmin_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x0xxxxxxxxxxx0000xxx1xxxx", InstName.Vqadd, InstEmit32.Vqadd, OpCode32SimdReg.Create);
|
||||
SetA32("111100100x01xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create);
|
||||
SetA32("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create);
|
||||
SetA32("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("111100111x>>>xxxxxxx100000x1xxx0", InstName.Vqshrun, InstEmit32.Vqshrun, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("1111001x0xxxxxxxxxxx0010xxx1xxxx", InstName.Vqsub, InstEmit32.Vqsub, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x111011xxxx010x0xx0xxxx", InstName.Vrecpe, InstEmit32.Vrecpe, OpCode32SimdSqrte.Create);
|
||||
SetA32("111100100x00xxxxxxxx1111xxx1xxxx", InstName.Vrecps, InstEmit32.Vrecps, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x11xx00xxxx000<<xx0xxxx", InstName.Vrev, InstEmit32.Vrev, OpCode32SimdRev.Create);
|
||||
SetA32("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create);
|
||||
SetA32("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create);
|
||||
SetA32("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0001xxx0xxxx", InstName.Vrhadd, InstEmit32.Vrhadd, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x111010xxxx01010xx0xxxx", InstName.Vrinta, InstEmit32.Vrinta_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x111010xxxx01101xx0xxxx", InstName.Vrintm, InstEmit32.Vrintm_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x111010xxxx01000xx0xxxx", InstName.Vrintn, InstEmit32.Vrintn_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100111x111010xxxx01111xx0xxxx", InstName.Vrintp, InstEmit32.Vrintp_V, OpCode32SimdCmpZ.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx0010>xx1xxxx", InstName.Vrshr, InstEmit32.Vrshr, OpCode32SimdShImm.Create);
|
||||
SetA32("111100101x>>>xxxxxxx100001x1xxx0", InstName.Vrshrn, InstEmit32.Vrshrn, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("111100111x111011xxxx010x1xx0xxxx", InstName.Vrsqrte, InstEmit32.Vrsqrte, OpCode32SimdSqrte.Create);
|
||||
SetA32("111100100x10xxxxxxxx1111xxx1xxxx", InstName.Vrsqrts, InstEmit32.Vrsqrts, OpCode32SimdReg.Create);
|
||||
SetA32("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx0011>xx1xxxx", InstName.Vrsra, InstEmit32.Vrsra, OpCode32SimdShImm.Create);
|
||||
SetA32("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create);
|
||||
SetA32("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create); // A1 encoding.
|
||||
SetA32("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create);
|
||||
SetA32("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create);
|
||||
SetA32("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create);
|
||||
SetA32("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create);
|
||||
SetA32("111101001x00xxxxxxxx<<00xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create);
|
||||
SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemPair.Create); // Regs = 1.
|
||||
@ -960,17 +986,10 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("111101000x00xxxxxxxx010xxxxxxxxx", InstName.Vst3, InstEmit32.Vst3, OpCode32SimdMemPair.Create); // Inc = 1/2 (itype).
|
||||
SetA32("111101001x00xxxxxxxx<<11xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemSingle.Create);
|
||||
SetA32("111101000x00xxxxxxxx000xxxxxxxxx", InstName.Vst4, InstEmit32.Vst4, OpCode32SimdMemPair.Create); // Inc = 1/2 (itype).
|
||||
SetA32("<<<<11001x00xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x00xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11001x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<11010x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, OpCode32SimdMemMult.Create);
|
||||
SetA32("<<<<1101xx00xxxxxxxx101xxxxxxxxx", InstName.Vstr, InstEmit32.Vstr, OpCode32SimdMemImm.Create);
|
||||
SetA32("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, OpCode32SimdReg.Create);
|
||||
SetA32("<<<<11100x11xxxxxxxx101xx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, OpCode32SimdRegS.Create);
|
||||
SetA32("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx0011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create);
|
||||
SetA32("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create);
|
||||
SetA32("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create);
|
||||
SetA32("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create);
|
||||
@ -1008,7 +1027,7 @@ namespace ARMeilleure.Decoders
|
||||
SetT16("01000101xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT16AluRegHigh.Create);
|
||||
SetT16("01000110xxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16AluRegHigh.Create);
|
||||
SetT16("010001110xxxx000", InstName.Bx, InstEmit32.Bx, OpCodeT16BReg.Create);
|
||||
SetT16("010001111xxxx000", InstName.Blx, InstEmit32.Blx, OpCodeT16BReg.Create);
|
||||
SetT16("010001111xxxx000", InstName.Blx, InstEmit32.Blxr, OpCodeT16BReg.Create);
|
||||
SetT16("01001xxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT16MemLit.Create);
|
||||
SetT16("0101000xxxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT16MemReg.Create);
|
||||
SetT16("0101001xxxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT16MemReg.Create);
|
||||
@ -1056,49 +1075,71 @@ namespace ARMeilleure.Decoders
|
||||
SetT32("11110x01010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluImm.Create);
|
||||
SetT32("11101011000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x01000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluImm.Create);
|
||||
SetT32("11110x100000xxxx0xxxxxxxxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluImm12.Create);
|
||||
SetT32("11101010000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x00000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluImm.Create);
|
||||
SetT32("11110x<<<xxxxxxx10x0xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm20.Create);
|
||||
SetT32("11110xxxxxxxxxxx10x1xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm24.Create);
|
||||
SetT32("11110011011011110xxxxxxxxx0xxxxx", InstName.Bfc, InstEmit32.Bfc, OpCodeT32AluBf.Create);
|
||||
SetT32("111100110110<<<<0xxxxxxxxx0xxxxx", InstName.Bfi, InstEmit32.Bfi, OpCodeT32AluBf.Create);
|
||||
SetT32("11101010001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x00001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluImm.Create);
|
||||
SetT32("11110xxxxxxxxxxx11x1xxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, OpCodeT32BImm24.Create);
|
||||
SetT32("11110xxxxxxxxxxx11x0xxxxxxxxxxx0", InstName.Blx, InstEmit32.Blx, OpCodeT32BImm24.Create);
|
||||
SetT32("111110101011xxxx1111xxxx1000xxxx", InstName.Clz, InstEmit32.Clz, OpCodeT32AluReg.Create);
|
||||
SetT32("111010110001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x010001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluImm.Create);
|
||||
SetT32("111010111011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x011011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluImm.Create);
|
||||
SetT32("11101010100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x00100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluImm.Create);
|
||||
SetT32("111010001101xxxxxxxx111110101111", InstName.Lda, InstEmit32.Lda, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxx111110001111", InstName.Ldab, InstEmit32.Ldab, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxx111111101111", InstName.Ldaex, InstEmit32.Ldaex, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxx111111001111", InstName.Ldaexb, InstEmit32.Ldaexb, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxxxxxx11111111", InstName.Ldaexd, InstEmit32.Ldaexd, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxx111111011111", InstName.Ldaexh, InstEmit32.Ldaexh, OpCodeT32MemLdEx.Create);
|
||||
SetT32("111010001101xxxxxxxx111110011111", InstName.Ldah, InstEmit32.Ldah, OpCodeT32MemLdEx.Create);
|
||||
SetT32("1110100010x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldm, InstEmit32.Ldm, OpCodeT32MemMult.Create);
|
||||
SetT32("1110100100x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldm, InstEmit32.Ldm, OpCodeT32MemMult.Create);
|
||||
SetT32("111110000101xxxx<<<<10x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000101xxxx<<<<1100xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000101xxxx<<<<11x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000101xxxxxxxx10x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000101xxxxxxxx1100xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000101xxxxxxxx11x1xxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110001101xxxxxxxxxxxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemImm12.Create);
|
||||
SetT32("111110000101<<<<xxxx000000xxxxxx", InstName.Ldr, InstEmit32.Ldr, OpCodeT32MemRsImm.Create);
|
||||
SetT32("111110000001xxxx<<<<10x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxx<<<<1100xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxx<<<<11x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxxxxxx10x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxxxxxx1100xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxxxxxx11x1xxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110001001xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemImm12.Create);
|
||||
SetT32("1110100>x1>1<<<<xxxxxxxxxxxxxxxx", InstName.Ldrd, InstEmit32.Ldrd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("111110000011xxxx<<<<10x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000011xxxx<<<<1100xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000011xxxx<<<<11x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000001xxxx<<<<000000xxxxxx", InstName.Ldrb, InstEmit32.Ldrb, OpCodeT32MemRsImm.Create);
|
||||
SetT32("11101000x111<<<<xxxxxxxxxxxxxxxx", InstName.Ldrd, InstEmit32.Ldrd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("11101001x1x1<<<<xxxxxxxxxxxxxxxx", InstName.Ldrd, InstEmit32.Ldrd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("111110000011xxxxxxxx10x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000011xxxxxxxx1100xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000011xxxxxxxx11x1xxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110001011xxxxxxxxxxxxxxxxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemImm12.Create);
|
||||
SetT32("111110010001xxxx<<<<10x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010001xxxx<<<<1100xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010001xxxx<<<<11x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110000011xxxx<<<<000000xxxxxx", InstName.Ldrh, InstEmit32.Ldrh, OpCodeT32MemRsImm.Create);
|
||||
SetT32("111110010001xxxxxxxx10x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010001xxxxxxxx1100xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010001xxxxxxxx11x1xxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110011001xxxxxxxxxxxxxxxxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemImm12.Create);
|
||||
SetT32("111110010011xxxx<<<<10x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010011xxxx<<<<1100xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010011xxxx<<<<11x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010001xxxx<<<<000000xxxxxx", InstName.Ldrsb, InstEmit32.Ldrsb, OpCodeT32MemRsImm.Create);
|
||||
SetT32("111110010011xxxxxxxx10x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010011xxxxxxxx1100xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110010011xxxxxxxx11x1xxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110011011xxxxxxxxxxxxxxxxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemImm12.Create);
|
||||
SetT32("111110010011xxxx<<<<000000xxxxxx", InstName.Ldrsh, InstEmit32.Ldrsh, OpCodeT32MemRsImm.Create);
|
||||
SetT32("111110110000xxxx<<<<xxxx0000xxxx", InstName.Mla, InstEmit32.Mla, OpCodeT32AluMla.Create);
|
||||
SetT32("111110110000xxxxxxxxxxxx0001xxxx", InstName.Mls, InstEmit32.Mls, OpCodeT32AluMla.Create);
|
||||
SetT32("11101010010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluRsImm.Create);
|
||||
SetT32("111110100xxxxxxx1111xxxx0000xxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32ShiftReg.Create);
|
||||
SetT32("11110x00010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluImm.Create);
|
||||
SetT32("11110x100100xxxx0xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32MovImm16.Create);
|
||||
SetT32("11110x101100xxxx0xxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, OpCodeT32MovImm16.Create);
|
||||
SetT32("111110110000xxxx1111xxxx0000xxxx", InstName.Mul, InstEmit32.Mul, OpCodeT32AluMla.Create);
|
||||
SetT32("11101010011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x00011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluImm.Create);
|
||||
SetT32("11110011101011111000000000000000", InstName.Nop, InstEmit32.Nop, OpCodeT32.Create);
|
||||
SetT32("11101010011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x00011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluImm.Create);
|
||||
SetT32("11101010010x<<<<0xxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT32AluRsImm.Create);
|
||||
@ -1107,7 +1148,24 @@ namespace ARMeilleure.Decoders
|
||||
SetT32("11110x01110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluImm.Create);
|
||||
SetT32("11101011011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x01011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluImm.Create);
|
||||
SetT32("111100110100xxxx0xxxxxxxxx0xxxxx", InstName.Sbfx, InstEmit32.Sbfx, OpCodeT32AluBf.Create);
|
||||
SetT32("111110111001xxxx1111xxxx1111xxxx", InstName.Sdiv, InstEmit32.Sdiv, OpCodeT32AluMla.Create);
|
||||
SetT32("111110110001xxxx<<<<xxxx00xxxxxx", InstName.Smla__, InstEmit32.Smla__, OpCodeT32AluMla.Create);
|
||||
SetT32("111110111100xxxxxxxxxxxx0000xxxx", InstName.Smlal, InstEmit32.Smlal, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110111100xxxxxxxxxxxx10xxxxxx", InstName.Smlal__, InstEmit32.Smlal__, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110110011xxxx<<<<xxxx000xxxxx", InstName.Smlaw_, InstEmit32.Smlaw_, OpCodeT32AluMla.Create);
|
||||
SetT32("111110110101xxxx<<<<xxxx000xxxxx", InstName.Smmla, InstEmit32.Smmla, OpCodeT32AluMla.Create);
|
||||
SetT32("111110110110xxxxxxxxxxxx000xxxxx", InstName.Smmls, InstEmit32.Smmls, OpCodeT32AluMla.Create);
|
||||
SetT32("111110110001xxxx1111xxxx00xxxxxx", InstName.Smul__, InstEmit32.Smul__, OpCodeT32AluMla.Create);
|
||||
SetT32("111110111000xxxxxxxxxxxx0000xxxx", InstName.Smull, InstEmit32.Smull, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110110011xxxx1111xxxx000xxxxx", InstName.Smulw_, InstEmit32.Smulw_, OpCodeT32AluMla.Create);
|
||||
SetT32("111010001100xxxxxxxx111110101111", InstName.Stl, InstEmit32.Stl, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxx111110001111", InstName.Stlb, InstEmit32.Stlb, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxx11111110xxxx", InstName.Stlex, InstEmit32.Stlex, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxx11111100xxxx", InstName.Stlexb, InstEmit32.Stlexb, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxxxxxx1111xxxx", InstName.Stlexd, InstEmit32.Stlexd, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxx11111101xxxx", InstName.Stlexh, InstEmit32.Stlexh, OpCodeT32MemStEx.Create);
|
||||
SetT32("111010001100xxxxxxxx111110011111", InstName.Stlh, InstEmit32.Stlh, OpCodeT32MemStEx.Create);
|
||||
SetT32("1110100010x0xxxx0xxxxxxxxxxxxxxx", InstName.Stm, InstEmit32.Stm, OpCodeT32MemMult.Create);
|
||||
SetT32("1110100100x0xxxx0xxxxxxxxxxxxxxx", InstName.Stm, InstEmit32.Stm, OpCodeT32MemMult.Create);
|
||||
SetT32("111110000100xxxxxxxx1<<>xxxxxxxx", InstName.Str, InstEmit32.Str, OpCodeT32MemImm8.Create);
|
||||
@ -1115,15 +1173,31 @@ namespace ARMeilleure.Decoders
|
||||
SetT32("111110000100<<<<xxxx000000xxxxxx", InstName.Str, InstEmit32.Str, OpCodeT32MemRsImm.Create);
|
||||
SetT32("111110000000xxxxxxxx1<<>xxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110001000xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT32MemImm12.Create);
|
||||
SetT32("1110100>x1>0<<<<xxxxxxxxxxxxxxxx", InstName.Strd, InstEmit32.Strd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("111110000000<<<<xxxx000000xxxxxx", InstName.Strb, InstEmit32.Strb, OpCodeT32MemRsImm.Create);
|
||||
SetT32("11101000x110<<<<xxxxxxxxxxxxxxxx", InstName.Strd, InstEmit32.Strd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("11101001x1x0<<<<xxxxxxxxxxxxxxxx", InstName.Strd, InstEmit32.Strd, OpCodeT32MemImm8D.Create);
|
||||
SetT32("111110000010xxxxxxxx1<<>xxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT32MemImm8.Create);
|
||||
SetT32("111110001010xxxxxxxxxxxxxxxxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT32MemImm12.Create);
|
||||
SetT32("111110000010<<<<xxxx000000xxxxxx", InstName.Strh, InstEmit32.Strh, OpCodeT32MemRsImm.Create);
|
||||
SetT32("11101011101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x01101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluImm.Create);
|
||||
SetT32("111110100100xxxx1111xxxx10xxxxxx", InstName.Sxtb, InstEmit32.Sxtb, OpCodeT32AluUx.Create);
|
||||
SetT32("111110100010xxxx1111xxxx10xxxxxx", InstName.Sxtb16, InstEmit32.Sxtb16, OpCodeT32AluUx.Create);
|
||||
SetT32("111110100000xxxx1111xxxx10xxxxxx", InstName.Sxth, InstEmit32.Sxth, OpCodeT32AluUx.Create);
|
||||
SetT32("111010001101xxxx111100000000xxxx", InstName.Tbb, InstEmit32.Tbb, OpCodeT32Tb.Create);
|
||||
SetT32("111010001101xxxx111100000001xxxx", InstName.Tbh, InstEmit32.Tbh, OpCodeT32Tb.Create);
|
||||
SetT32("111010101001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x001001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluImm.Create);
|
||||
SetT32("111010100001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluRsImm.Create);
|
||||
SetT32("11110x000001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluImm.Create);
|
||||
SetT32("111100111100xxxx0xxxxxxxxx0xxxxx", InstName.Ubfx, InstEmit32.Ubfx, OpCodeT32AluBf.Create);
|
||||
SetT32("111110111011xxxx1111xxxx1111xxxx", InstName.Udiv, InstEmit32.Udiv, OpCodeT32AluMla.Create);
|
||||
SetT32("111110111110xxxxxxxxxxxx0110xxxx", InstName.Umaal, InstEmit32.Umaal, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110111110xxxxxxxxxxxx0000xxxx", InstName.Umlal, InstEmit32.Umlal, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110111010xxxxxxxxxxxx0000xxxx", InstName.Umull, InstEmit32.Umull, OpCodeT32AluUmull.Create);
|
||||
SetT32("111110100101xxxx1111xxxx10xxxxxx", InstName.Uxtb, InstEmit32.Uxtb, OpCodeT32AluUx.Create);
|
||||
SetT32("111110100011xxxx1111xxxx10xxxxxx", InstName.Uxtb16, InstEmit32.Uxtb16, OpCodeT32AluUx.Create);
|
||||
SetT32("111110100001xxxx1111xxxx10xxxxxx", InstName.Uxth, InstEmit32.Uxth, OpCodeT32AluUx.Create);
|
||||
#endregion
|
||||
|
||||
FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA);
|
||||
@ -1180,6 +1254,18 @@ namespace ARMeilleure.Decoders
|
||||
Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp);
|
||||
}
|
||||
|
||||
private static void SetVfp(string encoding, InstName name, InstEmitter emitter, MakeOp makeOpA32, MakeOp makeOpT32)
|
||||
{
|
||||
SetA32(encoding, name, emitter, makeOpA32);
|
||||
|
||||
string thumbEncoding = encoding;
|
||||
if (thumbEncoding.StartsWith("<<<<"))
|
||||
{
|
||||
thumbEncoding = "1110" + thumbEncoding.Substring(4);
|
||||
}
|
||||
SetT32(thumbEncoding, name, emitter, makeOpT32);
|
||||
}
|
||||
|
||||
private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
|
||||
{
|
||||
Set(encoding, AllInstA64, new InstDescriptor(name, emitter), makeOp);
|
||||
|
@ -74,7 +74,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Bfc(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
@ -84,7 +84,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Bfi(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
@ -185,7 +185,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Movt(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluImm16 op = (OpCode32AluImm16)context.CurrOp;
|
||||
IOpCode32AluImm16 op = (IOpCode32AluImm16)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand imm = Const(op.Immediate << 16); // Immeditate value as top halfword.
|
||||
@ -389,7 +389,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Sbfx(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
@ -484,7 +484,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Ubfx(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
|
@ -128,7 +128,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32);
|
||||
|
||||
if (((OpCode32)context.CurrOp).IsThumb())
|
||||
if (((OpCode32)context.CurrOp).IsThumb)
|
||||
{
|
||||
bool isReturn = IsA32Return(context);
|
||||
if (!isReturn)
|
||||
@ -205,7 +205,7 @@ namespace ARMeilleure.Instructions
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
|
||||
case OpCode32AluImm16 op: return Const(op.Immediate);
|
||||
case IOpCode32AluImm16 op: return Const(op.Immediate);
|
||||
|
||||
case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
||||
|
@ -34,7 +34,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb;
|
||||
|
||||
uint currentPc = isThumb
|
||||
? pc | 1
|
||||
@ -61,7 +61,7 @@ namespace ARMeilleure.Instructions
|
||||
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
||||
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb;
|
||||
|
||||
uint currentPc = isThumb
|
||||
? (pc - 2) | 1
|
||||
@ -107,5 +107,30 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.SetIfThenBlockState(op.IfThenBlockConds);
|
||||
}
|
||||
|
||||
public static void Tbb(ArmEmitterContext context) => EmitTb(context, halfword: false);
|
||||
public static void Tbh(ArmEmitterContext context) => EmitTb(context, halfword: true);
|
||||
|
||||
private static void EmitTb(ArmEmitterContext context, bool halfword)
|
||||
{
|
||||
OpCodeT32Tb op = (OpCodeT32Tb)context.CurrOp;
|
||||
|
||||
Operand halfwords;
|
||||
|
||||
if (halfword)
|
||||
{
|
||||
Operand address = context.Add(GetIntA32(context, op.Rn), context.ShiftLeft(GetIntA32(context, op.Rm), Const(1)));
|
||||
halfwords = InstEmitMemoryHelper.EmitReadInt(context, address, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand address = context.Add(GetIntA32(context, op.Rn), GetIntA32(context, op.Rm));
|
||||
halfwords = InstEmitMemoryHelper.EmitReadIntAligned(context, address, 0);
|
||||
}
|
||||
|
||||
Operand targetAddress = context.Add(Const((int)op.GetPc()), context.ShiftLeft(halfwords, Const(1)));
|
||||
|
||||
EmitVirtualJump(context, targetAddress, isReturn: false);
|
||||
}
|
||||
}
|
||||
}
|
@ -204,15 +204,15 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Load(op.Rt, 0, WordSizeLog2);
|
||||
Load(op.Rt | 1, 4, WordSizeLog2);
|
||||
Load(op.Rt, 0, WordSizeLog2);
|
||||
Load(op.Rt2, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Load(op.Rt | 1, 0, WordSizeLog2);
|
||||
Load(op.Rt, 4, WordSizeLog2);
|
||||
Load(op.Rt2, 0, WordSizeLog2);
|
||||
Load(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
@ -237,15 +237,15 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Store(op.Rt, 0, WordSizeLog2);
|
||||
Store(op.Rt | 1, 4, WordSizeLog2);
|
||||
Store(op.Rt, 0, WordSizeLog2);
|
||||
Store(op.Rt2, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Store(op.Rt | 1, 0, WordSizeLog2);
|
||||
Store(op.Rt, 4, WordSizeLog2);
|
||||
Store(op.Rt2, 0, WordSizeLog2);
|
||||
Store(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
@ -172,13 +172,13 @@ namespace ARMeilleure.Instructions
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
SetIntA32(context, op.Rt, valueLow);
|
||||
SetIntA32(context, op.Rt | 1, valueHigh);
|
||||
SetIntA32(context, op.Rt2, valueHigh);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
SetIntA32(context, op.Rt | 1, valueLow);
|
||||
SetIntA32(context, op.Rt2, valueLow);
|
||||
SetIntA32(context, op.Rt, valueHigh);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
@ -195,7 +195,7 @@ namespace ARMeilleure.Instructions
|
||||
// Split the result into 2 words (based on endianness)
|
||||
|
||||
Operand lo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
|
||||
Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt | 1));
|
||||
Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt2));
|
||||
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
@ -123,6 +123,41 @@ namespace ARMeilleure.Instructions
|
||||
context.CurrOp is OpCodeSimdMemSs);
|
||||
}
|
||||
|
||||
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
Operand temp = context.AllocateLocal(size == 3 ? OperandType.I64 : OperandType.I32);
|
||||
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
|
||||
|
||||
Operand value = default;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: value = context.Load8 (physAddr); break;
|
||||
case 1: value = context.Load16(physAddr); break;
|
||||
case 2: value = context.Load (OperandType.I32, physAddr); break;
|
||||
case 3: value = context.Load (OperandType.I64, physAddr); break;
|
||||
}
|
||||
|
||||
context.Copy(temp, value);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Copy(temp, EmitReadIntFallback(context, address, size));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static void EmitReadInt(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Operand lblSlowPath = Label();
|
||||
@ -419,6 +454,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
SetInt(context, rt, EmitReadIntFallback(context, address, size));
|
||||
}
|
||||
|
||||
private static Operand EmitReadIntFallback(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
MethodInfo info = null;
|
||||
|
||||
@ -430,7 +470,7 @@ namespace ARMeilleure.Instructions
|
||||
case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break;
|
||||
}
|
||||
|
||||
SetInt(context, rt, context.Call(info, address));
|
||||
return context.Call(info, address);
|
||||
}
|
||||
|
||||
private static void EmitReadVectorFallback(
|
||||
|
@ -25,7 +25,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Mla(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
@ -43,7 +43,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Mls(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
@ -71,7 +71,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
@ -99,7 +99,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smla__(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
@ -142,7 +142,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smlal__(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
@ -180,7 +180,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smlaw_(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
@ -210,7 +210,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smul__(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
@ -240,7 +240,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smull(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
@ -261,7 +261,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Smulw_(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
@ -285,7 +285,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Umaal(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
@ -310,7 +310,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Umull(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
@ -331,7 +331,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static void EmitMlal(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
@ -777,6 +777,13 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmlal_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitVectorTernaryLongOpI32(context, (d, n, m) => context.Add(d, context.Multiply(n, m)), !op.U);
|
||||
}
|
||||
|
||||
public static void Vmls_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
@ -992,6 +999,13 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vpaddl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
EmitVectorPairwiseLongOpI32(context, (op1, op2) => context.Add(op1, op2), (op.Opc & 1) == 0);
|
||||
}
|
||||
|
||||
public static void Vpmax_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
@ -1014,7 +1028,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorPairwiseOpI32(context, (op1, op2) =>
|
||||
EmitVectorPairwiseOpI32(context, (op1, op2) =>
|
||||
{
|
||||
Operand greater = op.U ? context.ICompareGreaterUI(op1, op2) : context.ICompareGreater(op1, op2);
|
||||
return context.ConditionalSelect(greater, op1, op2);
|
||||
@ -1052,6 +1066,62 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vqadd(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitSaturatingAddSubBinaryOp(context, add: true, !op.U);
|
||||
}
|
||||
|
||||
public static void Vqdmulh(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
||||
{
|
||||
if (op.Size == 2)
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op2 = context.SignExtend32(OperandType.I64, op2);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(op1, op2);
|
||||
res = context.ShiftRightSI(res, Const(eSize - 1));
|
||||
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
res = context.ConvertI64ToI32(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}, signed: true);
|
||||
}
|
||||
|
||||
public static void Vqmovn(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovn op = (OpCode32SimdMovn)context.CurrOp;
|
||||
|
||||
bool signed = !op.Q;
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signed, signed), signed);
|
||||
}
|
||||
|
||||
public static void Vqmovun(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovn op = (OpCode32SimdMovn)context.CurrOp;
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
|
||||
}
|
||||
|
||||
public static void Vqsub(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitSaturatingAddSubBinaryOp(context, add: false, !op.U);
|
||||
}
|
||||
|
||||
public static void Vrev(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdRev op = (OpCode32SimdRev)context.CurrOp;
|
||||
@ -1202,6 +1272,30 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vrhadd(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitVectorBinaryOpI32(context, (op1, op2) =>
|
||||
{
|
||||
if (op.Size == 2)
|
||||
{
|
||||
op1 = context.ZeroExtend32(OperandType.I64, op1);
|
||||
op2 = context.ZeroExtend32(OperandType.I64, op2);
|
||||
}
|
||||
|
||||
Operand res = context.Add(context.Add(op1, op2), Const(op1.Type, 1L));
|
||||
res = context.ShiftRightUI(res, Const(1));
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
res = context.ConvertI64ToI32(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}, !op.U);
|
||||
}
|
||||
|
||||
public static void Vrsqrte(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSqrte op = (OpCode32SimdSqrte)context.CurrOp;
|
||||
@ -1349,6 +1443,13 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vsubl_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdRegLong op = (OpCode32SimdRegLong)context.CurrOp;
|
||||
|
||||
EmitVectorBinaryLongOpI32(context, (op1, op2) => context.Subtract(op1, op2), !op.U);
|
||||
}
|
||||
|
||||
public static void Vsubw_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdRegWide op = (OpCode32SimdRegWide)context.CurrOp;
|
||||
@ -1356,6 +1457,46 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorBinaryWideOpI32(context, (op1, op2) => context.Subtract(op1, op2), !op.U);
|
||||
}
|
||||
|
||||
private static void EmitSaturatingAddSubBinaryOp(ArmEmitterContext context, bool add, bool signed)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
EmitVectorBinaryOpI32(context, (ne, me) =>
|
||||
{
|
||||
if (op.Size <= 2)
|
||||
{
|
||||
if (op.Size == 2)
|
||||
{
|
||||
ne = signed ? context.SignExtend32(OperandType.I64, ne) : context.ZeroExtend32(OperandType.I64, ne);
|
||||
me = signed ? context.SignExtend32(OperandType.I64, me) : context.ZeroExtend32(OperandType.I64, me);
|
||||
}
|
||||
|
||||
Operand res = add ? context.Add(ne, me) : context.Subtract(ne, me);
|
||||
|
||||
res = EmitSatQ(context, res, 8 << op.Size, signedSrc: true, signed);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
res = context.ConvertI64ToI32(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else if (add) /* if (op.Size == 3) */
|
||||
{
|
||||
return signed
|
||||
? EmitBinarySignedSatQAdd(context, ne, me)
|
||||
: EmitBinaryUnsignedSatQAdd(context, ne, me);
|
||||
}
|
||||
else /* if (sub) */
|
||||
{
|
||||
return signed
|
||||
? EmitBinarySignedSatQSub(context, ne, me)
|
||||
: EmitBinaryUnsignedSatQSub(context, ne, me);
|
||||
}
|
||||
}, signed);
|
||||
}
|
||||
|
||||
private static void EmitSse41MaxMinNumOpF32(ArmEmitterContext context, bool isMaxNum, bool scalar)
|
||||
{
|
||||
IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
|
||||
|
@ -323,6 +323,60 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTA (vector).
|
||||
public static void Vrinta_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
||||
}
|
||||
|
||||
// VRINTM (vector).
|
||||
public static void Vrintm_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsMinusInfinity)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTN (vector).
|
||||
public static void Vrintn_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.ToEven, m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTP (vector).
|
||||
public static void Vrintp_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsPlusInfinity)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTZ (floating-point).
|
||||
public static void Vrint_Z(ArmEmitterContext context)
|
||||
{
|
||||
|
@ -1281,7 +1281,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
|
||||
{
|
||||
isTrue = isTrue == default
|
||||
isTrue = isTrue == default
|
||||
? context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)))
|
||||
: isTrue;
|
||||
|
||||
@ -1612,7 +1612,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
// long BinarySignedSatQAdd(long op1, long op2);
|
||||
private static Operand EmitBinarySignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
public static Operand EmitBinarySignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
{
|
||||
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
|
||||
|
||||
@ -1639,7 +1639,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
// ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2);
|
||||
private static Operand EmitBinaryUnsignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
public static Operand EmitBinaryUnsignedSatQAdd(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
{
|
||||
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
|
||||
|
||||
@ -1660,7 +1660,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
// long BinarySignedSatQSub(long op1, long op2);
|
||||
private static Operand EmitBinarySignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
public static Operand EmitBinarySignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
{
|
||||
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
|
||||
|
||||
@ -1687,7 +1687,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
// ulong BinaryUnsignedSatQSub(ulong op1, ulong op2);
|
||||
private static Operand EmitBinaryUnsignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
public static Operand EmitBinaryUnsignedSatQSub(ArmEmitterContext context, Operand op1, Operand op2)
|
||||
{
|
||||
Debug.Assert(op1.Type == OperandType.I64 && op2.Type == OperandType.I64);
|
||||
|
||||
|
@ -219,6 +219,25 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
// Integer
|
||||
|
||||
public static void EmitVectorUnaryAccumulateOpI32(ArmEmitterContext context, Func1I emit, bool signed)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand res = GetVecA32(op.Qd);
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size, signed);
|
||||
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.Add(de, emit(me)), op.Id + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpI32(ArmEmitterContext context, Func1I emit, bool signed)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
@ -385,6 +404,18 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryOpI32(context, emit, true);
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpSx32(ArmEmitterContext context, Func1I emit, bool accumulate)
|
||||
{
|
||||
if (accumulate)
|
||||
{
|
||||
EmitVectorUnaryAccumulateOpI32(context, emit, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpI32(context, emit, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpSx32(ArmEmitterContext context, Func2I emit)
|
||||
{
|
||||
EmitVectorBinaryOpI32(context, emit, true);
|
||||
@ -400,6 +431,18 @@ namespace ARMeilleure.Instructions
|
||||
EmitVectorUnaryOpI32(context, emit, false);
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpZx32(ArmEmitterContext context, Func1I emit, bool accumulate)
|
||||
{
|
||||
if (accumulate)
|
||||
{
|
||||
EmitVectorUnaryAccumulateOpI32(context, emit, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpI32(context, emit, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpZx32(ArmEmitterContext context, Func2I emit)
|
||||
{
|
||||
EmitVectorBinaryOpI32(context, emit, false);
|
||||
@ -592,6 +635,34 @@ namespace ARMeilleure.Instructions
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void EmitVectorPairwiseLongOpI32(ArmEmitterContext context, Func2I emit, bool signed)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
int elems = (op.Q ? 16 : 8) >> op.Size;
|
||||
int pairs = elems >> 1;
|
||||
int id = (op.Vd & 1) * pairs;
|
||||
|
||||
Operand res = GetVecA32(op.Qd);
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int pairIndex = index << 1;
|
||||
Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
|
||||
Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
m1 = signed ? context.SignExtend32(OperandType.I64, m1) : context.ZeroExtend32(OperandType.I64, m1);
|
||||
m2 = signed ? context.SignExtend32(OperandType.I64, m2) : context.ZeroExtend32(OperandType.I64, m2);
|
||||
}
|
||||
|
||||
res = EmitVectorInsert(context, res, emit(m1, m2), id + index, op.Size + 1);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
// Narrow
|
||||
|
||||
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
||||
|
@ -33,64 +33,24 @@ namespace ARMeilleure.Instructions
|
||||
EmitShrImmSaturatingNarrowOp(context, op.U ? ShrImmSaturatingNarrowFlags.VectorZxZx : ShrImmSaturatingNarrowFlags.VectorSxSx);
|
||||
}
|
||||
|
||||
public static void Vqshrun(ArmEmitterContext context)
|
||||
{
|
||||
EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx);
|
||||
}
|
||||
|
||||
public static void Vrshr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
EmitRoundShrImmOp(context, accumulate: false);
|
||||
}
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
public static void Vrshrn(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmNarrowOp(context, signed: false);
|
||||
}
|
||||
|
||||
return context.ShiftRightUI(op1, Const(shift));
|
||||
});
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.ZeroExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightUI(op1, Const(shift)));
|
||||
});
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: false, roundConst, shift));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ShiftRightSI(op1, Const(shift));
|
||||
});
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightSI(op1, Const(shift)));
|
||||
});
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: true, roundConst, shift));
|
||||
}
|
||||
}
|
||||
public static void Vrsra(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmOp(context, accumulate: true);
|
||||
}
|
||||
|
||||
public static void Vshl(ArmEmitterContext context)
|
||||
@ -191,6 +151,89 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitRoundShrImmOp(ArmEmitterContext context, bool accumulate)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ShiftRightUI(op1, Const(shift));
|
||||
}, accumulate);
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.ZeroExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightUI(op1, Const(shift)));
|
||||
}, accumulate);
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: false, roundConst, shift), accumulate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ShiftRightSI(op1, Const(shift));
|
||||
}, accumulate);
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightSI(op1, Const(shift)));
|
||||
}, accumulate);
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: true, roundConst, shift), accumulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitRoundShrImmNarrowOp(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) =>
|
||||
{
|
||||
if (op.Size <= 1)
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
op1 = signed ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
|
||||
}
|
||||
else /* if (op.Size == 2 && round) */
|
||||
{
|
||||
op1 = EmitShrImm64(context, op1, signed, roundConst, shift); // shift <= 32
|
||||
}
|
||||
|
||||
return op1;
|
||||
}, signed);
|
||||
}
|
||||
|
||||
private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
|
||||
{
|
||||
if (shiftLsB.Type == OperandType.I64)
|
||||
@ -289,7 +332,7 @@ namespace ARMeilleure.Instructions
|
||||
op1 = EmitShrImm64(context, op1, signedSrc, roundConst, shift); // shift <= 32
|
||||
}
|
||||
|
||||
return EmitSatQ(context, op1, 8 << op.Size, signedDst);
|
||||
return EmitSatQ(context, op1, 8 << op.Size, signedSrc, signedDst);
|
||||
}, signedSrc);
|
||||
}
|
||||
|
||||
@ -313,15 +356,20 @@ namespace ARMeilleure.Instructions
|
||||
return context.Call(info, value, Const(roundConst), Const(shift));
|
||||
}
|
||||
|
||||
private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signed)
|
||||
private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signedSrc, bool signedDst)
|
||||
{
|
||||
Debug.Assert(eSize <= 32);
|
||||
|
||||
long intMin = signed ? -(1L << (eSize - 1)) : 0;
|
||||
long intMax = signed ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1;
|
||||
long intMin = signedDst ? -(1L << (eSize - 1)) : 0;
|
||||
long intMax = signedDst ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1;
|
||||
|
||||
Operand gt = context.ICompareGreater(value, Const(value.Type, intMax));
|
||||
Operand lt = context.ICompareLess(value, Const(value.Type, intMin));
|
||||
Operand gt = signedSrc
|
||||
? context.ICompareGreater(value, Const(value.Type, intMax))
|
||||
: context.ICompareGreaterUI(value, Const(value.Type, intMax));
|
||||
|
||||
Operand lt = signedSrc
|
||||
? context.ICompareLess(value, Const(value.Type, intMin))
|
||||
: context.ICompareLessUI(value, Const(value.Type, intMin));
|
||||
|
||||
value = context.ConditionalSelect(gt, Const(value.Type, intMax), value);
|
||||
value = context.ConditionalSelect(lt, Const(value.Type, intMin), value);
|
||||
|
@ -16,18 +16,13 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15)
|
||||
if (op.Coproc != 15 || op.Opc1 != 0)
|
||||
{
|
||||
InstEmit.Und(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (op.Opc1 != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
MethodInfo info;
|
||||
|
||||
switch (op.CRn)
|
||||
@ -35,7 +30,7 @@ namespace ARMeilleure.Instructions
|
||||
case 13: // Process and Thread Info.
|
||||
if (op.CRm != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
switch (op.Opc2)
|
||||
@ -44,7 +39,7 @@ namespace ARMeilleure.Instructions
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -59,11 +54,11 @@ namespace ARMeilleure.Instructions
|
||||
return; // No-op.
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
default:
|
||||
@ -77,18 +72,13 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15)
|
||||
if (op.Coproc != 15 || op.Opc1 != 0)
|
||||
{
|
||||
InstEmit.Und(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (op.Opc1 != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
MethodInfo info;
|
||||
|
||||
switch (op.CRn)
|
||||
@ -96,7 +86,7 @@ namespace ARMeilleure.Instructions
|
||||
case 13: // Process and Thread Info.
|
||||
if (op.CRm != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
switch (op.Opc2)
|
||||
@ -108,13 +98,13 @@ namespace ARMeilleure.Instructions
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
|
||||
if (op.Rt == RegisterAlias.Aarch32Pc)
|
||||
@ -154,13 +144,13 @@ namespace ARMeilleure.Instructions
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
|
||||
Operand result = context.Call(info);
|
||||
@ -265,7 +255,7 @@ namespace ARMeilleure.Instructions
|
||||
case 0b1000: // FPEXC
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +278,7 @@ namespace ARMeilleure.Instructions
|
||||
case 0b1000: // FPEXC
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,6 +545,8 @@ namespace ARMeilleure.Instructions
|
||||
Strexh,
|
||||
Strh,
|
||||
Sxtb16,
|
||||
Tbb,
|
||||
Tbh,
|
||||
Teq,
|
||||
Trap,
|
||||
Tst,
|
||||
@ -601,6 +603,7 @@ namespace ARMeilleure.Instructions
|
||||
Vmin,
|
||||
Vminnm,
|
||||
Vmla,
|
||||
Vmlal,
|
||||
Vmls,
|
||||
Vmlsl,
|
||||
Vmov,
|
||||
@ -618,15 +621,28 @@ namespace ARMeilleure.Instructions
|
||||
Vorn,
|
||||
Vorr,
|
||||
Vpadd,
|
||||
Vpaddl,
|
||||
Vpmax,
|
||||
Vpmin,
|
||||
Vqadd,
|
||||
Vqdmulh,
|
||||
Vqmovn,
|
||||
Vqmovun,
|
||||
Vqrshrn,
|
||||
Vqrshrun,
|
||||
Vqshrn,
|
||||
Vqshrun,
|
||||
Vqsub,
|
||||
Vrev,
|
||||
Vrhadd,
|
||||
Vrint,
|
||||
Vrinta,
|
||||
Vrintm,
|
||||
Vrintn,
|
||||
Vrintp,
|
||||
Vrintx,
|
||||
Vrshr,
|
||||
Vrshrn,
|
||||
Vsel,
|
||||
Vshl,
|
||||
Vshll,
|
||||
@ -643,8 +659,10 @@ namespace ARMeilleure.Instructions
|
||||
Vrecps,
|
||||
Vrsqrte,
|
||||
Vrsqrts,
|
||||
Vrsra,
|
||||
Vsra,
|
||||
Vsub,
|
||||
Vsubl,
|
||||
Vsubw,
|
||||
Vtbl,
|
||||
Vtrn,
|
||||
|
@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 3666; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 3683; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
10
README.md
10
README.md
@ -36,8 +36,8 @@
|
||||
|
||||
## Compatibility
|
||||
|
||||
As of January 2022, Ryujinx has been tested on approximately 3,500 titles; over 3,200 boot past menus and into gameplay, with roughly 2,500 of those being considered playable.
|
||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit an updated test on an existing game entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||
|
||||
## Usage
|
||||
|
||||
@ -118,11 +118,11 @@ If you'd like to support the project financially, Ryujinx has an active Patreon
|
||||
<img src="https://images.squarespace-cdn.com/content/v1/560c1d39e4b0b4fae0c9cf2a/1567548955044-WVD994WZP76EWF15T0L3/Patreon+Button.png?format=500w" width="150">
|
||||
</a>
|
||||
|
||||
All the developers working on the project do so on their free time, but the project has several expenses:
|
||||
All developers working on the project do so in their free time, but the project has several expenses:
|
||||
* Hackable Nintendo Switch consoles to reverse-engineer the hardware
|
||||
* Additional computer hardware for testing purposes (e.g. GPUs to diagnose graphical bugs, etc.)
|
||||
* Licenses for various software development tools (e.g. Jetbrains, LDN servers, IDA)
|
||||
* Web hosting and infrastructure maintenance
|
||||
* Licenses for various software development tools (e.g. Jetbrains, IDA)
|
||||
* Web hosting and infrastructure maintenance (e.g. LDN servers)
|
||||
|
||||
All funds received through Patreon are considered a donation to support the project. Patrons receive early access to progress reports and exclusive access to developer interviews.
|
||||
|
||||
|
@ -55,7 +55,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
private uint _processHandle;
|
||||
private ulong _appletResourceId;
|
||||
|
||||
private WritableRegion _workBufferRegion;
|
||||
private MemoryHandle _workBufferMemoryPin;
|
||||
|
||||
private Memory<float> _mixBuffer;
|
||||
@ -98,7 +97,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
_sessionId = 0;
|
||||
}
|
||||
|
||||
public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, IVirtualMemoryManager memoryManager)
|
||||
public ResultCode Initialize(
|
||||
ref AudioRendererConfiguration parameter,
|
||||
uint processHandle,
|
||||
Memory<byte> workBufferMemory,
|
||||
CpuAddress workBuffer,
|
||||
ulong workBufferSize,
|
||||
int sessionId,
|
||||
ulong appletResourceId,
|
||||
IVirtualMemoryManager memoryManager)
|
||||
{
|
||||
if (!BehaviourContext.CheckValidRevision(parameter.Revision))
|
||||
{
|
||||
@ -134,11 +141,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
WorkBufferAllocator workBufferAllocator;
|
||||
|
||||
_workBufferRegion = MemoryManager.GetWritableRegion(workBuffer, (int)workBufferSize);
|
||||
_workBufferRegion.Memory.Span.Fill(0);
|
||||
_workBufferMemoryPin = _workBufferRegion.Memory.Pin();
|
||||
workBufferMemory.Span.Fill(0);
|
||||
_workBufferMemoryPin = workBufferMemory.Pin();
|
||||
|
||||
workBufferAllocator = new WorkBufferAllocator(_workBufferRegion.Memory);
|
||||
workBufferAllocator = new WorkBufferAllocator(workBufferMemory);
|
||||
|
||||
PoolMapper poolMapper = new PoolMapper(processHandle, false);
|
||||
poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize);
|
||||
@ -841,7 +847,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
_manager.Unregister(this);
|
||||
_terminationEvent.Dispose();
|
||||
_workBufferMemoryPin.Dispose();
|
||||
_workBufferRegion.Dispose();
|
||||
|
||||
if (MemoryManager is IRefCounted rc)
|
||||
{
|
||||
|
@ -305,13 +305,34 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <param name="workBufferSize">The guest work buffer size.</param>
|
||||
/// <param name="processHandle">The process handle of the application.</param>
|
||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
|
||||
public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, IVirtualMemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle, float volume)
|
||||
public ResultCode OpenAudioRenderer(
|
||||
out AudioRenderSystem renderer,
|
||||
IVirtualMemoryManager memoryManager,
|
||||
ref AudioRendererConfiguration parameter,
|
||||
ulong appletResourceUserId,
|
||||
ulong workBufferAddress,
|
||||
ulong workBufferSize,
|
||||
uint processHandle,
|
||||
float volume)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
AudioRenderSystem audioRenderer = new AudioRenderSystem(this, _sessionsSystemEvent[sessionId]);
|
||||
|
||||
ResultCode result = audioRenderer.Initialize(ref parameter, processHandle, workBufferAddress, workBufferSize, sessionId, appletResourceUserId, memoryManager);
|
||||
// TODO: Eventually, we should try to use the guest supplied work buffer instead of allocating
|
||||
// our own. However, it was causing problems on some applications that would unmap the memory
|
||||
// before the audio renderer was fully disposed.
|
||||
Memory<byte> workBufferMemory = GC.AllocateArray<byte>((int)workBufferSize, pinned: true);
|
||||
|
||||
ResultCode result = audioRenderer.Initialize(
|
||||
ref parameter,
|
||||
processHandle,
|
||||
workBufferMemory,
|
||||
workBufferAddress,
|
||||
workBufferSize,
|
||||
sessionId,
|
||||
appletResourceUserId,
|
||||
memoryManager);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
|
@ -5,10 +5,10 @@ namespace Ryujinx.Common.Collections
|
||||
/// </summary>
|
||||
public class IntrusiveRedBlackTreeNode<T> where T : IntrusiveRedBlackTreeNode<T>
|
||||
{
|
||||
public bool Color = true;
|
||||
public T Left;
|
||||
public T Right;
|
||||
public T Parent;
|
||||
internal bool Color = true;
|
||||
internal T Left;
|
||||
internal T Right;
|
||||
internal T Parent;
|
||||
|
||||
public T Predecessor => IntrusiveRedBlackTreeImpl<T>.PredecessorOf((T)this);
|
||||
public T Successor => IntrusiveRedBlackTreeImpl<T>.SuccessorOf((T)this);
|
||||
|
@ -274,7 +274,8 @@ namespace Ryujinx.Cpu.Jit
|
||||
/// <inheritdoc/>
|
||||
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)data.Length, write: true);
|
||||
|
||||
_addressSpaceMirror.Write(va, data);
|
||||
|
@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsNonConstantTextureOffset;
|
||||
public readonly bool SupportsShaderBallot;
|
||||
public readonly bool SupportsTextureShadowLod;
|
||||
public readonly bool SupportsViewportIndex;
|
||||
public readonly bool SupportsViewportSwizzle;
|
||||
public readonly bool SupportsIndirectParameters;
|
||||
|
||||
@ -59,6 +60,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsNonConstantTextureOffset,
|
||||
bool supportsShaderBallot,
|
||||
bool supportsTextureShadowLod,
|
||||
bool supportsViewportIndex,
|
||||
bool supportsViewportSwizzle,
|
||||
bool supportsIndirectParameters,
|
||||
uint maximumUniformBuffersPerStage,
|
||||
@ -89,6 +91,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||
SupportsShaderBallot = supportsShaderBallot;
|
||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||
SupportsViewportIndex = supportsViewportIndex;
|
||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||
SupportsIndirectParameters = supportsIndirectParameters;
|
||||
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
@ -151,10 +152,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
ulong ticks = _context.GetTimestamp();
|
||||
|
||||
float divisor = type switch
|
||||
{
|
||||
ReportCounterType.SamplesPassed => _channel.TextureManager.RenderTargetScale * _channel.TextureManager.RenderTargetScale,
|
||||
_ => 1f
|
||||
};
|
||||
|
||||
ICounterEvent counter = null;
|
||||
|
||||
void resultHandler(object evt, ulong result)
|
||||
{
|
||||
if (divisor != 1f)
|
||||
{
|
||||
result = (ulong)MathF.Ceiling(result / divisor);
|
||||
}
|
||||
|
||||
CounterData counterData = new CounterData
|
||||
{
|
||||
Counter = result,
|
||||
|
@ -47,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
public int TextureHandle;
|
||||
public int SamplerHandle;
|
||||
public Format ImageFormat;
|
||||
public int InvalidatedSequence;
|
||||
public Texture CachedTexture;
|
||||
public Sampler CachedSampler;
|
||||
@ -564,6 +565,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
|
||||
// to ensure we're not using a old buffer that was already deleted.
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
|
||||
|
||||
// Cache is not used for buffer texture, it must always rebind.
|
||||
state.CachedTexture = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -659,14 +663,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
cachedTexture?.SignalModified();
|
||||
}
|
||||
|
||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
|
||||
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||
|
||||
if (state.ImageFormat != format ||
|
||||
((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)))
|
||||
{
|
||||
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||
|
||||
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||
|
||||
state.Texture = hostTextureRebind;
|
||||
state.ImageFormat = format;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
|
||||
}
|
||||
@ -696,6 +702,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
|
||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
|
||||
|
||||
// Cache is not used for buffer texture, it must always rebind.
|
||||
state.CachedTexture = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -721,6 +730,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
format = texture.Format;
|
||||
}
|
||||
|
||||
state.ImageFormat = format;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 3672;
|
||||
private const uint CodeGenVersion = 3644;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
@ -827,4 +827,4 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
||||
|
||||
public bool QueryHostSupportsViewportIndex() => _context.Capabilities.SupportsViewportIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
||||
/// </summary>
|
||||
|
@ -120,6 +120,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||
supportsViewportIndex: true,
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||
|
@ -267,6 +267,15 @@ namespace Ryujinx.Graphics.Shader
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU shader viewport index output support.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports shader viewport index output, false otherwise</returns>
|
||||
bool QueryHostSupportsViewportIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the point size from the GPU state, used when it is not explicitly set on the shader.
|
||||
/// </summary>
|
||||
|
@ -78,6 +78,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return From(config, value, isOutAttr).IsValid;
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
format = VkFormat.D32SfloatS8Uint;
|
||||
}
|
||||
else if (srcFormat == GAL.Format.R4G4B4A4Unorm)
|
||||
{
|
||||
format = VkFormat.R4G4B4A4UnormPack16;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
|
||||
|
@ -6,11 +6,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class FormatTable
|
||||
{
|
||||
private static readonly VkFormat[] Table;
|
||||
private static readonly VkFormat[] _table;
|
||||
|
||||
static FormatTable()
|
||||
{
|
||||
Table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
|
||||
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
|
||||
|
||||
Add(Format.R8Unorm, VkFormat.R8Unorm);
|
||||
Add(Format.R8Snorm, VkFormat.R8SNorm);
|
||||
@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);
|
||||
Add(Format.R8G8B8A8Srgb, VkFormat.R8G8B8A8Srgb);
|
||||
Add(Format.R4G4Unorm, VkFormat.R4G4UnormPack8);
|
||||
Add(Format.R4G4B4A4Unorm, VkFormat.R4G4B4A4UnormPack16);
|
||||
Add(Format.R4G4B4A4Unorm, VkFormat.A4B4G4R4UnormPack16Ext);
|
||||
Add(Format.R5G5B5X1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.R5G5B5A1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.R5G6B5Unorm, VkFormat.R5G6B5UnormPack16);
|
||||
@ -161,12 +161,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private static void Add(Format format, VkFormat vkFormat)
|
||||
{
|
||||
Table[(int)format] = vkFormat;
|
||||
_table[(int)format] = vkFormat;
|
||||
}
|
||||
|
||||
public static VkFormat GetFormat(Format format)
|
||||
{
|
||||
return Table[(int)format];
|
||||
return _table[(int)format];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,17 +74,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
swizzleR = swizzleB;
|
||||
swizzleB = temp;
|
||||
}
|
||||
else if (info.Format == GAL.Format.R4G4B4A4Unorm)
|
||||
{
|
||||
var tempG = swizzleG;
|
||||
var tempB = swizzleB;
|
||||
|
||||
swizzleB = swizzleA;
|
||||
swizzleG = swizzleR;
|
||||
swizzleR = tempG;
|
||||
swizzleA = tempB;
|
||||
}
|
||||
else if (info.Format == GAL.Format.A1B5G5R5Unorm)
|
||||
else if (VkFormat == VkFormat.R4G4B4A4UnormPack16 || info.Format == GAL.Format.A1B5G5R5Unorm)
|
||||
{
|
||||
var tempB = swizzleB;
|
||||
var tempA = swizzleA;
|
||||
|
@ -380,7 +380,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return BufferManager.GetData(buffer, offset, size);
|
||||
}
|
||||
|
||||
public Capabilities GetCapabilities()
|
||||
public unsafe Capabilities GetCapabilities()
|
||||
{
|
||||
FormatFeatureFlags compressedFormatFeatureFlags =
|
||||
FormatFeatureFlags.FormatFeatureSampledImageBit |
|
||||
@ -409,7 +409,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
GAL.Format.Bc7Srgb,
|
||||
GAL.Format.Bc7Unorm);
|
||||
|
||||
Api.GetPhysicalDeviceFeatures(_physicalDevice, out var features);
|
||||
|
||||
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceVulkan12Features
|
||||
};
|
||||
|
||||
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceFeatures2,
|
||||
PNext = &featuresVk12
|
||||
};
|
||||
|
||||
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
|
||||
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
|
||||
|
||||
var limits = properties.Limits;
|
||||
@ -419,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
GpuVendor,
|
||||
hasFrontFacingBug: IsIntelWindows,
|
||||
hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
|
||||
supportsAstcCompression: features.TextureCompressionAstcLdr,
|
||||
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr,
|
||||
supportsBc123Compression: supportsBc123CompressionFormat,
|
||||
supportsBc45Compression: supportsBc45CompressionFormat,
|
||||
supportsBc67Compression: supportsBc67CompressionFormat,
|
||||
@ -429,12 +441,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
|
||||
supportsFragmentShaderOrderingIntel: false,
|
||||
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
||||
supportsImageLoadFormatted: features.ShaderStorageImageReadWithoutFormat,
|
||||
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
||||
supportsMismatchingViewFormat: true,
|
||||
supportsCubemapView: !IsAmdGcn,
|
||||
supportsNonConstantTextureOffset: false,
|
||||
supportsShaderBallot: false,
|
||||
supportsTextureShadowLod: false,
|
||||
supportsViewportIndex: featuresVk12.ShaderOutputViewportIndex,
|
||||
supportsViewportSwizzle: false,
|
||||
supportsIndirectParameters: Capabilities.SupportsIndirectParameters,
|
||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
||||
|
@ -31,11 +31,26 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||
return AudioRendererManagerImpl.GetWorkBufferSize(ref parameter);
|
||||
}
|
||||
|
||||
public ResultCode OpenAudioRenderer(ServiceCtx context, out IAudioRenderer obj, ref AudioRendererConfiguration parameter, ulong workBufferSize, ulong appletResourceUserId, KTransferMemory workBufferTransferMemory, uint processHandle)
|
||||
public ResultCode OpenAudioRenderer(
|
||||
ServiceCtx context,
|
||||
out IAudioRenderer obj,
|
||||
ref AudioRendererConfiguration parameter,
|
||||
ulong workBufferSize,
|
||||
ulong appletResourceUserId,
|
||||
KTransferMemory workBufferTransferMemory,
|
||||
uint processHandle)
|
||||
{
|
||||
var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
|
||||
|
||||
ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, memoryManager, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle, context.Device.Configuration.AudioVolume);
|
||||
ResultCode result = (ResultCode)_impl.OpenAudioRenderer(
|
||||
out AudioRenderSystem renderer,
|
||||
memoryManager,
|
||||
ref parameter,
|
||||
appletResourceUserId,
|
||||
workBufferTransferMemory.Address,
|
||||
workBufferTransferMemory.Size,
|
||||
processHandle,
|
||||
context.Device.Configuration.AudioVolume);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
|
@ -34,7 +34,14 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||
KTransferMemory workBufferTransferMemory = context.Process.HandleTable.GetObject<KTransferMemory>(transferMemoryHandle);
|
||||
uint processHandle = (uint)context.Request.HandleDesc.ToCopy[1];
|
||||
|
||||
ResultCode result = _impl.OpenAudioRenderer(context, out IAudioRenderer renderer, ref parameter, workBufferSize, appletResourceUserId, workBufferTransferMemory, processHandle);
|
||||
ResultCode result = _impl.OpenAudioRenderer(
|
||||
context,
|
||||
out IAudioRenderer renderer,
|
||||
ref parameter,
|
||||
workBufferSize,
|
||||
appletResourceUserId,
|
||||
workBufferTransferMemory,
|
||||
processHandle);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
|
@ -1,453 +0,0 @@
|
||||
using Ryujinx.Common.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
/// <summary>
|
||||
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key</typeparam>
|
||||
/// <typeparam name="V">Value</typeparam>
|
||||
class IntervalTree<K, V> : IntrusiveRedBlackTreeImpl<IntervalTreeNode<K, V>> where K : IComparable<K>
|
||||
{
|
||||
private const int ArrayGrowthSize = 32;
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the interval whose key is <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node value to get</param>
|
||||
/// <param name="value">Value with the given <paramref name="key"/></param>
|
||||
/// <returns>True if the key is on the dictionary, false otherwise</returns>
|
||||
public bool TryGet(K key, out V value)
|
||||
{
|
||||
IntervalTreeNode<K, V> node = GetNode(key);
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = node.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the start addresses of the intervals whose start and end keys overlap the given range.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range</param>
|
||||
/// <param name="end">End of the range</param>
|
||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
||||
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
|
||||
/// <returns>Number of intervals found</returns>
|
||||
public int Get(K start, K end, ref IntervalTreeNode<K, V>[] overlaps, int overlapCount = 0)
|
||||
{
|
||||
GetNodes(Root, start, end, ref overlaps, ref overlapCount);
|
||||
|
||||
return overlapCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range to add</param>
|
||||
/// <param name="end">End of the range to insert</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
|
||||
public void Add(K start, K end, V value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
BSTInsert(start, end, value, null, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node to remove</param>
|
||||
/// <returns>Number of deleted values</returns>
|
||||
public int Remove(K key)
|
||||
{
|
||||
return Remove(GetNode(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="nodeToDelete">Node to be removed</param>
|
||||
/// <returns>Number of deleted values</returns>
|
||||
public int Remove(IntervalTreeNode<K, V> nodeToDelete)
|
||||
{
|
||||
if (nodeToDelete == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Delete(nodeToDelete);
|
||||
|
||||
Count--;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
|
||||
/// </summary>
|
||||
/// <returns>A list of all values sorted by Key Order</returns>
|
||||
public List<V> AsList()
|
||||
{
|
||||
List<V> list = new List<V>();
|
||||
|
||||
AddToList(Root, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods (BST)
|
||||
|
||||
/// <summary>
|
||||
/// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to search for values within</param>
|
||||
/// <param name="list">The list to add values to</param>
|
||||
private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddToList(node.Left, list);
|
||||
|
||||
list.Add(node.Value);
|
||||
|
||||
AddToList(node.Right, list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node to get</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||
/// <returns>Node reference in the tree</returns>
|
||||
private IntervalTreeNode<K, V> GetNode(K key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
|
||||
IntervalTreeNode<K, V> node = Root;
|
||||
while (node != null)
|
||||
{
|
||||
int cmp = key.CompareTo(node.Start);
|
||||
if (cmp < 0)
|
||||
{
|
||||
node = node.Left;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
node = node.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all nodes that overlap the given start and end keys.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range</param>
|
||||
/// <param name="end">End of the range</param>
|
||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
||||
/// <param name="overlapCount">Overlaps count to update</param>
|
||||
private void GetNodes(IntervalTreeNode<K, V> node, K start, K end, ref IntervalTreeNode<K, V>[] overlaps, ref int overlapCount)
|
||||
{
|
||||
if (node == null || start.CompareTo(node.Max) >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetNodes(node.Left, start, end, ref overlaps, ref overlapCount);
|
||||
|
||||
bool endsOnRight = end.CompareTo(node.Start) > 0;
|
||||
if (endsOnRight)
|
||||
{
|
||||
if (start.CompareTo(node.End) < 0)
|
||||
{
|
||||
if (overlaps.Length >= overlapCount)
|
||||
{
|
||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||
}
|
||||
|
||||
overlaps[overlapCount++] = node;
|
||||
}
|
||||
|
||||
GetNodes(node.Right, start, end, ref overlaps, ref overlapCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate an increase in max value starting at the given node, heading up the tree.
|
||||
/// This should only be called if the max increases - not for rebalancing or removals.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to start propagating from</param>
|
||||
private void PropagateIncrease(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
K max = node.Max;
|
||||
IntervalTreeNode<K, V> ptr = node;
|
||||
|
||||
while ((ptr = ptr.Parent) != null)
|
||||
{
|
||||
if (max.CompareTo(ptr.Max) > 0)
|
||||
{
|
||||
ptr.Max = max;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate recalculating max value starting at the given node, heading up the tree.
|
||||
/// This fully recalculates the max value from all children when there is potential for it to decrease.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to start propagating from</param>
|
||||
private void PropagateFull(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
IntervalTreeNode<K, V> ptr = node;
|
||||
|
||||
do
|
||||
{
|
||||
K max = ptr.End;
|
||||
|
||||
if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
|
||||
{
|
||||
max = ptr.Left.Max;
|
||||
}
|
||||
|
||||
if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
|
||||
{
|
||||
max = ptr.Right.Max;
|
||||
}
|
||||
|
||||
ptr.Max = max;
|
||||
} while ((ptr = ptr.Parent) != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
|
||||
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
|
||||
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
|
||||
/// Post insertion, the "max" value of the node and all parents are updated.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range to insert</param>
|
||||
/// <param name="end">End of the range to insert</param>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
|
||||
/// <param name="outNode">Node that was inserted or modified</param>
|
||||
/// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
|
||||
private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
|
||||
{
|
||||
IntervalTreeNode<K, V> parent = null;
|
||||
IntervalTreeNode<K, V> node = Root;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
parent = node;
|
||||
int cmp = start.CompareTo(node.Start);
|
||||
if (cmp < 0)
|
||||
{
|
||||
node = node.Left;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
node = node.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
outNode = node;
|
||||
|
||||
if (updateFactoryCallback != null)
|
||||
{
|
||||
// Replace
|
||||
node.Value = updateFactoryCallback(start, node.Value);
|
||||
|
||||
int endCmp = end.CompareTo(node.End);
|
||||
|
||||
if (endCmp > 0)
|
||||
{
|
||||
node.End = end;
|
||||
if (end.CompareTo(node.Max) > 0)
|
||||
{
|
||||
node.Max = end;
|
||||
PropagateIncrease(node);
|
||||
RestoreBalanceAfterInsertion(node);
|
||||
}
|
||||
}
|
||||
else if (endCmp < 0)
|
||||
{
|
||||
node.End = end;
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
|
||||
if (newNode.Parent == null)
|
||||
{
|
||||
Root = newNode;
|
||||
}
|
||||
else if (start.CompareTo(parent.Start) < 0)
|
||||
{
|
||||
parent.Left = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Right = newNode;
|
||||
}
|
||||
|
||||
PropagateIncrease(newNode);
|
||||
Count++;
|
||||
RestoreBalanceAfterInsertion(newNode);
|
||||
outNode = newNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the value from the dictionary after searching for it with <paramref name="key">.
|
||||
/// </summary>
|
||||
/// <param name="key">Tree node to be removed</param>
|
||||
private void Delete(IntervalTreeNode<K, V> nodeToDelete)
|
||||
{
|
||||
IntervalTreeNode<K, V> replacementNode;
|
||||
|
||||
if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
|
||||
{
|
||||
replacementNode = nodeToDelete;
|
||||
}
|
||||
else
|
||||
{
|
||||
replacementNode = nodeToDelete.Predecessor;
|
||||
}
|
||||
|
||||
IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||
|
||||
if (tmp != null)
|
||||
{
|
||||
tmp.Parent = ParentOf(replacementNode);
|
||||
}
|
||||
|
||||
if (ParentOf(replacementNode) == null)
|
||||
{
|
||||
Root = tmp;
|
||||
}
|
||||
else if (replacementNode == LeftOf(ParentOf(replacementNode)))
|
||||
{
|
||||
ParentOf(replacementNode).Left = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentOf(replacementNode).Right = tmp;
|
||||
}
|
||||
|
||||
if (replacementNode != nodeToDelete)
|
||||
{
|
||||
nodeToDelete.Start = replacementNode.Start;
|
||||
nodeToDelete.Value = replacementNode.Value;
|
||||
nodeToDelete.End = replacementNode.End;
|
||||
nodeToDelete.Max = replacementNode.Max;
|
||||
}
|
||||
|
||||
PropagateFull(replacementNode);
|
||||
|
||||
if (tmp != null && ColorOf(replacementNode) == Black)
|
||||
{
|
||||
RestoreBalanceAfterRemoval(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods (RBL)
|
||||
|
||||
protected override void RotateLeft(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
base.RotateLeft(node);
|
||||
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RotateRight(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
base.RotateRight(node);
|
||||
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool ContainsKey(K key)
|
||||
{
|
||||
return GetNode(key) != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key type of the node</typeparam>
|
||||
/// <typeparam name="V">Value type of the node</typeparam>
|
||||
class IntervalTreeNode<K, V> : IntrusiveRedBlackTreeNode<IntervalTreeNode<K, V>>
|
||||
{
|
||||
/// <summary>
|
||||
/// The start of the range.
|
||||
/// </summary>
|
||||
public K Start;
|
||||
|
||||
/// <summary>
|
||||
/// The end of the range.
|
||||
/// </summary>
|
||||
public K End;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum end value of this node and all its children.
|
||||
/// </summary>
|
||||
public K Max;
|
||||
|
||||
/// <summary>
|
||||
/// Value stored on this node.
|
||||
/// </summary>
|
||||
public V Value;
|
||||
|
||||
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
Max = end;
|
||||
Value = value;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
87
Ryujinx.Memory/WindowsShared/MappingTree.cs
Normal file
87
Ryujinx.Memory/WindowsShared/MappingTree.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using Ryujinx.Common.Collections;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
/// <summary>
|
||||
/// A intrusive Red-Black Tree that also supports getting nodes overlapping a given range.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the value stored on the node</typeparam>
|
||||
class MappingTree<T> : IntrusiveRedBlackTree<RangeNode<T>>
|
||||
{
|
||||
private const int ArrayGrowthSize = 16;
|
||||
|
||||
public int GetNodes(ulong start, ulong end, ref RangeNode<T>[] overlaps, int overlapCount = 0)
|
||||
{
|
||||
RangeNode<T> node = this.GetNodeByKey(start);
|
||||
|
||||
for (; node != null; node = node.Successor)
|
||||
{
|
||||
if (overlaps.Length <= overlapCount)
|
||||
{
|
||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||
}
|
||||
|
||||
overlaps[overlapCount++] = node;
|
||||
|
||||
if (node.End >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return overlapCount;
|
||||
}
|
||||
}
|
||||
|
||||
class RangeNode<T> : IntrusiveRedBlackTreeNode<RangeNode<T>>, IComparable<RangeNode<T>>, IComparable<ulong>
|
||||
{
|
||||
public ulong Start { get; }
|
||||
public ulong End { get; private set; }
|
||||
public T Value { get; }
|
||||
|
||||
public RangeNode(ulong start, ulong end, T value)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void Extend(ulong sizeDelta)
|
||||
{
|
||||
End += sizeDelta;
|
||||
}
|
||||
|
||||
public int CompareTo(RangeNode<T> other)
|
||||
{
|
||||
if (Start < other.Start)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (Start <= other.End - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(ulong address)
|
||||
{
|
||||
if (address < Start)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (address <= End - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Collections;
|
||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@ -13,10 +14,10 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
[SupportedOSPlatform("windows")]
|
||||
class PlaceholderManager
|
||||
{
|
||||
private const ulong MinimumPageSize = 0x1000;
|
||||
private const int InitialOverlapsSize = 10;
|
||||
|
||||
private readonly IntervalTree<ulong, ulong> _mappings;
|
||||
private readonly IntervalTree<ulong, MemoryPermission> _protections;
|
||||
private readonly MappingTree<ulong> _mappings;
|
||||
private readonly MappingTree<MemoryPermission> _protections;
|
||||
private readonly IntPtr _partialUnmapStatePtr;
|
||||
private readonly Thread _partialUnmapTrimThread;
|
||||
|
||||
@ -25,8 +26,8 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
/// </summary>
|
||||
public PlaceholderManager()
|
||||
{
|
||||
_mappings = new IntervalTree<ulong, ulong>();
|
||||
_protections = new IntervalTree<ulong, MemoryPermission>();
|
||||
_mappings = new MappingTree<ulong>();
|
||||
_protections = new MappingTree<MemoryPermission>();
|
||||
|
||||
_partialUnmapStatePtr = PartialUnmapState.GlobalState;
|
||||
|
||||
@ -67,7 +68,12 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
lock (_mappings)
|
||||
{
|
||||
_mappings.Add(address, address + size, ulong.MaxValue);
|
||||
_mappings.Add(new RangeNode<ulong>(address, address + size, ulong.MaxValue));
|
||||
}
|
||||
|
||||
lock (_protections)
|
||||
{
|
||||
_protections.Add(new RangeNode<MemoryPermission>(address, size, MemoryPermission.None));
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,35 +87,30 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||
int count;
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
count = _mappings.Get(address, endAddress, ref overlaps);
|
||||
RangeNode<ulong> node = _mappings.GetNodeByKey(address);
|
||||
RangeNode<ulong> successorNode;
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
for (; node != null; node = successorNode)
|
||||
{
|
||||
var overlap = overlaps[index];
|
||||
successorNode = node.Successor;
|
||||
|
||||
if (IsMapped(overlap.Value))
|
||||
if (IsMapped(node.Value))
|
||||
{
|
||||
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
|
||||
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)node.Start, 2))
|
||||
{
|
||||
throw new WindowsApiException("UnmapViewOfFile2");
|
||||
}
|
||||
}
|
||||
|
||||
_mappings.Remove(overlap);
|
||||
}
|
||||
}
|
||||
_mappings.Remove(node);
|
||||
|
||||
if (count > 1)
|
||||
{
|
||||
CheckFreeResult(WindowsApi.VirtualFree(
|
||||
(IntPtr)address,
|
||||
(IntPtr)size,
|
||||
AllocationType.Release | AllocationType.CoalescePlaceholders));
|
||||
if (node.End >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemoveProtection(address, size);
|
||||
@ -130,7 +131,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
|
||||
try
|
||||
{
|
||||
UnmapViewInternal(sharedMemory, location, size, owner);
|
||||
UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: false);
|
||||
MapViewInternal(sharedMemory, srcOffset, location, size);
|
||||
}
|
||||
finally
|
||||
@ -166,6 +167,8 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
throw new WindowsApiException("MapViewOfFile3");
|
||||
}
|
||||
|
||||
UpdateProtection((ulong)location, (ulong)size, MemoryPermission.ReadAndWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -178,18 +181,17 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||
var overlaps = new RangeNode<ulong>[InitialOverlapsSize];
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
int count = _mappings.Get(address, endAddress, ref overlaps);
|
||||
int count = _mappings.GetNodes(address, endAddress, ref overlaps);
|
||||
|
||||
Debug.Assert(count == 1);
|
||||
Debug.Assert(!IsMapped(overlaps[0].Value));
|
||||
|
||||
var overlap = overlaps[0];
|
||||
|
||||
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
|
||||
ulong overlapStart = overlap.Start;
|
||||
ulong overlapEnd = overlap.End;
|
||||
ulong overlapValue = overlap.Value;
|
||||
@ -206,8 +208,8 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
(IntPtr)size,
|
||||
AllocationType.Release | AllocationType.PreservePlaceholder));
|
||||
|
||||
_mappings.Add(overlapStart, address, overlapValue);
|
||||
_mappings.Add(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart));
|
||||
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
|
||||
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
|
||||
}
|
||||
else if (overlapStartsBefore)
|
||||
{
|
||||
@ -218,7 +220,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
(IntPtr)overlappedSize,
|
||||
AllocationType.Release | AllocationType.PreservePlaceholder));
|
||||
|
||||
_mappings.Add(overlapStart, address, overlapValue);
|
||||
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
|
||||
}
|
||||
else if (overlapEndsAfter)
|
||||
{
|
||||
@ -229,10 +231,10 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
(IntPtr)overlappedSize,
|
||||
AllocationType.Release | AllocationType.PreservePlaceholder));
|
||||
|
||||
_mappings.Add(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize));
|
||||
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
|
||||
}
|
||||
|
||||
_mappings.Add(address, endAddress, backingOffset);
|
||||
_mappings.Add(new RangeNode<ulong>(address, endAddress, backingOffset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +256,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
|
||||
try
|
||||
{
|
||||
UnmapViewInternal(sharedMemory, location, size, owner);
|
||||
UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -273,19 +275,20 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
/// <param name="location">Address to unmap</param>
|
||||
/// <param name="size">Size of the region to unmap in bytes</param>
|
||||
/// <param name="owner">Memory block that owns the mapping</param>
|
||||
/// <param name="updateProtection">Indicates if the memory protections should be updated after the unmap</param>
|
||||
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
|
||||
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner, bool updateProtection)
|
||||
{
|
||||
ulong startAddress = (ulong)location;
|
||||
ulong unmapSize = (ulong)size;
|
||||
ulong endAddress = startAddress + unmapSize;
|
||||
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||
var overlaps = new RangeNode<ulong>[InitialOverlapsSize];
|
||||
int count;
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
count = _mappings.Get(startAddress, endAddress, ref overlaps);
|
||||
count = _mappings.GetNodes(startAddress, endAddress, ref overlaps);
|
||||
}
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
@ -294,19 +297,14 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
|
||||
if (IsMapped(overlap.Value))
|
||||
{
|
||||
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
|
||||
ulong overlapStart = overlap.Start;
|
||||
ulong overlapEnd = overlap.End;
|
||||
ulong overlapValue = overlap.Value;
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
_mappings.Remove(overlap);
|
||||
_mappings.Add(overlapStart, overlapEnd, ulong.MaxValue);
|
||||
_mappings.Add(new RangeNode<ulong>(overlap.Start, overlap.End, ulong.MaxValue));
|
||||
}
|
||||
|
||||
bool overlapStartsBefore = overlapStart < startAddress;
|
||||
bool overlapEndsAfter = overlapEnd > endAddress;
|
||||
bool overlapStartsBefore = overlap.Start < startAddress;
|
||||
bool overlapEndsAfter = overlap.End > endAddress;
|
||||
|
||||
if (overlapStartsBefore || overlapEndsAfter)
|
||||
{
|
||||
@ -323,25 +321,25 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
partialUnmapState.PartialUnmapsCount++;
|
||||
|
||||
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlapStart, 2))
|
||||
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
|
||||
{
|
||||
throw new WindowsApiException("UnmapViewOfFile2");
|
||||
}
|
||||
|
||||
if (overlapStartsBefore)
|
||||
{
|
||||
ulong remapSize = startAddress - overlapStart;
|
||||
ulong remapSize = startAddress - overlap.Start;
|
||||
|
||||
MapViewInternal(sharedMemory, overlapValue, (IntPtr)overlapStart, (IntPtr)remapSize);
|
||||
RestoreRangeProtection(overlapStart, remapSize);
|
||||
MapViewInternal(sharedMemory, overlap.Value, (IntPtr)overlap.Start, (IntPtr)remapSize);
|
||||
RestoreRangeProtection(overlap.Start, remapSize);
|
||||
}
|
||||
|
||||
if (overlapEndsAfter)
|
||||
{
|
||||
ulong overlappedSize = endAddress - overlapStart;
|
||||
ulong remapBackingOffset = overlapValue + overlappedSize;
|
||||
ulong remapAddress = overlapStart + overlappedSize;
|
||||
ulong remapSize = overlapEnd - endAddress;
|
||||
ulong overlappedSize = endAddress - overlap.Start;
|
||||
ulong remapBackingOffset = overlap.Value + overlappedSize;
|
||||
ulong remapAddress = overlap.Start + overlappedSize;
|
||||
ulong remapSize = overlap.End - endAddress;
|
||||
|
||||
MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize);
|
||||
RestoreRangeProtection(remapAddress, remapSize);
|
||||
@ -352,7 +350,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
partialUnmapLock.DowngradeFromWriterLock();
|
||||
}
|
||||
}
|
||||
else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlapStart, 2))
|
||||
else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
|
||||
{
|
||||
throw new WindowsApiException("UnmapViewOfFile2");
|
||||
}
|
||||
@ -360,7 +358,11 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
}
|
||||
|
||||
CoalesceForUnmap(startAddress, unmapSize, owner);
|
||||
RemoveProtection(startAddress, unmapSize);
|
||||
|
||||
if (updateProtection)
|
||||
{
|
||||
UpdateProtection(startAddress, unmapSize, MemoryPermission.None);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -374,44 +376,58 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
ulong endAddress = address + size;
|
||||
ulong blockAddress = (ulong)owner.Pointer;
|
||||
ulong blockEnd = blockAddress + owner.Size;
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||
int unmappedCount = 0;
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
int count = _mappings.Get(
|
||||
Math.Max(address - MinimumPageSize, blockAddress),
|
||||
Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps);
|
||||
RangeNode<ulong> node = _mappings.GetNodeByKey(address);
|
||||
|
||||
if (count < 2)
|
||||
if (node == null)
|
||||
{
|
||||
// Nothing to coalesce if we only have 1 or no overlaps.
|
||||
// Nothing to coalesce if we have no overlaps.
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
RangeNode<ulong> predecessor = node.Predecessor;
|
||||
RangeNode<ulong> successor = null;
|
||||
|
||||
for (; node != null; node = successor)
|
||||
{
|
||||
var overlap = overlaps[index];
|
||||
successor = node.Successor;
|
||||
var overlap = node;
|
||||
|
||||
if (!IsMapped(overlap.Value))
|
||||
{
|
||||
if (address > overlap.Start)
|
||||
{
|
||||
address = overlap.Start;
|
||||
}
|
||||
|
||||
if (endAddress < overlap.End)
|
||||
{
|
||||
endAddress = overlap.End;
|
||||
}
|
||||
address = Math.Min(address, overlap.Start);
|
||||
endAddress = Math.Max(endAddress, overlap.End);
|
||||
|
||||
_mappings.Remove(overlap);
|
||||
|
||||
unmappedCount++;
|
||||
}
|
||||
|
||||
if (node.End >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_mappings.Add(address, endAddress, ulong.MaxValue);
|
||||
if (predecessor != null && !IsMapped(predecessor.Value) && predecessor.Start >= blockAddress)
|
||||
{
|
||||
address = Math.Min(address, predecessor.Start);
|
||||
|
||||
_mappings.Remove(predecessor);
|
||||
unmappedCount++;
|
||||
}
|
||||
|
||||
if (successor != null && !IsMapped(successor.Value) && successor.End <= blockEnd)
|
||||
{
|
||||
endAddress = Math.Max(endAddress, successor.End);
|
||||
|
||||
_mappings.Remove(successor);
|
||||
unmappedCount++;
|
||||
}
|
||||
|
||||
_mappings.Add(new RangeNode<ulong>(address, endAddress, ulong.MaxValue));
|
||||
}
|
||||
|
||||
if (unmappedCount > 1)
|
||||
@ -462,60 +478,55 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
ulong reprotectSize = (ulong)size;
|
||||
ulong endAddress = reprotectAddress + reprotectSize;
|
||||
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||
int count;
|
||||
bool success = true;
|
||||
|
||||
lock (_mappings)
|
||||
{
|
||||
count = _mappings.Get(reprotectAddress, endAddress, ref overlaps);
|
||||
}
|
||||
RangeNode<ulong> node = _mappings.GetNodeByKey(reprotectAddress);
|
||||
RangeNode<ulong> successorNode;
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
var overlap = overlaps[index];
|
||||
|
||||
ulong mappedAddress = overlap.Start;
|
||||
ulong mappedSize = overlap.End - overlap.Start;
|
||||
|
||||
if (mappedAddress < reprotectAddress)
|
||||
for (; node != null; node = successorNode)
|
||||
{
|
||||
ulong delta = reprotectAddress - mappedAddress;
|
||||
mappedAddress = reprotectAddress;
|
||||
mappedSize -= delta;
|
||||
}
|
||||
successorNode = node.Successor;
|
||||
var overlap = node;
|
||||
|
||||
ulong mappedEndAddress = mappedAddress + mappedSize;
|
||||
ulong mappedAddress = overlap.Start;
|
||||
ulong mappedSize = overlap.End - overlap.Start;
|
||||
|
||||
if (mappedEndAddress > endAddress)
|
||||
{
|
||||
ulong delta = mappedEndAddress - endAddress;
|
||||
mappedSize -= delta;
|
||||
}
|
||||
|
||||
if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _))
|
||||
{
|
||||
if (throwOnError)
|
||||
if (mappedAddress < reprotectAddress)
|
||||
{
|
||||
throw new WindowsApiException("VirtualProtect");
|
||||
ulong delta = reprotectAddress - mappedAddress;
|
||||
mappedAddress = reprotectAddress;
|
||||
mappedSize -= delta;
|
||||
}
|
||||
|
||||
success = false;
|
||||
}
|
||||
ulong mappedEndAddress = mappedAddress + mappedSize;
|
||||
|
||||
// We only keep track of "non-standard" protections,
|
||||
// that is, everything that is not just RW (which is the default when views are mapped).
|
||||
if (permission == MemoryPermission.ReadAndWrite)
|
||||
{
|
||||
RemoveProtection(mappedAddress, mappedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddProtection(mappedAddress, mappedSize, permission);
|
||||
if (mappedEndAddress > endAddress)
|
||||
{
|
||||
ulong delta = mappedEndAddress - endAddress;
|
||||
mappedSize -= delta;
|
||||
}
|
||||
|
||||
if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _))
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw new WindowsApiException("VirtualProtect");
|
||||
}
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (node.End >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateProtection(reprotectAddress, reprotectSize, permission);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -564,29 +575,30 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
/// <param name="address">Address of the protected region</param>
|
||||
/// <param name="size">Size of the protected region in bytes</param>
|
||||
/// <param name="permission">Memory permissions of the region</param>
|
||||
private void AddProtection(ulong address, ulong size, MemoryPermission permission)
|
||||
private void UpdateProtection(ulong address, ulong size, MemoryPermission permission)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
|
||||
int count;
|
||||
|
||||
lock (_protections)
|
||||
{
|
||||
count = _protections.Get(address, endAddress, ref overlaps);
|
||||
RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address);
|
||||
|
||||
if (count == 1 &&
|
||||
overlaps[0].Start <= address &&
|
||||
overlaps[0].End >= endAddress &&
|
||||
overlaps[0].Value == permission)
|
||||
if (node != null &&
|
||||
node.Start <= address &&
|
||||
node.End >= endAddress &&
|
||||
node.Value == permission)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RangeNode<MemoryPermission> successorNode;
|
||||
|
||||
ulong startAddress = address;
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
for (; node != null; node = successorNode)
|
||||
{
|
||||
var protection = overlaps[index];
|
||||
successorNode = node.Successor;
|
||||
var protection = node;
|
||||
|
||||
ulong protAddress = protection.Start;
|
||||
ulong protEndAddress = protection.End;
|
||||
@ -610,17 +622,22 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
if (startAddress > protAddress)
|
||||
{
|
||||
_protections.Add(protAddress, startAddress, protPermission);
|
||||
_protections.Add(new RangeNode<MemoryPermission>(protAddress, startAddress, protPermission));
|
||||
}
|
||||
|
||||
if (endAddress < protEndAddress)
|
||||
{
|
||||
_protections.Add(endAddress, protEndAddress, protPermission);
|
||||
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
|
||||
}
|
||||
}
|
||||
|
||||
if (node.End >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_protections.Add(startAddress, endAddress, permission);
|
||||
_protections.Add(new RangeNode<MemoryPermission>(startAddress, endAddress, permission));
|
||||
}
|
||||
}
|
||||
|
||||
@ -632,16 +649,16 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
private void RemoveProtection(ulong address, ulong size)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
|
||||
int count;
|
||||
|
||||
lock (_protections)
|
||||
{
|
||||
count = _protections.Get(address, endAddress, ref overlaps);
|
||||
RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address);
|
||||
RangeNode<MemoryPermission> successorNode;
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
for (; node != null; node = successorNode)
|
||||
{
|
||||
var protection = overlaps[index];
|
||||
successorNode = node.Successor;
|
||||
var protection = node;
|
||||
|
||||
ulong protAddress = protection.Start;
|
||||
ulong protEndAddress = protection.End;
|
||||
@ -651,12 +668,17 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
|
||||
if (address > protAddress)
|
||||
{
|
||||
_protections.Add(protAddress, address, protPermission);
|
||||
_protections.Add(new RangeNode<MemoryPermission>(protAddress, address, protPermission));
|
||||
}
|
||||
|
||||
if (endAddress < protEndAddress)
|
||||
{
|
||||
_protections.Add(endAddress, protEndAddress, protPermission);
|
||||
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
|
||||
}
|
||||
|
||||
if (node.End >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -670,12 +692,12 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
private void RestoreRangeProtection(ulong address, ulong size)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
|
||||
var overlaps = new RangeNode<MemoryPermission>[InitialOverlapsSize];
|
||||
int count;
|
||||
|
||||
lock (_protections)
|
||||
{
|
||||
count = _protections.Get(address, endAddress, ref overlaps);
|
||||
count = _protections.GetNodes(address, endAddress, ref overlaps);
|
||||
}
|
||||
|
||||
ulong startAddress = address;
|
||||
@ -684,6 +706,12 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
var protection = overlaps[index];
|
||||
|
||||
// If protection is R/W we don't need to reprotect as views are initially mapped as R/W.
|
||||
if (protection.Value == MemoryPermission.ReadAndWrite)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong protAddress = protection.Start;
|
||||
ulong protEndAddress = protection.End;
|
||||
|
||||
|
@ -12,12 +12,22 @@ namespace Ryujinx.Tests.Cpu
|
||||
#if Simd32
|
||||
|
||||
#region "ValueSource (Opcodes)"
|
||||
private static uint[] _Vabs_Vneg_V_()
|
||||
private static uint[] _Vabs_Vneg_Vpaddl_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf3b10300u, // VABS.S8 D0, D0
|
||||
0xf3b10380u // VNEG.S8 D0, D0
|
||||
0xf3b10300u, // VABS.S8 D0, D0
|
||||
0xf3b10380u, // VNEG.S8 D0, D0
|
||||
0xf3b00200u // VPADDL.S8 D0, D0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vabs_Vneg_F_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf3b90700u, // VABS.F32 D0, D0
|
||||
0xf3b90780u // VNEG.F32 D0, D0
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
@ -201,40 +211,14 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vabs_Vneg_V_S8_S16_S32([ValueSource("_Vabs_Vneg_V_")] uint opcode,
|
||||
[Range(0u, 3u)] uint rd,
|
||||
[Range(0u, 3u)] uint rm,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u)] uint size, // <S8, S16, S32>
|
||||
[Values] bool q)
|
||||
public void Vabs_Vneg_Vpaddl_V_I([ValueSource("_Vabs_Vneg_Vpaddl_I_")] uint opcode,
|
||||
[Range(0u, 3u)] uint rd,
|
||||
[Range(0u, 3u)] uint rm,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u)] uint size, // <S8, S16, S32>
|
||||
[Values] bool q)
|
||||
{
|
||||
const bool f = false;
|
||||
|
||||
Vabs_Vneg_V(opcode, rd, rm, z, b, size, f, q);
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vabs_Vneg_V_F32([ValueSource("_Vabs_Vneg_V_")] uint opcode,
|
||||
[Range(0u, 3u)] uint rd,
|
||||
[Range(0u, 3u)] uint rm,
|
||||
[ValueSource("_2S_F_")] ulong z,
|
||||
[ValueSource("_2S_F_")] ulong b,
|
||||
[Values] bool q)
|
||||
{
|
||||
const uint size = 0b10; // <F32>
|
||||
const bool f = true;
|
||||
|
||||
Vabs_Vneg_V(opcode, rd, rm, z, b, size, f, q);
|
||||
}
|
||||
|
||||
private void Vabs_Vneg_V(uint opcode, uint rd, uint rm, ulong z, ulong b, uint size, bool f, bool q)
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
opcode |= 1 << 10;
|
||||
}
|
||||
|
||||
if (q)
|
||||
{
|
||||
opcode |= 1 << 6;
|
||||
@ -256,6 +240,33 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vabs_Vneg_V_F32([ValueSource("_Vabs_Vneg_F_")] uint opcode,
|
||||
[Range(0u, 3u)] uint rd,
|
||||
[Range(0u, 3u)] uint rm,
|
||||
[ValueSource("_2S_F_")] ulong z,
|
||||
[ValueSource("_2S_F_")] ulong b,
|
||||
[Values] bool q)
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
opcode |= 1 << 6;
|
||||
|
||||
rd >>= 1; rd <<= 1;
|
||||
rm >>= 1; rm <<= 1;
|
||||
}
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, ~z);
|
||||
V128 v1 = MakeVectorE0E1(b, ~b);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VCNT.8 D0, D0 | VCNT.8 Q0, Q0")]
|
||||
public void Vcnt([Values(0u, 1u)] uint rd,
|
||||
[Values(0u, 1u)] uint rm,
|
||||
@ -283,6 +294,32 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vmovn_V([Range(0u, 3u)] uint rd,
|
||||
[Range(0u, 3u)] uint rm,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u, 3u)] uint op,
|
||||
[Values(0u, 1u, 2u)] uint size) // <S8, S16, S32>
|
||||
{
|
||||
rm >>= 1; rm <<= 1;
|
||||
|
||||
uint opcode = 0xf3b20200u; // VMOVN.S16 D0, Q0
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
opcode |= (op & 0x3) << 6;
|
||||
opcode |= (size & 0x3) << 18;
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, ~z);
|
||||
V128 v1 = MakeVectorE0E1(b, ~b);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,16 @@ namespace Ryujinx.Tests.Cpu
|
||||
#if SimdCvt32
|
||||
|
||||
#region "ValueSource (Opcodes)"
|
||||
private static uint[] _Vrint_AMNP_V_F32_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf3ba0500u, // VRINTA.F32 Q0, Q0
|
||||
0xf3ba0680u, // VRINTM.F32 Q0, Q0
|
||||
0xf3ba0400u, // VRINTN.F32 Q0, Q0
|
||||
0xf3ba0780u // VRINTP.F32 Q0, Q0
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "ValueSource (Types)"
|
||||
@ -64,6 +74,47 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ulong> _2S_F_()
|
||||
{
|
||||
yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue)
|
||||
yield return 0x8080000080800000ul; // -Min Normal
|
||||
yield return 0x807FFFFF807FFFFFul; // -Max Subnormal
|
||||
yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon)
|
||||
yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue)
|
||||
yield return 0x0080000000800000ul; // +Min Normal
|
||||
yield return 0x007FFFFF007FFFFFul; // +Max Subnormal
|
||||
yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon)
|
||||
|
||||
if (!NoZeros)
|
||||
{
|
||||
yield return 0x8000000080000000ul; // -Zero
|
||||
yield return 0x0000000000000000ul; // +Zero
|
||||
}
|
||||
|
||||
if (!NoInfs)
|
||||
{
|
||||
yield return 0xFF800000FF800000ul; // -Infinity
|
||||
yield return 0x7F8000007F800000ul; // +Infinity
|
||||
}
|
||||
|
||||
if (!NoNaNs)
|
||||
{
|
||||
yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN)
|
||||
yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload)
|
||||
yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN)
|
||||
yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload)
|
||||
}
|
||||
|
||||
for (int cnt = 1; cnt <= RndCnt; cnt++)
|
||||
{
|
||||
ulong rnd1 = GenNormalS();
|
||||
ulong rnd2 = GenSubnormalS();
|
||||
|
||||
yield return (rnd1 << 32) | rnd1;
|
||||
yield return (rnd2 << 32) | rnd2;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ulong> _1D_F_()
|
||||
{
|
||||
yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue)
|
||||
@ -224,6 +275,35 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise] [Explicit]
|
||||
public void Vrint_AMNP_V_F32([ValueSource(nameof(_Vrint_AMNP_V_F32_))] uint opcode,
|
||||
[Values(0u, 1u, 2u, 3u)] uint rd,
|
||||
[Values(0u, 1u, 2u, 3u)] uint rm,
|
||||
[ValueSource(nameof(_2S_F_))] ulong d0,
|
||||
[ValueSource(nameof(_2S_F_))] ulong d1,
|
||||
[ValueSource(nameof(_2S_F_))] ulong d2,
|
||||
[ValueSource(nameof(_2S_F_))] ulong d3,
|
||||
[Values] bool q)
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
opcode |= 1 << 6;
|
||||
|
||||
rd >>= 1; rd <<= 1;
|
||||
rm >>= 1; rm <<= 1;
|
||||
}
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(d0, d1);
|
||||
V128 v1 = MakeVectorE0E1(d2, d3);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VRINTX.F<size> <Sd>, <Sm>")]
|
||||
public void Vrintx_S([Values(0u, 1u)] uint rd,
|
||||
[Values(0u, 1u)] uint rm,
|
||||
@ -253,7 +333,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
|
||||
opcode |= ((size & 3) << 8);
|
||||
|
||||
|
||||
int fpscr = (int)rMode << (int)Fpcr.RMode;
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, fpscr: fpscr);
|
||||
|
||||
|
@ -12,11 +12,13 @@ namespace Ryujinx.Tests.Cpu
|
||||
#if SimdReg32
|
||||
|
||||
#region "ValueSource (Opcodes)"
|
||||
private static uint[] _V_Add_Sub_Wide_I_()
|
||||
private static uint[] _V_Add_Sub_Long_Wide_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2800000u, // VADDL.S8 Q0, D0, D0
|
||||
0xf2800100u, // VADDW.S8 Q0, Q0, D0
|
||||
0xf2800200u, // VSUBL.S8 Q0, D0, D0
|
||||
0xf2800300u // VSUBW.S8 Q0, Q0, D0
|
||||
};
|
||||
}
|
||||
@ -74,6 +76,15 @@ namespace Ryujinx.Tests.Cpu
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vmlal_Vmlsl_V_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2800800u, // VMLAL.S8 Q0, D0, D0
|
||||
0xf2800a00u // VMLSL.S8 Q0, D0, D0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vp_Add_Max_Min_F_()
|
||||
{
|
||||
return new uint[]
|
||||
@ -84,16 +95,30 @@ namespace Ryujinx.Tests.Cpu
|
||||
};
|
||||
}
|
||||
|
||||
// VPADD does not have an unsigned flag, so we check the opcode before setting it.
|
||||
private static uint VpaddI8 = 0xf2000b10u; // VPADD.I8 D0, D0, D0
|
||||
|
||||
private static uint[] _Vp_Add_Max_Min_I_()
|
||||
private static uint[] _Vp_Add_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
VpaddI8,
|
||||
0xf2000a00u, // VPMAX.S8 D0, D0, D0
|
||||
0xf2000a10u // VPMIN.S8 D0, D0, D0
|
||||
0xf2000b10u // VPADD.I8 D0, D0, D0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _V_Pmax_Pmin_Rhadd_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2000a00u, // VPMAX .S8 D0, D0, D0
|
||||
0xf2000a10u, // VPMIN .S8 D0, D0, D0
|
||||
0xf2000100u, // VRHADD.S8 D0, D0, D0
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vq_Add_Sub_I_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2000050u, // VQADD.S8 Q0, Q0, Q0
|
||||
0xf2000250u // VQSUB.S8 Q0, Q0, Q0
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
@ -350,7 +375,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
[Explicit]
|
||||
[Test, Pairwise, Description("VADD.f32 V0, V0, V0")]
|
||||
public void Vadd_f32([Values(0u)] uint rd,
|
||||
public void Vadd_F32([Values(0u)] uint rd,
|
||||
[Values(0u, 1u)] uint rn,
|
||||
[Values(0u, 2u)] uint rm,
|
||||
[ValueSource("_2S_F_")] ulong z0,
|
||||
@ -384,15 +409,15 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void V_Add_Sub_Wide_I([ValueSource("_V_Add_Sub_Wide_I_")] uint opcode,
|
||||
[Range(0u, 5u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u)] uint size, // <SU8, SU16, SU32>
|
||||
[Values] bool u) // <S, U>
|
||||
public void V_Add_Sub_Long_Wide_I([ValueSource("_V_Add_Sub_Long_Wide_I_")] uint opcode,
|
||||
[Range(0u, 5u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u)] uint size, // <SU8, SU16, SU32>
|
||||
[Values] bool u) // <S, U>
|
||||
{
|
||||
if (u)
|
||||
{
|
||||
@ -566,18 +591,17 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VMLSL.<type><size> <Vd>, <Vn>, <Vm>")]
|
||||
public void Vmlsl_I([Values(0u)] uint rd,
|
||||
[Values(1u, 0u)] uint rn,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
[Test, Pairwise]
|
||||
public void Vmlal_Vmlsl_I([ValueSource(nameof(_Vmlal_Vmlsl_V_I_))] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Values(1u, 0u)] uint rn,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
{
|
||||
uint opcode = 0xf2800a00u; // VMLSL.S8 Q0, D0, D0
|
||||
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
@ -736,17 +760,42 @@ namespace Ryujinx.Tests.Cpu
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vp_Add_Max_Min_I([ValueSource("_Vp_Add_Max_Min_I_")] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
public void Vp_Add_I([ValueSource("_Vp_Add_I_")] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b)
|
||||
{
|
||||
if (u && opcode != VpaddI8)
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
|
||||
opcode |= size << 20;
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, z);
|
||||
V128 v1 = MakeVectorE0E1(a, z);
|
||||
V128 v2 = MakeVectorE0E1(b, z);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void V_Pmax_Pmin_Rhadd_I([ValueSource("_V_Pmax_Pmin_Rhadd_I_")] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
{
|
||||
if (u)
|
||||
{
|
||||
opcode |= 1 << 24;
|
||||
}
|
||||
@ -765,6 +814,71 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vq_Add_Sub_I([ValueSource("_Vq_Add_Sub_I_")] uint opcode,
|
||||
[Range(0u, 5u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b,
|
||||
[Values(0u, 1u, 2u)] uint size, // <SU8, SU16, SU32>
|
||||
[Values] bool u) // <S, U>
|
||||
{
|
||||
if (u)
|
||||
{
|
||||
opcode |= 1 << 24;
|
||||
}
|
||||
|
||||
rd >>= 1; rd <<= 1;
|
||||
rn >>= 1; rn <<= 1;
|
||||
rm >>= 1; rm <<= 1;
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
opcode |= (size & 0x3) << 20;
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, ~z);
|
||||
V128 v1 = MakeVectorE0E1(a, ~a);
|
||||
V128 v2 = MakeVectorE0E1(b, ~b);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VQDMULH.<S16, S32> <Qd>, <Qn>, <Qm>")]
|
||||
public void Vqdmulh_I([Range(0u, 5u)] uint rd,
|
||||
[Range(0u, 5u)] uint rn,
|
||||
[Range(0u, 5u)] uint rm,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a,
|
||||
[ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b,
|
||||
[Values(1u, 2u)] uint size) // <S16, S32>
|
||||
{
|
||||
rd >>= 1; rd <<= 1;
|
||||
rn >>= 1; rn <<= 1;
|
||||
rm >>= 1; rm <<= 1;
|
||||
|
||||
uint opcode = 0xf2100b40u & ~(3u << 20); // VQDMULH.S16 Q0, Q0, Q0
|
||||
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
opcode |= (size & 0x3) << 20;
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z, ~z);
|
||||
V128 v1 = MakeVectorE0E1(a, ~a);
|
||||
V128 v2 = MakeVectorE0E1(b, ~b);
|
||||
|
||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,10 @@ namespace Ryujinx.Tests.Cpu
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2880010u, // VSHR.S8 D0, D0, #8
|
||||
0xf2880110u, // VSRA.S8 D0, D0, #8
|
||||
0xf2880210u, // VRSHR.S8 D0, D0, #8
|
||||
0xf2880010u // VSHR.S8 D0, D0, #8
|
||||
0xf2880310u // VRSRA.S8 D0, D0, #8
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,9 +52,10 @@ namespace Ryujinx.Tests.Cpu
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2900010u, // VSHR.S16 D0, D0, #16
|
||||
0xf2900110u, // VSRA.S16 D0, D0, #16
|
||||
0xf2900210u, // VRSHR.S16 D0, D0, #16
|
||||
0xf2900010u // VSHR.S16 D0, D0, #16
|
||||
0xf2900310u // VRSRA.S16 D0, D0, #16
|
||||
};
|
||||
}
|
||||
|
||||
@ -61,9 +63,10 @@ namespace Ryujinx.Tests.Cpu
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2a00010u, // VSHR.S32 D0, D0, #32
|
||||
0xf2a00110u, // VSRA.S32 D0, D0, #32
|
||||
0xf2a00210u, // VRSHR.S32 D0, D0, #32
|
||||
0xf2a00010u // VSHR.S32 D0, D0, #32
|
||||
0xf2a00310u // VRSRA.S32 D0, D0, #32
|
||||
};
|
||||
}
|
||||
|
||||
@ -76,6 +79,25 @@ namespace Ryujinx.Tests.Cpu
|
||||
0xf2800090u // VSHR.S64 D0, D0, #64
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vqshrn_Vqrshrn_Vrshrn_Imm_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf2800910u, // VORR.I16 D0, #0 (immediate value changes it into QSHRN)
|
||||
0xf2800950u, // VORR.I16 Q0, #0 (immediate value changes it into QRSHRN)
|
||||
0xf2800850u // VMOV.I16 Q0, #0 (immediate value changes it into RSHRN)
|
||||
};
|
||||
}
|
||||
|
||||
private static uint[] _Vqshrun_Vqrshrun_Imm_()
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
0xf3800810u, // VMOV.I16 D0, #0x80 (immediate value changes it into QSHRUN)
|
||||
0xf3800850u // VMOV.I16 Q0, #0x80 (immediate value changes it into QRSHRUN)
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
private const int RndCnt = 2;
|
||||
@ -230,18 +252,17 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VQRSHRN.<type><size> <Vd>, <Vm>, #<imm>")]
|
||||
public void Vqrshrn_Imm([Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCntShiftImm)] [Values(0u)] uint shiftImm,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
[Test, Pairwise]
|
||||
public void Vqshrn_Vqrshrn_Vrshrn_Imm([ValueSource("_Vqshrn_Vqrshrn_Vrshrn_Imm_")] uint opcode,
|
||||
[Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCntShiftImm)] [Values(0u)] uint shiftImm,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b,
|
||||
[Values] bool u)
|
||||
{
|
||||
uint opcode = 0xf2800950u; // VORR.I16 Q0, #0 (immediate value changes it into QRSHRN)
|
||||
|
||||
uint imm = 1u << ((int)size + 3);
|
||||
imm |= shiftImm & (imm - 1);
|
||||
|
||||
@ -265,17 +286,16 @@ namespace Ryujinx.Tests.Cpu
|
||||
CompareAgainstUnicorn(fpsrMask: Fpsr.Qc);
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("VQRSHRUN.<type><size> <Vd>, <Vm>, #<imm>")]
|
||||
public void Vqrshrun_Imm([Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCntShiftImm)] [Values(0u)] uint shiftImm,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b)
|
||||
[Test, Pairwise]
|
||||
public void Vqshrun_Vqrshrun_Imm([ValueSource("_Vqshrun_Vqrshrun_Imm_")] uint opcode,
|
||||
[Values(0u, 1u)] uint rd,
|
||||
[Values(2u, 0u)] uint rm,
|
||||
[Values(0u, 1u, 2u)] uint size,
|
||||
[Random(RndCntShiftImm)] [Values(0u)] uint shiftImm,
|
||||
[Random(RndCnt)] ulong z,
|
||||
[Random(RndCnt)] ulong a,
|
||||
[Random(RndCnt)] ulong b)
|
||||
{
|
||||
uint opcode = 0xf3800850u; // VMOV.I16 Q0, #0x80 (immediate value changes it into QRSHRUN)
|
||||
|
||||
uint imm = 1u << ((int)size + 3);
|
||||
imm |= shiftImm & (imm - 1);
|
||||
|
||||
|
Reference in New Issue
Block a user