diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/ARMeilleure/State | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/ARMeilleure/State')
| -rw-r--r-- | src/ARMeilleure/State/Aarch32Mode.cs | 15 | ||||
| -rw-r--r-- | src/ARMeilleure/State/ExceptionCallback.cs | 5 | ||||
| -rw-r--r-- | src/ARMeilleure/State/ExecutionContext.cs | 173 | ||||
| -rw-r--r-- | src/ARMeilleure/State/ExecutionMode.cs | 9 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPCR.cs | 22 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPException.cs | 12 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPRoundingMode.cs | 11 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPSCR.cs | 15 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPSR.cs | 18 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPState.cs | 31 | ||||
| -rw-r--r-- | src/ARMeilleure/State/FPType.cs | 11 | ||||
| -rw-r--r-- | src/ARMeilleure/State/ICounter.cs | 18 | ||||
| -rw-r--r-- | src/ARMeilleure/State/NativeContext.cs | 269 | ||||
| -rw-r--r-- | src/ARMeilleure/State/PState.cs | 17 | ||||
| -rw-r--r-- | src/ARMeilleure/State/RegisterAlias.cs | 42 | ||||
| -rw-r--r-- | src/ARMeilleure/State/RegisterConsts.cs | 15 | ||||
| -rw-r--r-- | src/ARMeilleure/State/V128.cs | 312 |
17 files changed, 995 insertions, 0 deletions
diff --git a/src/ARMeilleure/State/Aarch32Mode.cs b/src/ARMeilleure/State/Aarch32Mode.cs new file mode 100644 index 00000000..395e288a --- /dev/null +++ b/src/ARMeilleure/State/Aarch32Mode.cs @@ -0,0 +1,15 @@ +namespace ARMeilleure.State +{ + enum Aarch32Mode + { + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, + Supervisor = 0b10011, + Monitor = 0b10110, + Abort = 0b10111, + Hypervisor = 0b11010, + Undefined = 0b11011, + System = 0b11111 + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/ExceptionCallback.cs b/src/ARMeilleure/State/ExceptionCallback.cs new file mode 100644 index 00000000..38d6eef7 --- /dev/null +++ b/src/ARMeilleure/State/ExceptionCallback.cs @@ -0,0 +1,5 @@ +namespace ARMeilleure.State +{ + public delegate void ExceptionCallbackNoArgs(ExecutionContext context); + public delegate void ExceptionCallback(ExecutionContext context, ulong address, int id); +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs new file mode 100644 index 00000000..859fb3a5 --- /dev/null +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -0,0 +1,173 @@ +using ARMeilleure.Memory; +using System; + +namespace ARMeilleure.State +{ + public class ExecutionContext + { + private const int MinCountForCheck = 4000; + + private NativeContext _nativeContext; + + internal IntPtr NativeContextPtr => _nativeContext.BasePtr; + + private bool _interrupted; + + private readonly ICounter _counter; + + public ulong Pc => _nativeContext.GetPc(); + + public uint CtrEl0 => 0x8444c004; + public uint DczidEl0 => 0x00000004; + + public ulong CntfrqEl0 => _counter.Frequency; + public ulong CntpctEl0 => _counter.Counter; + + // CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2 + // Since EL2 isn't implemented, CNTVOFF_EL2 = 0 + public ulong CntvctEl0 => CntpctEl0; + + public long TpidrEl0 + { + get => _nativeContext.GetTpidrEl0(); + set => _nativeContext.SetTpidrEl0(value); + } + + public long TpidrroEl0 + { + get => _nativeContext.GetTpidrroEl0(); + set => _nativeContext.SetTpidrroEl0(value); + } + + public uint Pstate + { + get => _nativeContext.GetPstate(); + set => _nativeContext.SetPstate(value); + } + + public FPSR Fpsr + { + get => (FPSR)_nativeContext.GetFPState((uint)FPSR.Mask); + set => _nativeContext.SetFPState((uint)value, (uint)FPSR.Mask); + } + + public FPCR Fpcr + { + get => (FPCR)_nativeContext.GetFPState((uint)FPCR.Mask); + set => _nativeContext.SetFPState((uint)value, (uint)FPCR.Mask); + } + public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz; + + public FPSCR Fpscr + { + get => (FPSCR)_nativeContext.GetFPState((uint)FPSCR.Mask); + set => _nativeContext.SetFPState((uint)value, (uint)FPSCR.Mask); + } + + public bool IsAarch32 { get; set; } + + internal ExecutionMode ExecutionMode + { + get + { + if (IsAarch32) + { + return GetPstateFlag(PState.TFlag) + ? ExecutionMode.Aarch32Thumb + : ExecutionMode.Aarch32Arm; + } + else + { + return ExecutionMode.Aarch64; + } + } + } + + public bool Running + { + get => _nativeContext.GetRunning(); + private set => _nativeContext.SetRunning(value); + } + + private readonly ExceptionCallbackNoArgs _interruptCallback; + private readonly ExceptionCallback _breakCallback; + private readonly ExceptionCallback _supervisorCallback; + private readonly ExceptionCallback _undefinedCallback; + + public ExecutionContext( + IJitMemoryAllocator allocator, + ICounter counter, + ExceptionCallbackNoArgs interruptCallback = null, + ExceptionCallback breakCallback = null, + ExceptionCallback supervisorCallback = null, + ExceptionCallback undefinedCallback = null) + { + _nativeContext = new NativeContext(allocator); + _counter = counter; + _interruptCallback = interruptCallback; + _breakCallback = breakCallback; + _supervisorCallback = supervisorCallback; + _undefinedCallback = undefinedCallback; + + Running = true; + + _nativeContext.SetCounter(MinCountForCheck); + } + + public ulong GetX(int index) => _nativeContext.GetX(index); + public void SetX(int index, ulong value) => _nativeContext.SetX(index, value); + + public V128 GetV(int index) => _nativeContext.GetV(index); + public void SetV(int index, V128 value) => _nativeContext.SetV(index, value); + + public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag); + public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value); + + public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag); + public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value); + + internal void CheckInterrupt() + { + if (_interrupted) + { + _interrupted = false; + + _interruptCallback?.Invoke(this); + } + + _nativeContext.SetCounter(MinCountForCheck); + } + + public void RequestInterrupt() + { + _interrupted = true; + } + + internal void OnBreak(ulong address, int imm) + { + _breakCallback?.Invoke(this, address, imm); + } + + internal void OnSupervisorCall(ulong address, int imm) + { + _supervisorCallback?.Invoke(this, address, imm); + } + + internal void OnUndefined(ulong address, int opCode) + { + _undefinedCallback?.Invoke(this, address, opCode); + } + + public void StopRunning() + { + Running = false; + + _nativeContext.SetCounter(0); + } + + public void Dispose() + { + _nativeContext.Dispose(); + } + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/ExecutionMode.cs b/src/ARMeilleure/State/ExecutionMode.cs new file mode 100644 index 00000000..29154a25 --- /dev/null +++ b/src/ARMeilleure/State/ExecutionMode.cs @@ -0,0 +1,9 @@ +namespace ARMeilleure.State +{ + enum ExecutionMode : int + { + Aarch32Arm = 0, + Aarch32Thumb = 1, + Aarch64 = 2 + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/FPCR.cs b/src/ARMeilleure/State/FPCR.cs new file mode 100644 index 00000000..6f707de7 --- /dev/null +++ b/src/ARMeilleure/State/FPCR.cs @@ -0,0 +1,22 @@ +using System; + +namespace ARMeilleure.State +{ + [Flags] + public enum FPCR : uint + { + Ioe = 1u << 8, + Dze = 1u << 9, + Ofe = 1u << 10, + Ufe = 1u << 11, + Ixe = 1u << 12, + Ide = 1u << 15, + RMode0 = 1u << 22, + RMode1 = 1u << 23, + Fz = 1u << 24, + Dn = 1u << 25, + Ahp = 1u << 26, + + Mask = Ahp | Dn | Fz | RMode1 | RMode0 | Ide | Ixe | Ufe | Ofe | Dze | Ioe // 0x07C09F00u + } +} diff --git a/src/ARMeilleure/State/FPException.cs b/src/ARMeilleure/State/FPException.cs new file mode 100644 index 00000000..e24e07af --- /dev/null +++ b/src/ARMeilleure/State/FPException.cs @@ -0,0 +1,12 @@ +namespace ARMeilleure.State +{ + enum FPException + { + InvalidOp = 0, + DivideByZero = 1, + Overflow = 2, + Underflow = 3, + Inexact = 4, + InputDenorm = 7 + } +} diff --git a/src/ARMeilleure/State/FPRoundingMode.cs b/src/ARMeilleure/State/FPRoundingMode.cs new file mode 100644 index 00000000..8d757a15 --- /dev/null +++ b/src/ARMeilleure/State/FPRoundingMode.cs @@ -0,0 +1,11 @@ +namespace ARMeilleure.State +{ + public enum FPRoundingMode + { + ToNearest = 0, // With ties to even. + TowardsPlusInfinity = 1, + TowardsMinusInfinity = 2, + TowardsZero = 3, + ToNearestAway = 4 // With ties to away. + } +} diff --git a/src/ARMeilleure/State/FPSCR.cs b/src/ARMeilleure/State/FPSCR.cs new file mode 100644 index 00000000..d6d2fc26 --- /dev/null +++ b/src/ARMeilleure/State/FPSCR.cs @@ -0,0 +1,15 @@ +using System; + +namespace ARMeilleure.State +{ + [Flags] + public enum FPSCR : uint + { + V = 1u << 28, + C = 1u << 29, + Z = 1u << 30, + N = 1u << 31, + + Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask // 0xFFC09F9Fu + } +} diff --git a/src/ARMeilleure/State/FPSR.cs b/src/ARMeilleure/State/FPSR.cs new file mode 100644 index 00000000..5e66d5ce --- /dev/null +++ b/src/ARMeilleure/State/FPSR.cs @@ -0,0 +1,18 @@ +using System; + +namespace ARMeilleure.State +{ + [Flags] + public enum FPSR : uint + { + Ioc = 1u << 0, + Dzc = 1u << 1, + Ofc = 1u << 2, + Ufc = 1u << 3, + Ixc = 1u << 4, + Idc = 1u << 7, + Qc = 1u << 27, + + Mask = Qc | Idc | Ixc | Ufc | Ofc | Dzc | Ioc // 0x0800009Fu + } +} diff --git a/src/ARMeilleure/State/FPState.cs b/src/ARMeilleure/State/FPState.cs new file mode 100644 index 00000000..fa6ab9d4 --- /dev/null +++ b/src/ARMeilleure/State/FPState.cs @@ -0,0 +1,31 @@ +namespace ARMeilleure.State +{ + public enum FPState + { + // FPSR Flags. + IocFlag = 0, + DzcFlag = 1, + OfcFlag = 2, + UfcFlag = 3, + IxcFlag = 4, + IdcFlag = 7, + QcFlag = 27, + VFlag = 28, + CFlag = 29, + ZFlag = 30, + NFlag = 31, + + // FPCR Flags. + IoeFlag = 8, + DzeFlag = 9, + OfeFlag = 10, + UfeFlag = 11, + IxeFlag = 12, + IdeFlag = 15, + RMode0Flag = 22, + RMode1Flag = 23, + FzFlag = 24, + DnFlag = 25, + AhpFlag = 26 + } +} diff --git a/src/ARMeilleure/State/FPType.cs b/src/ARMeilleure/State/FPType.cs new file mode 100644 index 00000000..84e0db8d --- /dev/null +++ b/src/ARMeilleure/State/FPType.cs @@ -0,0 +1,11 @@ +namespace ARMeilleure.State +{ + enum FPType + { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN + } +} diff --git a/src/ARMeilleure/State/ICounter.cs b/src/ARMeilleure/State/ICounter.cs new file mode 100644 index 00000000..93e721ea --- /dev/null +++ b/src/ARMeilleure/State/ICounter.cs @@ -0,0 +1,18 @@ +namespace ARMeilleure.State +{ + /// <summary> + /// CPU Counter interface. + /// </summary> + public interface ICounter + { + /// <summary> + /// Counter frequency in Hertz. + /// </summary> + ulong Frequency { get; } + + /// <summary> + /// Current counter value. + /// </summary> + ulong Counter { get; } + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/NativeContext.cs b/src/ARMeilleure/State/NativeContext.cs new file mode 100644 index 00000000..3189bdd8 --- /dev/null +++ b/src/ARMeilleure/State/NativeContext.cs @@ -0,0 +1,269 @@ +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Memory; +using System; +using System.Runtime.CompilerServices; + +namespace ARMeilleure.State +{ + class NativeContext : IDisposable + { + private unsafe struct NativeCtxStorage + { + public fixed ulong X[RegisterConsts.IntRegsCount]; + public fixed ulong V[RegisterConsts.VecRegsCount * 2]; + public fixed uint Flags[RegisterConsts.FlagsCount]; + public fixed uint FpFlags[RegisterConsts.FpFlagsCount]; + public long TpidrEl0; + public long TpidrroEl0; + public int Counter; + public ulong DispatchAddress; + public ulong ExclusiveAddress; + public ulong ExclusiveValueLow; + public ulong ExclusiveValueHigh; + public int Running; + } + + private static NativeCtxStorage _dummyStorage = new NativeCtxStorage(); + + private readonly IJitMemoryBlock _block; + + public IntPtr BasePtr => _block.Pointer; + + public NativeContext(IJitMemoryAllocator allocator) + { + _block = allocator.Allocate((ulong)Unsafe.SizeOf<NativeCtxStorage>()); + + GetStorage().ExclusiveAddress = ulong.MaxValue; + } + + public ulong GetPc() + { + // TODO: More precise tracking of PC value. + return GetStorage().DispatchAddress; + } + + public unsafe ulong GetX(int index) + { + if ((uint)index >= RegisterConsts.IntRegsCount) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return GetStorage().X[index]; + } + + public unsafe void SetX(int index, ulong value) + { + if ((uint)index >= RegisterConsts.IntRegsCount) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + GetStorage().X[index] = value; + } + + public unsafe V128 GetV(int index) + { + if ((uint)index >= RegisterConsts.VecRegsCount) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]); + } + + public unsafe void SetV(int index, V128 value) + { + if ((uint)index >= RegisterConsts.VecRegsCount) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + GetStorage().V[index * 2 + 0] = value.Extract<ulong>(0); + GetStorage().V[index * 2 + 1] = value.Extract<ulong>(1); + } + + public unsafe bool GetPstateFlag(PState flag) + { + if ((uint)flag >= RegisterConsts.FlagsCount) + { + throw new ArgumentException($"Invalid flag \"{flag}\" specified."); + } + + return GetStorage().Flags[(int)flag] != 0; + } + + public unsafe void SetPstateFlag(PState flag, bool value) + { + if ((uint)flag >= RegisterConsts.FlagsCount) + { + throw new ArgumentException($"Invalid flag \"{flag}\" specified."); + } + + GetStorage().Flags[(int)flag] = value ? 1u : 0u; + } + + public unsafe uint GetPstate() + { + uint value = 0; + for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++) + { + value |= GetStorage().Flags[flag] != 0 ? 1u << flag : 0u; + } + return value; + } + + public unsafe void SetPstate(uint value) + { + for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++) + { + uint bit = 1u << flag; + GetStorage().Flags[flag] = (value & bit) == bit ? 1u : 0u; + } + } + + public unsafe bool GetFPStateFlag(FPState flag) + { + if ((uint)flag >= RegisterConsts.FpFlagsCount) + { + throw new ArgumentException($"Invalid flag \"{flag}\" specified."); + } + + return GetStorage().FpFlags[(int)flag] != 0; + } + + public unsafe void SetFPStateFlag(FPState flag, bool value) + { + if ((uint)flag >= RegisterConsts.FpFlagsCount) + { + throw new ArgumentException($"Invalid flag \"{flag}\" specified."); + } + + GetStorage().FpFlags[(int)flag] = value ? 1u : 0u; + } + + public unsafe uint GetFPState(uint mask = uint.MaxValue) + { + uint value = 0; + for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++) + { + uint bit = 1u << flag; + + if ((mask & bit) == bit) + { + value |= GetStorage().FpFlags[flag] != 0 ? bit : 0u; + } + } + return value; + } + + public unsafe void SetFPState(uint value, uint mask = uint.MaxValue) + { + for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++) + { + uint bit = 1u << flag; + + if ((mask & bit) == bit) + { + GetStorage().FpFlags[flag] = (value & bit) == bit ? 1u : 0u; + } + } + } + + public long GetTpidrEl0() => GetStorage().TpidrEl0; + public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value; + + public long GetTpidrroEl0() => GetStorage().TpidrroEl0; + public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value; + + public int GetCounter() => GetStorage().Counter; + public void SetCounter(int value) => GetStorage().Counter = value; + + public bool GetRunning() => GetStorage().Running != 0; + public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0; + + public unsafe static int GetRegisterOffset(Register reg) + { + if (reg.Type == RegisterType.Integer) + { + if ((uint)reg.Index >= RegisterConsts.IntRegsCount) + { + throw new ArgumentException("Invalid register."); + } + + return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[reg.Index]); + } + else if (reg.Type == RegisterType.Vector) + { + if ((uint)reg.Index >= RegisterConsts.VecRegsCount) + { + throw new ArgumentException("Invalid register."); + } + + return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[reg.Index * 2]); + } + else if (reg.Type == RegisterType.Flag) + { + if ((uint)reg.Index >= RegisterConsts.FlagsCount) + { + throw new ArgumentException("Invalid register."); + } + + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags[reg.Index]); + } + else /* if (reg.Type == RegisterType.FpFlag) */ + { + if ((uint)reg.Index >= RegisterConsts.FpFlagsCount) + { + throw new ArgumentException("Invalid register."); + } + + return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags[reg.Index]); + } + } + + public static int GetTpidrEl0Offset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0); + } + + public static int GetTpidrroEl0Offset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0); + } + + public static int GetCounterOffset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); + } + + public static int GetDispatchAddressOffset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.DispatchAddress); + } + + public static int GetExclusiveAddressOffset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveAddress); + } + + public static int GetExclusiveValueOffset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveValueLow); + } + + public static int GetRunningOffset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running); + } + + private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target) + { + return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target); + } + + private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer); + + public void Dispose() => _block.Dispose(); + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/PState.cs b/src/ARMeilleure/State/PState.cs new file mode 100644 index 00000000..9a80bc57 --- /dev/null +++ b/src/ARMeilleure/State/PState.cs @@ -0,0 +1,17 @@ +namespace ARMeilleure.State +{ + public enum PState + { + TFlag = 5, + EFlag = 9, + GE0Flag = 16, + GE1Flag = 17, + GE2Flag = 18, + GE3Flag = 19, + QFlag = 27, + VFlag = 28, + CFlag = 29, + ZFlag = 30, + NFlag = 31 + } +} diff --git a/src/ARMeilleure/State/RegisterAlias.cs b/src/ARMeilleure/State/RegisterAlias.cs new file mode 100644 index 00000000..7ebfa275 --- /dev/null +++ b/src/ARMeilleure/State/RegisterAlias.cs @@ -0,0 +1,42 @@ +namespace ARMeilleure.State +{ + static class RegisterAlias + { + public const int R8Usr = 8; + public const int R9Usr = 9; + public const int R10Usr = 10; + public const int R11Usr = 11; + public const int R12Usr = 12; + public const int SpUsr = 13; + public const int LrUsr = 14; + + public const int SpHyp = 15; + + public const int LrIrq = 16; + public const int SpIrq = 17; + + public const int LrSvc = 18; + public const int SpSvc = 19; + + public const int LrAbt = 20; + public const int SpAbt = 21; + + public const int LrUnd = 22; + public const int SpUnd = 23; + + public const int R8Fiq = 24; + public const int R9Fiq = 25; + public const int R10Fiq = 26; + public const int R11Fiq = 27; + public const int R12Fiq = 28; + public const int SpFiq = 29; + public const int LrFiq = 30; + + public const int Aarch32Sp = 13; + public const int Aarch32Lr = 14; + public const int Aarch32Pc = 15; + + public const int Lr = 30; + public const int Zr = 31; + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/RegisterConsts.cs b/src/ARMeilleure/State/RegisterConsts.cs new file mode 100644 index 00000000..d6294080 --- /dev/null +++ b/src/ARMeilleure/State/RegisterConsts.cs @@ -0,0 +1,15 @@ +namespace ARMeilleure.State +{ + static class RegisterConsts + { + public const int IntRegsCount = 32; + public const int VecRegsCount = 32; + public const int FlagsCount = 32; + public const int FpFlagsCount = 32; + public const int IntAndVecRegsCount = IntRegsCount + VecRegsCount; + public const int FpFlagsOffset = IntRegsCount + VecRegsCount + FlagsCount; + public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount + FpFlagsCount; + + public const int ZeroIndex = 31; + } +}
\ No newline at end of file diff --git a/src/ARMeilleure/State/V128.cs b/src/ARMeilleure/State/V128.cs new file mode 100644 index 00000000..3fa9f9a9 --- /dev/null +++ b/src/ARMeilleure/State/V128.cs @@ -0,0 +1,312 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ARMeilleure.State +{ + /// <summary> + /// Represents a 128-bit vector. + /// </summary> + [StructLayout(LayoutKind.Sequential, Size = 16)] + public struct V128 : IEquatable<V128> + { + // _e0 & _e1 could be marked as readonly, however they are not readonly because we modify them through the Unsafe + // APIs. This also means that one should be careful when changing the layout of this struct. + + private ulong _e0; + private ulong _e1; + + /// <summary> + /// Gets a new <see cref="V128"/> with all bits set to zero. + /// </summary> + public static V128 Zero => new V128(0, 0); + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="double"/> value + /// as a scalar. + /// </summary> + /// <param name="value">Scalar value</param> + public V128(double value) : this(value, 0) { } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="double"/> elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + public V128(double e0, double e1) + { + _e0 = (ulong)BitConverter.DoubleToInt64Bits(e0); + _e1 = (ulong)BitConverter.DoubleToInt64Bits(e1); + } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="float"/> value as a + /// scalar. + /// </summary> + /// <param name="value">Scalar value</param> + public V128(float value) : this(value, 0, 0, 0) { } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="float"/> elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + /// <param name="e2">Element 2</param> + /// <param name="e3">Element 3</param> + public V128(float e0, float e1, float e2, float e3) + { + _e0 = (ulong)(uint)BitConverter.SingleToInt32Bits(e0) << 0; + _e0 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e1) << 32; + _e1 = (ulong)(uint)BitConverter.SingleToInt32Bits(e2) << 0; + _e1 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e3) << 32; + } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="ulong"/> + /// elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + public V128(long e0, long e1) : this((ulong)e0, (ulong)e1) { } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="long"/> elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + public V128(ulong e0, ulong e1) + { + _e0 = e0; + _e1 = e1; + } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="int"/> elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + /// <param name="e2">Element 2</param> + /// <param name="e3">Element 3</param> + public V128(int e0, int e1, int e2, int e3) : this((uint)e0, (uint)e1, (uint)e2, (uint)e3) { } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="uint"/> elements. + /// </summary> + /// <param name="e0">Element 0</param> + /// <param name="e1">Element 1</param> + /// <param name="e2">Element 2</param> + /// <param name="e3">Element 3</param> + public V128(uint e0, uint e1, uint e2, uint e3) + { + _e0 = (ulong)e0 << 0; + _e0 |= (ulong)e1 << 32; + _e1 = (ulong)e2 << 0; + _e1 |= (ulong)e3 << 32; + } + + /// <summary> + /// Initializes a new instance of the <see cref="V128"/> struct from the specified <see cref="byte"/> array. + /// </summary> + /// <param name="data"><see cref="byte"/> array to use</param> + public V128(byte[] data) + { + _e0 = (ulong)BitConverter.ToInt64(data, 0); + _e1 = (ulong)BitConverter.ToInt64(data, 8); + } + + /// <summary> + /// Returns the value of the <see cref="V128"/> as a <typeparamref name="T"/> scalar. + /// </summary> + /// <typeparam name="T">Type of scalar</typeparam> + /// <returns>Value of the <see cref="V128"/> as a <typeparamref name="T"/> scalar</returns> + /// <exception cref="ArgumentOutOfRangeException">Size of <typeparamref name="T"/> is larger than 16 bytes</exception> + public T As<T>() where T : unmanaged + { + return Extract<T>(0); + } + + /// <summary> + /// Extracts the element at the specified index as a <typeparamref name="T"/> from the <see cref="V128"/>. + /// </summary> + /// <typeparam name="T">Element type</typeparam> + /// <param name="index">Index of element</param> + /// <returns>Element at the specified index as a <typeparamref name="T"/> from the <see cref="V128"/></returns> + /// <exception cref="ArgumentOutOfRangeException"> + /// <paramref name="index"/> is out of bound or the size of <typeparamref name="T"/> is larger than 16 bytes + /// </exception> + public T Extract<T>(int index) where T : unmanaged + { + if ((uint)index >= GetElementCount<T>()) + ThrowIndexOutOfRange(); + + // Performs: + // return *((*T)this + index); + return Unsafe.Add(ref Unsafe.As<V128, T>(ref this), index); + } + + /// <summary> + /// Inserts the specified value into the element at the specified index in the <see cref="V128"/>. + /// </summary> + /// <typeparam name="T">Element type</typeparam> + /// <param name="index">Index of element</param> + /// <param name="value">Value to insert</param> + /// <exception cref="ArgumentOutOfRangeException"> + /// <paramref name="index"/> is out of bound or the size of <typeparamref name="T"/> is larger than 16 bytes + /// </exception> + public void Insert<T>(int index, T value) where T : unmanaged + { + if ((uint)index >= GetElementCount<T>()) + ThrowIndexOutOfRange(); + + // Performs: + // *((*T)this + index) = value; + Unsafe.Add(ref Unsafe.As<V128, T>(ref this), index) = value; + } + + /// <summary> + /// Returns a new <see cref="byte"/> array which represents the <see cref="V128"/>. + /// </summary> + /// <returns>A new <see cref="byte"/> array which represents the <see cref="V128"/></returns> + public byte[] ToArray() + { + byte[] data = new byte[16]; + Span<byte> span = data; + + BitConverter.TryWriteBytes(span, _e0); + BitConverter.TryWriteBytes(span.Slice(8), _e1); + + return data; + } + + /// <summary> + /// Performs a bitwise logical left shift on the specified <see cref="V128"/> by the specified shift count. + /// </summary> + /// <param name="x"><see cref="V128"/> instance</param> + /// <param name="shift">Number of shifts</param> + /// <returns>Result of left shift</returns> + /// <remarks> + /// This supports shift counts up to 63; anything above may result in unexpected behaviour. + /// </remarks> + public static V128 operator <<(V128 x, int shift) + { + if (shift == 0) + { + return new V128(x._e0, x._e1); + } + + ulong shiftOut = x._e0 >> (64 - shift); + + return new V128(x._e0 << shift, (x._e1 << shift) | shiftOut); + } + + /// <summary> + /// Performs a bitwise logical right shift on the specified <see cref="V128"/> by the specified shift count. + /// </summary> + /// <param name="x"><see cref="V128"/> instance</param> + /// <param name="shift">Number of shifts</param> + /// <returns>Result of right shift</returns> + /// <remarks> + /// This supports shift counts up to 63; anything above may result in unexpected behaviour. + /// </remarks> + public static V128 operator >>(V128 x, int shift) + { + if (shift == 0) + { + return new V128(x._e0, x._e1); + } + + ulong shiftOut = x._e1 & ((1UL << shift) - 1); + + return new V128((x._e0 >> shift) | (shiftOut << (64 - shift)), x._e1 >> shift); + } + + /// <summary> + /// Performs a bitwise not on the specified <see cref="V128"/>. + /// </summary> + /// <param name="x">Target <see cref="V128"/></param> + /// <returns>Result of not operation</returns> + public static V128 operator ~(V128 x) => new V128(~x._e0, ~x._e1); + + /// <summary> + /// Performs a bitwise and on the specified <see cref="V128"/> instances. + /// </summary> + /// <param name="x">First instance</param> + /// <param name="y">Second instance</param> + /// <returns>Result of and operation</returns> + public static V128 operator &(V128 x, V128 y) => new V128(x._e0 & y._e0, x._e1 & y._e1); + + /// <summary> + /// Performs a bitwise or on the specified <see cref="V128"/> instances. + /// </summary> + /// <param name="x">First instance</param> + /// <param name="y">Second instance</param> + /// <returns>Result of or operation</returns> + public static V128 operator |(V128 x, V128 y) => new V128(x._e0 | y._e0, x._e1 | y._e1); + + /// <summary> + /// Performs a bitwise exlusive or on the specified <see cref="V128"/> instances. + /// </summary> + /// <param name="x">First instance</param> + /// <param name="y">Second instance</param> + /// <returns>Result of exclusive or operation</returns> + public static V128 operator ^(V128 x, V128 y) => new V128(x._e0 ^ y._e0, x._e1 ^ y._e1); + + /// <summary> + /// Determines if the specified <see cref="V128"/> instances are equal. + /// </summary> + /// <param name="x">First instance</param> + /// <param name="y">Second instance</param> + /// <returns>true if equal; otherwise false</returns> + public static bool operator ==(V128 x, V128 y) => x.Equals(y); + + /// <summary> + /// Determines if the specified <see cref="V128"/> instances are not equal. + /// </summary> + /// <param name="x">First instance</param> + /// <param name="y">Second instance</param> + /// <returns>true if not equal; otherwise false</returns> + public static bool operator !=(V128 x, V128 y) => !x.Equals(y); + + /// <summary> + /// Determines if the specified <see cref="V128"/> is equal to this <see cref="V128"/> instance. + /// </summary> + /// <param name="other">Other <see cref="V128"/> instance</param> + /// <returns>true if equal; otherwise false</returns> + public bool Equals(V128 other) + { + return other._e0 == _e0 && other._e1 == _e1; + } + + /// <summary> + /// Determines if the specified <see cref="object"/> is equal to this <see cref="V128"/> instance. + /// </summary> + /// <param name="obj">Other <see cref="object"/> instance</param> + /// <returns>true if equal; otherwise false</returns> + public override bool Equals(object obj) + { + return obj is V128 vector && Equals(vector); + } + + /// <inheritdoc/> + public override int GetHashCode() + { + return HashCode.Combine(_e0, _e1); + } + + /// <inheritdoc/> + public override string ToString() + { + return $"0x{_e1:X16}{_e0:X16}"; + } + + private uint GetElementCount<T>() where T : unmanaged + { + return (uint)(Unsafe.SizeOf<V128>() / Unsafe.SizeOf<T>()); + } + + private static void ThrowIndexOutOfRange() + { + throw new ArgumentOutOfRangeException("index"); + } + } +}
\ No newline at end of file |
