aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Tests.Unicorn
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Tests.Unicorn')
-rw-r--r--Ryujinx.Tests.Unicorn/Native/Arm32Register.cs139
-rw-r--r--Ryujinx.Tests.Unicorn/UnicornAArch32.cs280
2 files changed, 419 insertions, 0 deletions
diff --git a/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs b/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs
new file mode 100644
index 00000000..f34b42a9
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/Native/Arm32Register.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ryujinx.Tests.Unicorn.Native
+{
+ public enum Arm32Register
+ {
+ INVALID = 0,
+
+ APSR,
+ APSR_NZCV,
+ CPSR,
+ FPEXC,
+ FPINST,
+ FPSCR,
+ FPSCR_NZCV,
+ FPSID,
+ ITSTATE,
+ LR,
+ PC,
+ SP,
+ SPSR,
+ D0,
+ D1,
+ D2,
+ D3,
+ D4,
+ D5,
+ D6,
+ D7,
+ D8,
+ D9,
+ D10,
+ D11,
+ D12,
+ D13,
+ D14,
+ D15,
+ D16,
+ D17,
+ D18,
+ D19,
+ D20,
+ D21,
+ D22,
+ D23,
+ D24,
+ D25,
+ D26,
+ D27,
+ D28,
+ D29,
+ D30,
+ D31,
+ FPINST2,
+ MVFR0,
+ MVFR1,
+ MVFR2,
+ Q0,
+ Q1,
+ Q2,
+ Q3,
+ Q4,
+ Q5,
+ Q6,
+ Q7,
+ Q8,
+ Q9,
+ Q10,
+ Q11,
+ Q12,
+ Q13,
+ Q14,
+ Q15,
+ R0,
+ R1,
+ R2,
+ R3,
+ R4,
+ R5,
+ R6,
+ R7,
+ R8,
+ R9,
+ R10,
+ R11,
+ R12,
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+ S12,
+ S13,
+ S14,
+ S15,
+ S16,
+ S17,
+ S18,
+ S19,
+ S20,
+ S21,
+ S22,
+ S23,
+ S24,
+ S25,
+ S26,
+ S27,
+ S28,
+ S29,
+ S30,
+ S31,
+ C1_C0_2,
+ C13_C0_2,
+ C13_C0_3,
+ IPSR,
+ MSP,
+ PSP,
+ CONTROL,
+ ENDING,
+
+ // Alias registers.
+ R13 = SP,
+ R14 = LR,
+ R15 = PC,
+ SB = R9,
+ SL = R10,
+ FP = R11,
+ IP = R12,
+ }
+}
diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch32.cs b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs
new file mode 100644
index 00000000..d7ae90d6
--- /dev/null
+++ b/Ryujinx.Tests.Unicorn/UnicornAArch32.cs
@@ -0,0 +1,280 @@
+using Ryujinx.Tests.Unicorn.Native;
+using System;
+
+namespace Ryujinx.Tests.Unicorn
+{
+ public class UnicornAArch32
+ {
+ internal readonly IntPtr uc;
+
+ public IndexedProperty<int, uint> R
+ {
+ get
+ {
+ return new IndexedProperty<int, uint>(
+ (int i) => GetX(i),
+ (int i, uint value) => SetX(i, value));
+ }
+ }
+
+ public IndexedProperty<int, SimdValue> Q
+ {
+ get
+ {
+ return new IndexedProperty<int, SimdValue>(
+ (int i) => GetQ(i),
+ (int i, SimdValue value) => SetQ(i, value));
+ }
+ }
+
+ public uint LR
+ {
+ get => GetRegister(Arm32Register.LR);
+ set => SetRegister(Arm32Register.LR, value);
+ }
+
+ public uint SP
+ {
+ get => GetRegister(Arm32Register.SP);
+ set => SetRegister(Arm32Register.SP, value);
+ }
+
+ public uint PC
+ {
+ get => GetRegister(Arm32Register.PC);
+ set => SetRegister(Arm32Register.PC, value);
+ }
+
+ public uint APSR
+ {
+ get => (uint)GetRegister(Arm32Register.APSR);
+ set => SetRegister(Arm32Register.APSR, (uint)value);
+ }
+
+ public int Fpscr
+ {
+ get => (int)GetRegister(Arm32Register.FPSCR) | ((int)GetRegister(Arm32Register.FPSCR_NZCV));
+ set => SetRegister(Arm32Register.FPSCR, (uint)value);
+ }
+
+ public bool OverflowFlag
+ {
+ get => (APSR & 0x10000000u) != 0;
+ set => APSR = (APSR & ~0x10000000u) | (value ? 0x10000000u : 0u);
+ }
+
+ public bool CarryFlag
+ {
+ get => (APSR & 0x20000000u) != 0;
+ set => APSR = (APSR & ~0x20000000u) | (value ? 0x20000000u : 0u);
+ }
+
+ public bool ZeroFlag
+ {
+ get => (APSR & 0x40000000u) != 0;
+ set => APSR = (APSR & ~0x40000000u) | (value ? 0x40000000u : 0u);
+ }
+
+ public bool NegativeFlag
+ {
+ get => (APSR & 0x80000000u) != 0;
+ set => APSR = (APSR & ~0x80000000u) | (value ? 0x80000000u : 0u);
+ }
+
+ public UnicornAArch32()
+ {
+ Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
+
+ SetRegister(Arm32Register.C1_C0_2, GetRegister(Arm32Register.C1_C0_2) | 0xf00000);
+ SetRegister(Arm32Register.FPEXC, 0x40000000);
+ }
+
+ ~UnicornAArch32()
+ {
+ Interface.Checked(Native.Interface.uc_close(uc));
+ }
+
+ public void RunForCount(ulong count)
+ {
+ Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
+ }
+
+ public void Step()
+ {
+ RunForCount(1);
+ }
+
+ private static Arm32Register[] XRegisters = new Arm32Register[16]
+ {
+ Arm32Register.R0,
+ Arm32Register.R1,
+ Arm32Register.R2,
+ Arm32Register.R3,
+ Arm32Register.R4,
+ Arm32Register.R5,
+ Arm32Register.R6,
+ Arm32Register.R7,
+ Arm32Register.R8,
+ Arm32Register.R9,
+ Arm32Register.R10,
+ Arm32Register.R11,
+ Arm32Register.R12,
+ Arm32Register.R13,
+ Arm32Register.R14,
+ Arm32Register.R15,
+ };
+
+ private static Arm32Register[] QRegisters = new Arm32Register[16]
+ {
+ Arm32Register.Q0,
+ Arm32Register.Q1,
+ Arm32Register.Q2,
+ Arm32Register.Q3,
+ Arm32Register.Q4,
+ Arm32Register.Q5,
+ Arm32Register.Q6,
+ Arm32Register.Q7,
+ Arm32Register.Q8,
+ Arm32Register.Q9,
+ Arm32Register.Q10,
+ Arm32Register.Q11,
+ Arm32Register.Q12,
+ Arm32Register.Q13,
+ Arm32Register.Q14,
+ Arm32Register.Q15
+ };
+
+ public uint GetX(int index)
+ {
+ if ((uint)index > 15)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ return GetRegister(XRegisters[index]);
+ }
+
+ public void SetX(int index, uint value)
+ {
+ if ((uint)index > 15)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ SetRegister(XRegisters[index], value);
+ }
+
+ public SimdValue GetQ(int index)
+ {
+ if ((uint)index > 15)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ // Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
+ return GetVector((Arm32Register)((int)Arm32Register.D0 + index * 2));
+ }
+
+ public void SetQ(int index, SimdValue value)
+ {
+ if ((uint)index > 15)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ SetVector((Arm32Register)((int)Arm32Register.D0 + index * 2), value);
+ }
+
+ public uint GetRegister(Arm32Register register)
+ {
+ byte[] data = new byte[4];
+
+ Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, data));
+
+ return (uint)BitConverter.ToInt32(data, 0);
+ }
+
+ public void SetRegister(Arm32Register register, uint value)
+ {
+ byte[] data = BitConverter.GetBytes(value);
+
+ Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
+ }
+
+ public SimdValue GetVector(Arm32Register register)
+ {
+ byte[] data = new byte[8];
+
+ Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
+ ulong lo = BitConverter.ToUInt64(data, 0);
+ Interface.Checked(Interface.uc_reg_read(uc, (int)register + 1, data));
+ ulong hi = BitConverter.ToUInt64(data, 0);
+
+ return new SimdValue(lo, hi);
+ }
+
+ private void SetVector(Arm32Register register, SimdValue value)
+ {
+ byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
+ Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
+ data = BitConverter.GetBytes(value.GetUInt64(1));
+ Interface.Checked(Interface.uc_reg_write(uc, (int)register + 1, data));
+ }
+
+ public byte[] MemoryRead(ulong address, ulong size)
+ {
+ byte[] value = new byte[size];
+
+ Interface.Checked(Interface.uc_mem_read(uc, address, value, size));
+
+ return value;
+ }
+
+ public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0];
+ public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0);
+ public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0);
+ public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0);
+
+ public void MemoryWrite(ulong address, byte[] value)
+ {
+ Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
+ }
+
+ public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new byte[] { value });
+ public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+ public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+ public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+ public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+ public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+ public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
+
+ public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
+ {
+ Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions));
+ }
+
+ public void MemoryUnmap(ulong address, ulong size)
+ {
+ Interface.Checked(Interface.uc_mem_unmap(uc, address, size));
+ }
+
+ public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
+ {
+ Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions));
+ }
+
+ public static bool IsAvailable()
+ {
+ try
+ {
+ Interface.uc_version(out _, out _);
+
+ return true;
+ }
+ catch (DllNotFoundException)
+ {
+ return false;
+ }
+ }
+ }
+}