aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/State
diff options
context:
space:
mode:
Diffstat (limited to 'ARMeilleure/State')
-rw-r--r--ARMeilleure/State/Aarch32Mode.cs15
-rw-r--r--ARMeilleure/State/ExecutionContext.cs130
-rw-r--r--ARMeilleure/State/ExecutionMode.cs9
-rw-r--r--ARMeilleure/State/FPCR.cs23
-rw-r--r--ARMeilleure/State/FPException.cs12
-rw-r--r--ARMeilleure/State/FPRoundingMode.cs10
-rw-r--r--ARMeilleure/State/FPSR.cs11
-rw-r--r--ARMeilleure/State/FPType.cs11
-rw-r--r--ARMeilleure/State/IExecutionContext.cs37
-rw-r--r--ARMeilleure/State/InstExceptionEventArgs.cs16
-rw-r--r--ARMeilleure/State/InstUndefinedEventArgs.cs16
-rw-r--r--ARMeilleure/State/NativeContext.cs157
-rw-r--r--ARMeilleure/State/PState.cs16
-rw-r--r--ARMeilleure/State/RegisterAlias.cs41
-rw-r--r--ARMeilleure/State/RegisterConsts.cs13
-rw-r--r--ARMeilleure/State/V128.cs214
16 files changed, 731 insertions, 0 deletions
diff --git a/ARMeilleure/State/Aarch32Mode.cs b/ARMeilleure/State/Aarch32Mode.cs
new file mode 100644
index 00000000..395e288a
--- /dev/null
+++ b/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/ARMeilleure/State/ExecutionContext.cs b/ARMeilleure/State/ExecutionContext.cs
new file mode 100644
index 00000000..22cfcb69
--- /dev/null
+++ b/ARMeilleure/State/ExecutionContext.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Diagnostics;
+
+namespace ARMeilleure.State
+{
+ public class ExecutionContext : IExecutionContext
+ {
+ private const int MinCountForCheck = 40000;
+
+ private NativeContext _nativeContext;
+
+ internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
+
+ private bool _interrupted;
+
+ private static Stopwatch _tickCounter;
+
+ private static double _hostTickFreq;
+
+ public uint CtrEl0 => 0x8444c004;
+ public uint DczidEl0 => 0x00000004;
+
+ public ulong CntfrqEl0 { get; set; }
+ public ulong CntpctEl0
+ {
+ get
+ {
+ double ticks = _tickCounter.ElapsedTicks * _hostTickFreq;
+
+ return (ulong)(ticks * CntfrqEl0);
+ }
+ }
+
+ public long TpidrEl0 { get; set; }
+ public long Tpidr { get; set; }
+
+ public FPCR Fpcr { get; set; }
+ public FPSR Fpsr { get; set; }
+
+ 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; set; }
+
+ public event EventHandler<EventArgs> Interrupt;
+ public event EventHandler<InstExceptionEventArgs> Break;
+ public event EventHandler<InstExceptionEventArgs> SupervisorCall;
+ public event EventHandler<InstUndefinedEventArgs> Undefined;
+
+ static ExecutionContext()
+ {
+ _hostTickFreq = 1.0 / Stopwatch.Frequency;
+
+ _tickCounter = new Stopwatch();
+
+ _tickCounter.Start();
+ }
+
+ public ExecutionContext()
+ {
+ _nativeContext = new NativeContext();
+
+ 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);
+
+ internal void CheckInterrupt()
+ {
+ if (_interrupted)
+ {
+ _interrupted = false;
+
+ Interrupt?.Invoke(this, EventArgs.Empty);
+ }
+
+ _nativeContext.SetCounter(MinCountForCheck);
+ }
+
+ public void RequestInterrupt()
+ {
+ _interrupted = true;
+ }
+
+ internal void OnBreak(ulong address, int imm)
+ {
+ Break?.Invoke(this, new InstExceptionEventArgs(address, imm));
+ }
+
+ internal void OnSupervisorCall(ulong address, int imm)
+ {
+ SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
+ }
+
+ internal void OnUndefined(ulong address, int opCode)
+ {
+ Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
+ }
+
+ public void Dispose()
+ {
+ _nativeContext.Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/ExecutionMode.cs b/ARMeilleure/State/ExecutionMode.cs
new file mode 100644
index 00000000..eaed9d27
--- /dev/null
+++ b/ARMeilleure/State/ExecutionMode.cs
@@ -0,0 +1,9 @@
+namespace ARMeilleure.State
+{
+ enum ExecutionMode
+ {
+ Aarch32Arm,
+ Aarch32Thumb,
+ Aarch64
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/FPCR.cs b/ARMeilleure/State/FPCR.cs
new file mode 100644
index 00000000..511681fa
--- /dev/null
+++ b/ARMeilleure/State/FPCR.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ [Flags]
+ public enum FPCR
+ {
+ Ufe = 1 << 11,
+ Fz = 1 << 24,
+ Dn = 1 << 25,
+ Ahp = 1 << 26
+ }
+
+ public static class FPCRExtensions
+ {
+ private const int RModeShift = 22;
+
+ public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
+ {
+ return (FPRoundingMode)(((int)fpcr >> RModeShift) & 3);
+ }
+ }
+}
diff --git a/ARMeilleure/State/FPException.cs b/ARMeilleure/State/FPException.cs
new file mode 100644
index 00000000..e24e07af
--- /dev/null
+++ b/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/ARMeilleure/State/FPRoundingMode.cs b/ARMeilleure/State/FPRoundingMode.cs
new file mode 100644
index 00000000..ee4f8766
--- /dev/null
+++ b/ARMeilleure/State/FPRoundingMode.cs
@@ -0,0 +1,10 @@
+namespace ARMeilleure.State
+{
+ public enum FPRoundingMode
+ {
+ ToNearest = 0,
+ TowardsPlusInfinity = 1,
+ TowardsMinusInfinity = 2,
+ TowardsZero = 3
+ }
+}
diff --git a/ARMeilleure/State/FPSR.cs b/ARMeilleure/State/FPSR.cs
new file mode 100644
index 00000000..c20dc439
--- /dev/null
+++ b/ARMeilleure/State/FPSR.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ [Flags]
+ public enum FPSR
+ {
+ Ufc = 1 << 3,
+ Qc = 1 << 27
+ }
+}
diff --git a/ARMeilleure/State/FPType.cs b/ARMeilleure/State/FPType.cs
new file mode 100644
index 00000000..84e0db8d
--- /dev/null
+++ b/ARMeilleure/State/FPType.cs
@@ -0,0 +1,11 @@
+namespace ARMeilleure.State
+{
+ enum FPType
+ {
+ Nonzero,
+ Zero,
+ Infinity,
+ QNaN,
+ SNaN
+ }
+}
diff --git a/ARMeilleure/State/IExecutionContext.cs b/ARMeilleure/State/IExecutionContext.cs
new file mode 100644
index 00000000..df91b7a1
--- /dev/null
+++ b/ARMeilleure/State/IExecutionContext.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ public interface IExecutionContext : IDisposable
+ {
+ uint CtrEl0 { get; }
+ uint DczidEl0 { get; }
+
+ ulong CntfrqEl0 { get; set; }
+ ulong CntpctEl0 { get; }
+
+ long TpidrEl0 { get; set; }
+ long Tpidr { get; set; }
+
+ FPCR Fpcr { get; set; }
+ FPSR Fpsr { get; set; }
+
+ bool IsAarch32 { get; set; }
+
+ bool Running { get; set; }
+
+ event EventHandler<EventArgs> Interrupt;
+ event EventHandler<InstExceptionEventArgs> Break;
+ event EventHandler<InstExceptionEventArgs> SupervisorCall;
+ event EventHandler<InstUndefinedEventArgs> Undefined;
+
+ ulong GetX(int index);
+ void SetX(int index, ulong value);
+
+ V128 GetV(int index);
+
+ bool GetPstateFlag(PState flag);
+
+ void RequestInterrupt();
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/InstExceptionEventArgs.cs b/ARMeilleure/State/InstExceptionEventArgs.cs
new file mode 100644
index 00000000..c2460e4b
--- /dev/null
+++ b/ARMeilleure/State/InstExceptionEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ public class InstExceptionEventArgs : EventArgs
+ {
+ public ulong Address { get; }
+ public int Id { get; }
+
+ public InstExceptionEventArgs(ulong address, int id)
+ {
+ Address = address;
+ Id = id;
+ }
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/InstUndefinedEventArgs.cs b/ARMeilleure/State/InstUndefinedEventArgs.cs
new file mode 100644
index 00000000..c02b648e
--- /dev/null
+++ b/ARMeilleure/State/InstUndefinedEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ public class InstUndefinedEventArgs : EventArgs
+ {
+ public ulong Address { get; }
+ public int OpCode { get; }
+
+ public InstUndefinedEventArgs(ulong address, int opCode)
+ {
+ Address = address;
+ OpCode = opCode;
+ }
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/NativeContext.cs b/ARMeilleure/State/NativeContext.cs
new file mode 100644
index 00000000..4e6a5302
--- /dev/null
+++ b/ARMeilleure/State/NativeContext.cs
@@ -0,0 +1,157 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.State
+{
+ class NativeContext : IDisposable
+ {
+ private const int IntSize = 8;
+ private const int VecSize = 16;
+ private const int FlagSize = 4;
+ private const int ExtraSize = 4;
+
+ private const int TotalSize = RegisterConsts.IntRegsCount * IntSize +
+ RegisterConsts.VecRegsCount * VecSize +
+ RegisterConsts.FlagsCount * FlagSize + ExtraSize;
+
+ public IntPtr BasePtr { get; }
+
+ public NativeContext()
+ {
+ BasePtr = MemoryManagement.Allocate(TotalSize);
+ }
+
+ public ulong GetX(int index)
+ {
+ if ((uint)index >= RegisterConsts.IntRegsCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ return (ulong)Marshal.ReadInt64(BasePtr, index * IntSize);
+ }
+
+ public void SetX(int index, ulong value)
+ {
+ if ((uint)index >= RegisterConsts.IntRegsCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ Marshal.WriteInt64(BasePtr, index * IntSize, (long)value);
+ }
+
+ public V128 GetV(int index)
+ {
+ if ((uint)index >= RegisterConsts.IntRegsCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ int offset = RegisterConsts.IntRegsCount * IntSize + index * VecSize;
+
+ return new V128(
+ Marshal.ReadInt64(BasePtr, offset + 0),
+ Marshal.ReadInt64(BasePtr, offset + 8));
+ }
+
+ public void SetV(int index, V128 value)
+ {
+ if ((uint)index >= RegisterConsts.IntRegsCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ int offset = RegisterConsts.IntRegsCount * IntSize + index * VecSize;
+
+ Marshal.WriteInt64(BasePtr, offset + 0, value.GetInt64(0));
+ Marshal.WriteInt64(BasePtr, offset + 8, value.GetInt64(1));
+ }
+
+ public bool GetPstateFlag(PState flag)
+ {
+ if ((uint)flag >= RegisterConsts.FlagsCount)
+ {
+ throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
+ }
+
+ int offset =
+ RegisterConsts.IntRegsCount * IntSize +
+ RegisterConsts.VecRegsCount * VecSize + (int)flag * FlagSize;
+
+ int value = Marshal.ReadInt32(BasePtr, offset);
+
+ return value != 0;
+ }
+
+ public void SetPstateFlag(PState flag, bool value)
+ {
+ if ((uint)flag >= RegisterConsts.FlagsCount)
+ {
+ throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
+ }
+
+ int offset =
+ RegisterConsts.IntRegsCount * IntSize +
+ RegisterConsts.VecRegsCount * VecSize + (int)flag * FlagSize;
+
+ Marshal.WriteInt32(BasePtr, offset, value ? 1 : 0);
+ }
+
+ public int GetCounter()
+ {
+ return Marshal.ReadInt32(BasePtr, GetCounterOffset());
+ }
+
+ public void SetCounter(int value)
+ {
+ Marshal.WriteInt32(BasePtr, GetCounterOffset(), value);
+ }
+
+ public static int GetRegisterOffset(Register reg)
+ {
+ int offset, size;
+
+ if (reg.Type == RegisterType.Integer)
+ {
+ offset = reg.Index * IntSize;
+
+ size = IntSize;
+ }
+ else if (reg.Type == RegisterType.Vector)
+ {
+ offset = RegisterConsts.IntRegsCount * IntSize + reg.Index * VecSize;
+
+ size = VecSize;
+ }
+ else /* if (reg.Type == RegisterType.Flag) */
+ {
+ offset = RegisterConsts.IntRegsCount * IntSize +
+ RegisterConsts.VecRegsCount * VecSize + reg.Index * FlagSize;
+
+ size = FlagSize;
+ }
+
+ if ((uint)(offset + size) > (uint)TotalSize)
+ {
+ throw new ArgumentException("Invalid register.");
+ }
+
+ return offset;
+ }
+
+ public static int GetCounterOffset()
+ {
+ return RegisterConsts.IntRegsCount * IntSize +
+ RegisterConsts.VecRegsCount * VecSize +
+ RegisterConsts.FlagsCount * FlagSize;
+ }
+
+ public void Dispose()
+ {
+ MemoryManagement.Free(BasePtr);
+ }
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/PState.cs b/ARMeilleure/State/PState.cs
new file mode 100644
index 00000000..ce755e95
--- /dev/null
+++ b/ARMeilleure/State/PState.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ [Flags]
+ public enum PState
+ {
+ TFlag = 5,
+ EFlag = 9,
+
+ VFlag = 28,
+ CFlag = 29,
+ ZFlag = 30,
+ NFlag = 31
+ }
+}
diff --git a/ARMeilleure/State/RegisterAlias.cs b/ARMeilleure/State/RegisterAlias.cs
new file mode 100644
index 00000000..ae0d4562
--- /dev/null
+++ b/ARMeilleure/State/RegisterAlias.cs
@@ -0,0 +1,41 @@
+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 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/ARMeilleure/State/RegisterConsts.cs b/ARMeilleure/State/RegisterConsts.cs
new file mode 100644
index 00000000..a85117bb
--- /dev/null
+++ b/ARMeilleure/State/RegisterConsts.cs
@@ -0,0 +1,13 @@
+namespace ARMeilleure.State
+{
+ static class RegisterConsts
+ {
+ public const int IntRegsCount = 32;
+ public const int VecRegsCount = 32;
+ public const int FlagsCount = 32;
+ public const int IntAndVecRegsCount = IntRegsCount + VecRegsCount;
+ public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount;
+
+ public const int ZeroIndex = 31;
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/State/V128.cs b/ARMeilleure/State/V128.cs
new file mode 100644
index 00000000..eeb9ff1c
--- /dev/null
+++ b/ARMeilleure/State/V128.cs
@@ -0,0 +1,214 @@
+using System;
+
+namespace ARMeilleure.State
+{
+ public struct V128 : IEquatable<V128>
+ {
+ private ulong _e0;
+ private ulong _e1;
+
+ private static V128 _zero = new V128(0, 0);
+
+ public static V128 Zero => _zero;
+
+ public V128(float value) : this(value, 0, 0, 0) { }
+
+ public V128(double value) : this(value, 0) { }
+
+ 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;
+ }
+
+ public V128(double e0, double e1)
+ {
+ _e0 = (ulong)BitConverter.DoubleToInt64Bits(e0);
+ _e1 = (ulong)BitConverter.DoubleToInt64Bits(e1);
+ }
+
+ public V128(int e0, int e1, int e2, int e3)
+ {
+ _e0 = (ulong)(uint)e0 << 0;
+ _e0 |= (ulong)(uint)e1 << 32;
+ _e1 = (ulong)(uint)e2 << 0;
+ _e1 |= (ulong)(uint)e3 << 32;
+ }
+
+ 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;
+ }
+
+ public V128(long e0, long e1)
+ {
+ _e0 = (ulong)e0;
+ _e1 = (ulong)e1;
+ }
+
+ public V128(ulong e0, ulong e1)
+ {
+ _e0 = e0;
+ _e1 = e1;
+ }
+
+ public V128(byte[] data)
+ {
+ _e0 = (ulong)BitConverter.ToInt64(data, 0);
+ _e1 = (ulong)BitConverter.ToInt64(data, 8);
+ }
+
+ public void Insert(int index, uint value)
+ {
+ switch (index)
+ {
+ case 0: _e0 = (_e0 & 0xffffffff00000000) | ((ulong)value << 0); break;
+ case 1: _e0 = (_e0 & 0x00000000ffffffff) | ((ulong)value << 32); break;
+ case 2: _e1 = (_e1 & 0xffffffff00000000) | ((ulong)value << 0); break;
+ case 3: _e1 = (_e1 & 0x00000000ffffffff) | ((ulong)value << 32); break;
+
+ default: throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+
+ public void Insert(int index, ulong value)
+ {
+ switch (index)
+ {
+ case 0: _e0 = value; break;
+ case 1: _e1 = value; break;
+
+ default: throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+
+ public float AsFloat()
+ {
+ return GetFloat(0);
+ }
+
+ public double AsDouble()
+ {
+ return GetDouble(0);
+ }
+
+ public float GetFloat(int index)
+ {
+ return BitConverter.Int32BitsToSingle(GetInt32(index));
+ }
+
+ public double GetDouble(int index)
+ {
+ return BitConverter.Int64BitsToDouble(GetInt64(index));
+ }
+
+ public int GetInt32(int index) => (int)GetUInt32(index);
+ public long GetInt64(int index) => (long)GetUInt64(index);
+
+ public uint GetUInt32(int index)
+ {
+ switch (index)
+ {
+ case 0: return (uint)(_e0 >> 0);
+ case 1: return (uint)(_e0 >> 32);
+ case 2: return (uint)(_e1 >> 0);
+ case 3: return (uint)(_e1 >> 32);
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ public ulong GetUInt64(int index)
+ {
+ switch (index)
+ {
+ case 0: return _e0;
+ case 1: return _e1;
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ public byte[] ToArray()
+ {
+ byte[] e0Data = BitConverter.GetBytes(_e0);
+ byte[] e1Data = BitConverter.GetBytes(_e1);
+
+ byte[] data = new byte[16];
+
+ Buffer.BlockCopy(e0Data, 0, data, 0, 8);
+ Buffer.BlockCopy(e1Data, 0, data, 8, 8);
+
+ return data;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_e0, _e1);
+ }
+
+ public static V128 operator ~(V128 x)
+ {
+ return new V128(~x._e0, ~x._e1);
+ }
+
+ public static V128 operator &(V128 x, V128 y)
+ {
+ return new V128(x._e0 & y._e0, x._e1 & y._e1);
+ }
+
+ public static V128 operator |(V128 x, V128 y)
+ {
+ return new V128(x._e0 | y._e0, x._e1 | y._e1);
+ }
+
+ public static V128 operator ^(V128 x, V128 y)
+ {
+ return new V128(x._e0 ^ y._e0, x._e1 ^ y._e1);
+ }
+
+ public static V128 operator <<(V128 x, int shift)
+ {
+ ulong shiftOut = x._e0 >> (64 - shift);
+
+ return new V128(x._e0 << shift, (x._e1 << shift) | shiftOut);
+ }
+
+ public static V128 operator >>(V128 x, int shift)
+ {
+ ulong shiftOut = x._e1 & ((1UL << shift) - 1);
+
+ return new V128((x._e0 >> shift) | (shiftOut << (64 - shift)), x._e1 >> shift);
+ }
+
+ public static bool operator ==(V128 x, V128 y)
+ {
+ return x.Equals(y);
+ }
+
+ public static bool operator !=(V128 x, V128 y)
+ {
+ return !x.Equals(y);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is V128 vector && Equals(vector);
+ }
+
+ public bool Equals(V128 other)
+ {
+ return other._e0 == _e0 && other._e1 == _e1;
+ }
+
+ public override string ToString()
+ {
+ return $"0x{_e1:X16}{_e0:X16}";
+ }
+ }
+} \ No newline at end of file