aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Tests.Unicorn
diff options
context:
space:
mode:
authorMerry <MerryMage@users.noreply.github.com>2018-09-01 15:24:05 +0100
committergdkchan <gab.dark.100@gmail.com>2018-09-01 11:24:05 -0300
commit326777ca4a68b38c7a5e44c76291f09f07ddcf2e (patch)
tree4049b9229ed326c9bb809a93778e6377d51ce209 /Ryujinx.Tests.Unicorn
parent42dc925c3da59bf8801b14779482ee5bd9c25dc0 (diff)
Ryujinx.Tests: Add unicorn to test framework (#389)
* Ryujinx.Tests: Add unicorn to test framework * CpuTestSimdArithmetic: Comment out inaccurate results
Diffstat (limited to 'Ryujinx.Tests.Unicorn')
-rw-r--r--Ryujinx.Tests.Unicorn/IndexedProperty.cs28
-rw-r--r--Ryujinx.Tests.Unicorn/MemoryPermission.cs13
-rw-r--r--Ryujinx.Tests.Unicorn/Native/ArmRegister.cs296
-rw-r--r--Ryujinx.Tests.Unicorn/Native/Interface.cs68
-rw-r--r--Ryujinx.Tests.Unicorn/Native/UnicornArch.cs16
-rw-r--r--Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs13
-rw-r--r--Ryujinx.Tests.Unicorn/Native/UnicornMode.cs34
-rw-r--r--Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj18
-rw-r--r--Ryujinx.Tests.Unicorn/UnicornAArch64.cs315
-rw-r--r--Ryujinx.Tests.Unicorn/UnicornError.cs30
-rw-r--r--Ryujinx.Tests.Unicorn/UnicornException.cs23
-rw-r--r--Ryujinx.Tests.Unicorn/libs/README.md3
-rw-r--r--Ryujinx.Tests.Unicorn/libs/windows/unicorn.dllbin0 -> 4588032 bytes
13 files changed, 857 insertions, 0 deletions
diff --git a/Ryujinx.Tests.Unicorn/IndexedProperty.cs b/Ryujinx.Tests.Unicorn/IndexedProperty.cs
new file mode 100644
index 00000000..946620c6
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/IndexedProperty.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public class IndexedProperty<TIndex, TValue>
+ {
+ readonly Action<TIndex, TValue> SetAction;
+ readonly Func<TIndex, TValue> GetFunc;
+
+ public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
+ {
+ this.GetFunc = getFunc;
+ this.SetAction = setAction;
+ }
+
+ public TValue this[TIndex i]
+ {
+ get
+ {
+ return GetFunc(i);
+ }
+ set
+ {
+ SetAction(i, value);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/Ryujinx.Tests.Unicorn/MemoryPermission.cs
new file mode 100644
index 00000000..d79b2add
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/MemoryPermission.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public enum MemoryPermission
+ {
+ NONE = 0,
+ READ = 1,
+ WRITE = 2,
+ EXEC = 4,
+ ALL = 7,
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs
new file mode 100644
index 00000000..3554480c
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs
@@ -0,0 +1,296 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ public enum ArmRegister
+ {
+ INVALID = 0,
+
+ X29,
+ X30,
+ NZCV,
+ SP,
+ WSP,
+ WZR,
+ XZR,
+ B0,
+ B1,
+ B2,
+ B3,
+ B4,
+ B5,
+ B6,
+ B7,
+ B8,
+ B9,
+ B10,
+ B11,
+ B12,
+ B13,
+ B14,
+ B15,
+ B16,
+ B17,
+ B18,
+ B19,
+ B20,
+ B21,
+ B22,
+ B23,
+ B24,
+ B25,
+ B26,
+ B27,
+ B28,
+ B29,
+ B30,
+ B31,
+ D0,
+ D1,
+ D2,
+ D3,
+ D4,
+ D5,
+ D6,
+ D7,
+ D8,
+ D9,
+ D10,
+ D11,
+ D12,
+ D13,
+ D14,
+ D15,
+ D16,
+ D17,
+ D18,
+ D19,
+ D20,
+ D21,
+ D22,
+ D23,
+ D24,
+ D25,
+ D26,
+ D27,
+ D28,
+ D29,
+ D30,
+ D31,
+ H0,
+ H1,
+ H2,
+ H3,
+ H4,
+ H5,
+ H6,
+ H7,
+ H8,
+ H9,
+ H10,
+ H11,
+ H12,
+ H13,
+ H14,
+ H15,
+ H16,
+ H17,
+ H18,
+ H19,
+ H20,
+ H21,
+ H22,
+ H23,
+ H24,
+ H25,
+ H26,
+ H27,
+ H28,
+ H29,
+ H30,
+ H31,
+ Q0,
+ Q1,
+ Q2,
+ Q3,
+ Q4,
+ Q5,
+ Q6,
+ Q7,
+ Q8,
+ Q9,
+ Q10,
+ Q11,
+ Q12,
+ Q13,
+ Q14,
+ Q15,
+ Q16,
+ Q17,
+ Q18,
+ Q19,
+ Q20,
+ Q21,
+ Q22,
+ Q23,
+ Q24,
+ Q25,
+ Q26,
+ Q27,
+ Q28,
+ Q29,
+ Q30,
+ Q31,
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+ S12,
+ S13,
+ S14,
+ S15,
+ S16,
+ S17,
+ S18,
+ S19,
+ S20,
+ S21,
+ S22,
+ S23,
+ S24,
+ S25,
+ S26,
+ S27,
+ S28,
+ S29,
+ S30,
+ S31,
+ W0,
+ W1,
+ W2,
+ W3,
+ W4,
+ W5,
+ W6,
+ W7,
+ W8,
+ W9,
+ W10,
+ W11,
+ W12,
+ W13,
+ W14,
+ W15,
+ W16,
+ W17,
+ W18,
+ W19,
+ W20,
+ W21,
+ W22,
+ W23,
+ W24,
+ W25,
+ W26,
+ W27,
+ W28,
+ W29,
+ W30,
+ X0,
+ X1,
+ X2,
+ X3,
+ X4,
+ X5,
+ X6,
+ X7,
+ X8,
+ X9,
+ X10,
+ X11,
+ X12,
+ X13,
+ X14,
+ X15,
+ X16,
+ X17,
+ X18,
+ X19,
+ X20,
+ X21,
+ X22,
+ X23,
+ X24,
+ X25,
+ X26,
+ X27,
+ X28,
+
+ V0,
+ V1,
+ V2,
+ V3,
+ V4,
+ V5,
+ V6,
+ V7,
+ V8,
+ V9,
+ V10,
+ V11,
+ V12,
+ V13,
+ V14,
+ V15,
+ V16,
+ V17,
+ V18,
+ V19,
+ V20,
+ V21,
+ V22,
+ V23,
+ V24,
+ V25,
+ V26,
+ V27,
+ V28,
+ V29,
+ V30,
+ V31,
+
+ //> pseudo registers
+ PC, // program counter register
+
+ CPACR_EL1,
+ ESR,
+
+ //> thread registers
+ TPIDR_EL0,
+ TPIDRRO_EL0,
+ TPIDR_EL1,
+
+ PSTATE, // PSTATE pseudoregister
+
+ //> floating point control and status registers
+ FPCR,
+ FPSR,
+
+ ENDING, // <-- mark the end of the list of registers
+
+ //> alias registers
+
+ IP0 = X16,
+ IP1 = X17,
+ FP = X29,
+ LR = X30,
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs
new file mode 100644
index 00000000..a6563220
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/Interface.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Runtime.InteropServices;
+using Ryujinx.Tests.Unicorn;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ public class Interface
+ {
+ public static void Checked(UnicornError error)
+ {
+ if (error != UnicornError.UC_ERR_OK)
+ {
+ throw new UnicornException(error);
+ }
+ }
+
+ public static void MarshalArrayOf<T>(IntPtr input, int length, out T[] output)
+ {
+ var size = Marshal.SizeOf(typeof(T));
+ output = new T[length];
+
+ for (int i = 0; i < length; i++)
+ {
+ IntPtr item = new IntPtr(input.ToInt64() + i * size);
+ output[i] = Marshal.PtrToStructure<T>(item);
+ }
+ }
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern uint uc_version(out uint major, out uint minor);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_open(uint arch, uint mode, out IntPtr uc);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_close(IntPtr uc);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr uc_strerror(UnicornError err);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
+
+ [DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
+ public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs
new file mode 100644
index 00000000..be088366
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ public enum UnicornArch
+ {
+ UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
+ UC_ARCH_ARM64, // ARM-64, also called AArch64
+ UC_ARCH_MIPS, // Mips architecture
+ UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
+ UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
+ UC_ARCH_SPARC, // Sparc architecture
+ UC_ARCH_M68K, // M68K architecture
+ UC_ARCH_MAX,
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs
new file mode 100644
index 00000000..7ee34a74
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct UnicornMemoryRegion
+ {
+ public UInt64 begin; // begin address of the region (inclusive)
+ public UInt64 end; // end address of the region (inclusive)
+ public UInt32 perms; // memory permissions of the region
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs
new file mode 100644
index 00000000..950583bd
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ public enum UnicornMode
+ {
+ UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
+ UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
+ // arm / arm64
+ UC_MODE_ARM = 0, // ARM mode
+ UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
+ UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
+ UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
+ // mips
+ UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
+ UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
+ UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
+ UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
+ UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
+ // x86 / x64
+ UC_MODE_16 = 1 << 1, // 16-bit mode
+ UC_MODE_32 = 1 << 2, // 32-bit mode
+ UC_MODE_64 = 1 << 3, // 64-bit mode
+ // ppc
+ UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
+ UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
+ UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
+ // sparc
+ UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
+ UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
+ UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
+ // m68k
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
new file mode 100644
index 00000000..ee7c103d
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.1</TargetFramework>
+ <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
+ <PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs
new file mode 100644
index 00000000..1cd3671f
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs
@@ -0,0 +1,315 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public class UnicornAArch64
+ {
+ internal readonly IntPtr uc;
+
+ public IndexedProperty<int, ulong> X
+ {
+ get
+ {
+ return new IndexedProperty<int, ulong>(
+ (int i) => GetX(i),
+ (int i, ulong value) => SetX(i, value));
+ }
+ }
+
+ public IndexedProperty<int, Vector128<float>> Q
+ {
+ get
+ {
+ return new IndexedProperty<int, Vector128<float>>(
+ (int i) => GetQ(i),
+ (int i, Vector128<float> value) => SetQ(i, value));
+ }
+ }
+
+ public ulong LR
+ {
+ get { return GetRegister(Native.ArmRegister.LR); }
+ set { SetRegister(Native.ArmRegister.LR, value); }
+ }
+
+ public ulong SP
+ {
+ get { return GetRegister(Native.ArmRegister.SP); }
+ set { SetRegister(Native.ArmRegister.SP, value); }
+ }
+
+ public ulong PC
+ {
+ get { return GetRegister(Native.ArmRegister.PC); }
+ set { SetRegister(Native.ArmRegister.PC, value); }
+ }
+
+ public uint Pstate
+ {
+ get { return (uint)GetRegister(Native.ArmRegister.PSTATE); }
+ set { SetRegister(Native.ArmRegister.PSTATE, (uint)value); }
+ }
+
+ public int Fpcr
+ {
+ get { return (int)GetRegister(Native.ArmRegister.FPCR); }
+ set { SetRegister(Native.ArmRegister.FPCR, (uint)value); }
+ }
+
+ public int Fpsr
+ {
+ get { return (int)GetRegister(Native.ArmRegister.FPSR); }
+ set { SetRegister(Native.ArmRegister.FPSR, (uint)value); }
+ }
+
+ public bool OverflowFlag
+ {
+ get { return (Pstate & 0x10000000u) != 0; }
+ set { Pstate = (Pstate & ~0x10000000u) | (value ? 0x10000000u : 0u); }
+ }
+
+ public bool CarryFlag
+ {
+ get { return (Pstate & 0x20000000u) != 0; }
+ set { Pstate = (Pstate & ~0x20000000u) | (value ? 0x20000000u : 0u); }
+ }
+
+ public bool ZeroFlag
+ {
+ get { return (Pstate & 0x40000000u) != 0; }
+ set { Pstate = (Pstate & ~0x40000000u) | (value ? 0x40000000u : 0u); }
+ }
+
+ public bool NegativeFlag
+ {
+ get { return (Pstate & 0x80000000u) != 0; }
+ set { Pstate = (Pstate & ~0x80000000u) | (value ? 0x80000000u : 0u); }
+ }
+
+ public UnicornAArch64()
+ {
+ Native.Interface.Checked(Native.Interface.uc_open((uint)Native.UnicornArch.UC_ARCH_ARM64, (uint)Native.UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
+ SetRegister(Native.ArmRegister.CPACR_EL1, 0x00300000);
+ }
+
+ ~UnicornAArch64()
+ {
+ Native.Interface.Checked(Native.Interface.uc_close(uc));
+ }
+
+ public void RunForCount(ulong count)
+ {
+ Native.Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
+ }
+
+ public void Step()
+ {
+ RunForCount(1);
+ }
+
+ internal static Native.ArmRegister[] X_registers = new Native.ArmRegister[31]
+ {
+ Native.ArmRegister.X0,
+ Native.ArmRegister.X1,
+ Native.ArmRegister.X2,
+ Native.ArmRegister.X3,
+ Native.ArmRegister.X4,
+ Native.ArmRegister.X5,
+ Native.ArmRegister.X6,
+ Native.ArmRegister.X7,
+ Native.ArmRegister.X8,
+ Native.ArmRegister.X9,
+ Native.ArmRegister.X10,
+ Native.ArmRegister.X11,
+ Native.ArmRegister.X12,
+ Native.ArmRegister.X13,
+ Native.ArmRegister.X14,
+ Native.ArmRegister.X15,
+ Native.ArmRegister.X16,
+ Native.ArmRegister.X17,
+ Native.ArmRegister.X18,
+ Native.ArmRegister.X19,
+ Native.ArmRegister.X20,
+ Native.ArmRegister.X21,
+ Native.ArmRegister.X22,
+ Native.ArmRegister.X23,
+ Native.ArmRegister.X24,
+ Native.ArmRegister.X25,
+ Native.ArmRegister.X26,
+ Native.ArmRegister.X27,
+ Native.ArmRegister.X28,
+ Native.ArmRegister.X29,
+ Native.ArmRegister.X30,
+ };
+
+ internal static Native.ArmRegister[] Q_registers = new Native.ArmRegister[32]
+ {
+ Native.ArmRegister.Q0,
+ Native.ArmRegister.Q1,
+ Native.ArmRegister.Q2,
+ Native.ArmRegister.Q3,
+ Native.ArmRegister.Q4,
+ Native.ArmRegister.Q5,
+ Native.ArmRegister.Q6,
+ Native.ArmRegister.Q7,
+ Native.ArmRegister.Q8,
+ Native.ArmRegister.Q9,
+ Native.ArmRegister.Q10,
+ Native.ArmRegister.Q11,
+ Native.ArmRegister.Q12,
+ Native.ArmRegister.Q13,
+ Native.ArmRegister.Q14,
+ Native.ArmRegister.Q15,
+ Native.ArmRegister.Q16,
+ Native.ArmRegister.Q17,
+ Native.ArmRegister.Q18,
+ Native.ArmRegister.Q19,
+ Native.ArmRegister.Q20,
+ Native.ArmRegister.Q21,
+ Native.ArmRegister.Q22,
+ Native.ArmRegister.Q23,
+ Native.ArmRegister.Q24,
+ Native.ArmRegister.Q25,
+ Native.ArmRegister.Q26,
+ Native.ArmRegister.Q27,
+ Native.ArmRegister.Q28,
+ Native.ArmRegister.Q29,
+ Native.ArmRegister.Q30,
+ Native.ArmRegister.Q31,
+ };
+
+ internal ulong GetRegister(Native.ArmRegister register)
+ {
+ byte[] value_bytes = new byte[8];
+ Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
+ return (ulong)BitConverter.ToInt64(value_bytes, 0);
+ }
+
+ internal void SetRegister(Native.ArmRegister register, ulong value)
+ {
+ byte[] value_bytes = BitConverter.GetBytes(value);
+ Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
+ }
+
+ internal Vector128<float> GetVector(Native.ArmRegister register)
+ {
+ byte[] value_bytes = new byte[16];
+ Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
+ unsafe
+ {
+ fixed (byte* p = &value_bytes[0])
+ {
+ return Sse.LoadVector128((float*)p);
+ }
+ }
+ }
+
+ internal void SetVector(Native.ArmRegister register, Vector128<float> value)
+ {
+ byte[] value_bytes = new byte[16];
+ unsafe
+ {
+ fixed (byte* p = &value_bytes[0])
+ {
+ Sse.Store((float*)p, value);
+ }
+ }
+ Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
+ }
+
+ public ulong GetX(int index)
+ {
+ Contract.Requires(index <= 30, "invalid register");
+
+ return GetRegister(X_registers[index]);
+ }
+
+ public void SetX(int index, ulong value)
+ {
+ Contract.Requires(index <= 30, "invalid register");
+
+ SetRegister(X_registers[index], value);
+ }
+
+ public Vector128<float> GetQ(int index)
+ {
+ Contract.Requires(index <= 31, "invalid vector");
+
+ return GetVector(Q_registers[index]);
+ }
+
+ public void SetQ(int index, Vector128<float> value)
+ {
+ Contract.Requires(index <= 31, "invalid vector");
+
+ SetVector(Q_registers[index], value);
+ }
+
+ public byte[] MemoryRead(ulong address, ulong size)
+ {
+ byte[] value = new byte[size];
+ Native.Interface.Checked(Native.Interface.uc_mem_read(uc, address, value, size));
+ return value;
+ }
+
+ public byte MemoryRead8 (ulong address) { return MemoryRead(address, 1)[0]; }
+ public UInt16 MemoryRead16(ulong address) { return (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); }
+ public UInt32 MemoryRead32(ulong address) { return (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); }
+ public UInt64 MemoryRead64(ulong address) { return (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); }
+
+ public void MemoryWrite(ulong address, byte[] value)
+ {
+ Native.Interface.Checked(Native.Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
+ }
+
+ public void MemoryWrite8 (ulong address, byte value) { MemoryWrite(address, new byte[]{value}); }
+ public void MemoryWrite16(ulong address, Int16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+ public void MemoryWrite16(ulong address, UInt16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+ public void MemoryWrite32(ulong address, Int32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+ public void MemoryWrite32(ulong address, UInt32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+ public void MemoryWrite64(ulong address, Int64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+ public void MemoryWrite64(ulong address, UInt64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
+
+ public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
+ {
+ Native.Interface.Checked(Native.Interface.uc_mem_map(uc, address, size, (uint)permissions));
+ }
+
+ public void MemoryUnmap(ulong address, ulong size)
+ {
+ Native.Interface.Checked(Native.Interface.uc_mem_unmap(uc, address, size));
+ }
+
+ public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
+ {
+ Native.Interface.Checked(Native.Interface.uc_mem_protect(uc, address, size, (uint)permissions));
+ }
+
+ public void DumpMemoryInformation()
+ {
+ Native.Interface.Checked(Native.Interface.uc_mem_regions(uc, out IntPtr regions_raw, out uint length));
+ Native.Interface.MarshalArrayOf<Native.UnicornMemoryRegion>(regions_raw, (int)length, out var regions);
+ foreach (var region in regions)
+ {
+ Console.WriteLine("region: begin {0:X16} end {1:X16} perms {2:X8}", region.begin, region.end, region.perms);
+ }
+ }
+
+ public static bool IsAvailable()
+ {
+ try
+ {
+ Native.Interface.uc_version(out uint major, out uint minor);
+ return true;
+ }
+ catch (DllNotFoundException)
+ {
+ return false;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Tests.Unicorn/UnicornError.cs b/Ryujinx.Tests.Unicorn/UnicornError.cs
new file mode 100644
index 00000000..db56c615
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/UnicornError.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public enum UnicornError
+ {
+ UC_ERR_OK = 0, // No error: everything was fine
+ UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
+ UC_ERR_ARCH, // Unsupported architecture: uc_open()
+ UC_ERR_HANDLE, // Invalid handle
+ UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
+ UC_ERR_VERSION, // Unsupported version (bindings)
+ UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
+ UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
+ UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
+ UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
+ UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
+ UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
+ UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
+ UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
+ UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
+ UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API)
+ UC_ERR_READ_UNALIGNED, // Unaligned read
+ UC_ERR_WRITE_UNALIGNED, // Unaligned write
+ UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
+ UC_ERR_HOOK_EXIST, // hook for this event already existed
+ UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
+ UC_ERR_EXCEPTION // Unhandled CPU exception
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/UnicornException.cs b/Ryujinx.Tests.Unicorn/UnicornException.cs
new file mode 100644
index 00000000..3cb69370
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/UnicornException.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public class UnicornException : Exception
+ {
+ public readonly UnicornError Error;
+
+ internal UnicornException(UnicornError error)
+ {
+ Error = error;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error));
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/libs/README.md b/Ryujinx.Tests.Unicorn/libs/README.md
new file mode 100644
index 00000000..bb370164
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/libs/README.md
@@ -0,0 +1,3 @@
+The pre-compiled dynamic libraries in this directory are licenced under the GPLv2.
+
+The source code for windows/unicorn.dll is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960
diff --git a/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll
new file mode 100644
index 00000000..0e3cea58
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll
Binary files differ