aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Instruction
diff options
context:
space:
mode:
authoremmauss <emmausssss@gmail.com>2018-02-20 22:09:23 +0200
committergdkchan <gab.dark.100@gmail.com>2018-02-20 17:09:23 -0300
commit62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch)
tree0e5c55b341aee4db0ccb841a084f253ec5e05657 /ChocolArm64/Instruction
parentcb665bb715834526d73c9469d16114b287faaecd (diff)
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'ChocolArm64/Instruction')
-rw-r--r--ChocolArm64/Instruction/AInst.cs18
-rw-r--r--ChocolArm64/Instruction/AInstEmitAlu.cs364
-rw-r--r--ChocolArm64/Instruction/AInstEmitAluHelper.cs168
-rw-r--r--ChocolArm64/Instruction/AInstEmitBfm.cs217
-rw-r--r--ChocolArm64/Instruction/AInstEmitCcmp.cs81
-rw-r--r--ChocolArm64/Instruction/AInstEmitCsel.cs59
-rw-r--r--ChocolArm64/Instruction/AInstEmitException.cs65
-rw-r--r--ChocolArm64/Instruction/AInstEmitFlow.cs124
-rw-r--r--ChocolArm64/Instruction/AInstEmitMemory.cs252
-rw-r--r--ChocolArm64/Instruction/AInstEmitMemoryEx.cs180
-rw-r--r--ChocolArm64/Instruction/AInstEmitMemoryHelper.cs138
-rw-r--r--ChocolArm64/Instruction/AInstEmitMove.cs41
-rw-r--r--ChocolArm64/Instruction/AInstEmitMul.cs80
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs367
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdCmp.cs233
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdCvt.cs444
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdHelper.cs616
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdLogical.cs69
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdMemory.cs184
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdMove.cs333
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdShift.cs297
-rw-r--r--ChocolArm64/Instruction/AInstEmitSystem.cs122
-rw-r--r--ChocolArm64/Instruction/AInstEmitter.cs6
-rw-r--r--ChocolArm64/Instruction/ASoftFallback.cs316
24 files changed, 4774 insertions, 0 deletions
diff --git a/ChocolArm64/Instruction/AInst.cs b/ChocolArm64/Instruction/AInst.cs
new file mode 100644
index 00000000..cab597d6
--- /dev/null
+++ b/ChocolArm64/Instruction/AInst.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace ChocolArm64.Instruction
+{
+ struct AInst
+ {
+ public AInstEmitter Emitter { get; private set; }
+ public Type Type { get; private set; }
+
+ public static AInst Undefined => new AInst(AInstEmit.Und, null);
+
+ public AInst(AInstEmitter Emitter, Type Type)
+ {
+ this.Emitter = Emitter;
+ this.Type = Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs
new file mode 100644
index 00000000..72903f5b
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitAlu.cs
@@ -0,0 +1,364 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitAluHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Adc(AILEmitterCtx Context)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitLdflg((int)APState.CBit);
+
+ Type[] MthdTypes = new Type[] { typeof(bool) };
+
+ MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes);
+
+ Context.EmitCall(MthdInfo);
+
+ if (Context.CurrOp.RegisterSize != ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_I8);
+ }
+
+ Context.Emit(OpCodes.Add);
+
+ EmitDataStore(Context);
+ }
+
+ public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add);
+
+ public static void Adds(AILEmitterCtx Context)
+ {
+ Context.TryOptMarkCondWithoutCmp();
+
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitZNFlagCheck();
+
+ EmitAddsCCheck(Context);
+ EmitAddsVCheck(Context);
+ EmitDataStoreS(Context);
+ }
+
+ public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And);
+
+ public static void Ands(AILEmitterCtx Context)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.And);
+
+ EmitZeroCVFlags(Context);
+
+ Context.EmitZNFlagCheck();
+
+ EmitDataStoreS(Context);
+ }
+
+ public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr);
+
+ public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false);
+ public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true);
+
+ private static void EmitBic(AILEmitterCtx Context, bool SetFlags)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.And);
+
+ if (SetFlags)
+ {
+ EmitZeroCVFlags(Context);
+
+ Context.EmitZNFlagCheck();
+ }
+
+ EmitDataStore(Context, SetFlags);
+ }
+
+ public static void Clz(AILEmitterCtx Context)
+ {
+ AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ if (Op.RegisterSize == ARegisterSize.Int32)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros32));
+ }
+ else
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros64));
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Eon(AILEmitterCtx Context)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.Xor);
+
+ EmitDataStore(Context);
+ }
+
+ public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor);
+
+ public static void Extr(AILEmitterCtx Context)
+ {
+ //TODO: Ensure that the Shift is valid for the Is64Bits.
+ AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rm);
+
+ if (Op.Shift > 0)
+ {
+ Context.EmitLdc_I4(Op.Shift);
+
+ Context.Emit(OpCodes.Shr_Un);
+
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift);
+
+ Context.Emit(OpCodes.Shl);
+ Context.Emit(OpCodes.Or);
+ }
+
+ EmitDataStore(Context);
+ }
+
+ public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl);
+ public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un);
+
+ public static void Sbc(AILEmitterCtx Context)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Sub);
+
+ Context.EmitLdflg((int)APState.CBit);
+
+ Type[] MthdTypes = new Type[] { typeof(bool) };
+
+ MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes);
+
+ Context.EmitCall(MthdInfo);
+
+ Context.EmitLdc_I4(1);
+
+ Context.Emit(OpCodes.Xor);
+
+ if (Context.CurrOp.RegisterSize != ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_I8);
+ }
+
+ Context.Emit(OpCodes.Sub);
+
+ EmitDataStore(Context);
+ }
+
+ public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub);
+
+ public static void Subs(AILEmitterCtx Context)
+ {
+ Context.TryOptMarkCondWithoutCmp();
+
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Sub);
+
+ Context.EmitZNFlagCheck();
+
+ EmitSubsCCheck(Context);
+ EmitSubsVCheck(Context);
+ EmitDataStoreS(Context);
+ }
+
+ public static void Orn(AILEmitterCtx Context)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.Or);
+
+ EmitDataStore(Context);
+ }
+
+ public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or);
+
+ public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context,
+ nameof(ASoftFallback.ReverseBits32),
+ nameof(ASoftFallback.ReverseBits64));
+
+ public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context,
+ nameof(ASoftFallback.ReverseBytes16_32),
+ nameof(ASoftFallback.ReverseBytes16_64));
+
+ public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context,
+ nameof(ASoftFallback.ReverseBytes32_32),
+ nameof(ASoftFallback.ReverseBytes32_64));
+
+ public static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64)
+ {
+ AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ if (Op.RegisterSize == ARegisterSize.Int32)
+ {
+ ASoftFallback.EmitCall(Context, Name32);
+ }
+ else
+ {
+ ASoftFallback.EmitCall(Context, Name64);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Rev64(AILEmitterCtx Context)
+ {
+ AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64));
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Rorv(AILEmitterCtx Context)
+ {
+ EmitDataLoadRn(Context);
+ EmitDataLoadShift(Context);
+
+ Context.Emit(OpCodes.Shr_Un);
+
+ EmitDataLoadRn(Context);
+
+ Context.EmitLdc_I4(Context.CurrOp.GetBitsCount());
+
+ EmitDataLoadShift(Context);
+
+ Context.Emit(OpCodes.Sub);
+ Context.Emit(OpCodes.Shl);
+ Context.Emit(OpCodes.Or);
+
+ EmitDataStore(Context);
+ }
+
+ public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div);
+ public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un);
+
+ private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp)
+ {
+ //If Rm == 0, Rd = 0 (division by zero).
+ Context.EmitLdc_I(0);
+
+ EmitDataLoadRm(Context);
+
+ Context.EmitLdc_I(0);
+
+ AILLabel BadDiv = new AILLabel();
+
+ Context.Emit(OpCodes.Beq_S, BadDiv);
+ Context.Emit(OpCodes.Pop);
+
+ if (ILOp == OpCodes.Div)
+ {
+ //If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
+ long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1);
+
+ Context.EmitLdc_I(IntMin);
+
+ EmitDataLoadRn(Context);
+
+ Context.EmitLdc_I(IntMin);
+
+ Context.Emit(OpCodes.Ceq);
+
+ EmitDataLoadRm(Context);
+
+ Context.EmitLdc_I(-1);
+
+ Context.Emit(OpCodes.Ceq);
+ Context.Emit(OpCodes.And);
+ Context.Emit(OpCodes.Brtrue_S, BadDiv);
+ Context.Emit(OpCodes.Pop);
+ }
+
+ EmitDataLoadRn(Context);
+ EmitDataLoadRm(Context);
+
+ Context.Emit(ILOp);
+
+ Context.MarkLabel(BadDiv);
+
+ EmitDataStore(Context);
+ }
+
+ private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp)
+ {
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(ILOp);
+
+ EmitDataStore(Context);
+ }
+
+ private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp)
+ {
+ EmitDataLoadRn(Context);
+ EmitDataLoadShift(Context);
+
+ Context.Emit(ILOp);
+
+ EmitDataStore(Context);
+ }
+
+ private static void EmitDataLoadShift(AILEmitterCtx Context)
+ {
+ EmitDataLoadRm(Context);
+
+ Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1);
+
+ Context.Emit(OpCodes.And);
+
+ //Note: Only 32-bits shift values are valid, so when the value is 64-bits
+ //we need to cast it to a 32-bits integer. This is fine because we
+ //AND the value and only keep the lower 5 or 6 bits anyway -- it
+ //could very well fit on a byte.
+ if (Context.CurrOp.RegisterSize != ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_I4);
+ }
+ }
+
+ private static void EmitZeroCVFlags(AILEmitterCtx Context)
+ {
+ Context.EmitLdc_I4(0);
+
+ Context.EmitStflg((int)APState.VBit);
+
+ Context.EmitLdc_I4(0);
+
+ Context.EmitStflg((int)APState.CBit);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitAluHelper.cs b/ChocolArm64/Instruction/AInstEmitAluHelper.cs
new file mode 100644
index 00000000..e848742d
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitAluHelper.cs
@@ -0,0 +1,168 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static class AInstEmitAluHelper
+ {
+ public static void EmitAddsCCheck(AILEmitterCtx Context)
+ {
+ //C = Rd < Rn
+ Context.Emit(OpCodes.Dup);
+
+ EmitDataLoadRn(Context);
+
+ Context.Emit(OpCodes.Clt_Un);
+
+ Context.EmitStflg((int)APState.CBit);
+ }
+
+ public static void EmitAddsVCheck(AILEmitterCtx Context)
+ {
+ //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
+ Context.Emit(OpCodes.Dup);
+
+ EmitDataLoadRn(Context);
+
+ Context.Emit(OpCodes.Xor);
+
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Xor);
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.And);
+
+ Context.EmitLdc_I(0);
+
+ Context.Emit(OpCodes.Clt);
+
+ Context.EmitStflg((int)APState.VBit);
+ }
+
+ public static void EmitSubsCCheck(AILEmitterCtx Context)
+ {
+ //C = Rn == Rm || Rn > Rm = !(Rn < Rm)
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Clt_Un);
+
+ Context.EmitLdc_I4(1);
+
+ Context.Emit(OpCodes.Xor);
+
+ Context.EmitStflg((int)APState.CBit);
+ }
+
+ public static void EmitSubsVCheck(AILEmitterCtx Context)
+ {
+ //V = (Rd ^ Rn) & (Rn ^ Rm) < 0
+ Context.Emit(OpCodes.Dup);
+
+ EmitDataLoadRn(Context);
+
+ Context.Emit(OpCodes.Xor);
+
+ EmitDataLoadOpers(Context);
+
+ Context.Emit(OpCodes.Xor);
+ Context.Emit(OpCodes.And);
+
+ Context.EmitLdc_I(0);
+
+ Context.Emit(OpCodes.Clt);
+
+ Context.EmitStflg((int)APState.VBit);
+ }
+
+ public static void EmitDataLoadRm(AILEmitterCtx Context)
+ {
+ Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm);
+ }
+
+ public static void EmitDataLoadOpers(AILEmitterCtx Context)
+ {
+ EmitDataLoadRn(Context);
+ EmitDataLoadOper2(Context);
+ }
+
+ public static void EmitDataLoadRn(AILEmitterCtx Context)
+ {
+ IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
+
+ if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs)
+ {
+ Context.EmitLdintzr(Op.Rn);
+ }
+ else
+ {
+ Context.EmitLdint(Op.Rn);
+ }
+ }
+
+ public static void EmitDataLoadOper2(AILEmitterCtx Context)
+ {
+ switch (Context.CurrOp)
+ {
+ case IAOpCodeAluImm Op:
+ Context.EmitLdc_I(Op.Imm);
+ break;
+
+ case IAOpCodeAluRs Op:
+ Context.EmitLdintzr(Op.Rm);
+
+ switch (Op.ShiftType)
+ {
+ case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break;
+ case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break;
+ case AShiftType.Asr: Context.EmitAsr(Op.Shift); break;
+ case AShiftType.Ror: Context.EmitRor(Op.Shift); break;
+ }
+ break;
+
+ case IAOpCodeAluRx Op:
+ Context.EmitLdintzr(Op.Rm);
+ Context.EmitCast(Op.IntType);
+ Context.EmitLsl(Op.Shift);
+ break;
+ }
+ }
+
+ public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false);
+ public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true);
+
+ public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags)
+ {
+ IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp;
+
+ if (SetFlags || Op is IAOpCodeAluRs)
+ {
+ Context.EmitStintzr(Op.Rd);
+ }
+ else
+ {
+ Context.EmitStint(Op.Rd);
+ }
+ }
+
+ public static void EmitSetNZCV(AILEmitterCtx Context, int NZCV)
+ {
+ Context.EmitLdc_I4((NZCV >> 0) & 1);
+
+ Context.EmitStflg((int)APState.VBit);
+
+ Context.EmitLdc_I4((NZCV >> 1) & 1);
+
+ Context.EmitStflg((int)APState.CBit);
+
+ Context.EmitLdc_I4((NZCV >> 2) & 1);
+
+ Context.EmitStflg((int)APState.ZBit);
+
+ Context.EmitLdc_I4((NZCV >> 3) & 1);
+
+ Context.EmitStflg((int)APState.NBit);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitBfm.cs b/ChocolArm64/Instruction/AInstEmitBfm.cs
new file mode 100644
index 00000000..823af738
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitBfm.cs
@@ -0,0 +1,217 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Bfm(AILEmitterCtx Context)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ EmitBfmLoadRn(Context);
+
+ Context.EmitLdintzr(Op.Rd);
+ Context.EmitLdc_I(~Op.WMask & Op.TMask);
+
+ Context.Emit(OpCodes.And);
+ Context.Emit(OpCodes.Or);
+
+ Context.EmitLdintzr(Op.Rd);
+ Context.EmitLdc_I(~Op.TMask);
+
+ Context.Emit(OpCodes.And);
+ Context.Emit(OpCodes.Or);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Sbfm(AILEmitterCtx Context)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ int BitsCount = Op.GetBitsCount();
+
+ if (Op.Pos + 1 == BitsCount)
+ {
+ EmitSbfmShift(Context);
+ }
+ else if (Op.Pos < Op.Shift)
+ {
+ EmitSbfiz(Context);
+ }
+ else if (Op.Pos == 7 && Op.Shift == 0)
+ {
+ EmitSbfmCast(Context, OpCodes.Conv_I1);
+ }
+ else if (Op.Pos == 15 && Op.Shift == 0)
+ {
+ EmitSbfmCast(Context, OpCodes.Conv_I2);
+ }
+ else if (Op.Pos == 31 && Op.Shift == 0)
+ {
+ EmitSbfmCast(Context, OpCodes.Conv_I4);
+ }
+ else if (Op.Shift == 0)
+ {
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.EmitLsl(BitsCount - 1 - Op.Pos);
+ Context.EmitAsr(BitsCount - 1);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+ else
+ {
+ EmitBfmLoadRn(Context);
+
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.EmitLsl(BitsCount - 1 - Op.Pos);
+ Context.EmitAsr(BitsCount - 1);
+
+ Context.EmitLdc_I(~Op.TMask);
+
+ Context.Emit(OpCodes.And);
+ Context.Emit(OpCodes.Or);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+ }
+
+ public static void Ubfm(AILEmitterCtx Context)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ if (Op.Pos + 1 == Op.GetBitsCount())
+ {
+ EmitUbfmShift(Context);
+ }
+ else if (Op.Pos < Op.Shift)
+ {
+ EmitUbfiz(Context);
+ }
+ else if (Op.Pos + 1 == Op.Shift)
+ {
+ EmitBfmLsl(Context);
+ }
+ else if (Op.Pos == 7 && Op.Shift == 0)
+ {
+ EmitUbfmCast(Context, OpCodes.Conv_U1);
+ }
+ else if (Op.Pos == 15 && Op.Shift == 0)
+ {
+ EmitUbfmCast(Context, OpCodes.Conv_U2);
+ }
+ else
+ {
+ EmitBfmLoadRn(Context);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+ }
+
+ private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true);
+ private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false);
+
+ private static void EmitBfiz(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ int Width = Op.Pos + 1;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.EmitLsl(Op.GetBitsCount() - Width);
+
+ if (Signed)
+ {
+ Context.EmitAsr(Op.Shift - Width);
+ }
+ else
+ {
+ Context.EmitLsr(Op.Shift - Width);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp)
+ {
+ EmitBfmCast(Context, ILOp, true);
+ }
+
+ private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp)
+ {
+ EmitBfmCast(Context, ILOp, false);
+ }
+
+ private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.Emit(ILOp);
+
+ if (Op.RegisterSize != ARegisterSize.Int32)
+ {
+ Context.Emit(Signed
+ ? OpCodes.Conv_I8
+ : OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitSbfmShift(AILEmitterCtx Context)
+ {
+ EmitBfmShift(Context, true);
+ }
+
+ private static void EmitUbfmShift(AILEmitterCtx Context)
+ {
+ EmitBfmShift(Context, false);
+ }
+
+ private static void EmitBfmShift(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitLdc_I4(Op.Shift);
+
+ Context.Emit(Signed
+ ? OpCodes.Shr
+ : OpCodes.Shr_Un);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitBfmLsl(AILEmitterCtx Context)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.EmitLsl(Op.GetBitsCount() - Op.Shift);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitBfmLoadRn(AILEmitterCtx Context)
+ {
+ AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.EmitRor(Op.Shift);
+
+ Context.EmitLdc_I(Op.WMask & Op.TMask);
+
+ Context.Emit(OpCodes.And);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitCcmp.cs b/ChocolArm64/Instruction/AInstEmitCcmp.cs
new file mode 100644
index 00000000..7153a6a0
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitCcmp.cs
@@ -0,0 +1,81 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitAluHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ private enum CcmpOp
+ {
+ Cmp,
+ Cmn
+ }
+
+ public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn);
+ public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp);
+
+ private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp)
+ {
+ AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp;
+
+ AILLabel LblTrue = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.EmitCondBranch(LblTrue, Op.Cond);
+
+ Context.EmitLdc_I4((Op.NZCV >> 0) & 1);
+
+ Context.EmitStflg((int)APState.VBit);
+
+ Context.EmitLdc_I4((Op.NZCV >> 1) & 1);
+
+ Context.EmitStflg((int)APState.CBit);
+
+ Context.EmitLdc_I4((Op.NZCV >> 2) & 1);
+
+ Context.EmitStflg((int)APState.ZBit);
+
+ Context.EmitLdc_I4((Op.NZCV >> 3) & 1);
+
+ Context.EmitStflg((int)APState.NBit);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+
+ Context.MarkLabel(LblTrue);
+
+ EmitDataLoadOpers(Context);
+
+ if (CmpOp == CcmpOp.Cmp)
+ {
+ Context.Emit(OpCodes.Sub);
+
+ Context.EmitZNFlagCheck();
+
+ EmitSubsCCheck(Context);
+ EmitSubsVCheck(Context);
+ }
+ else if (CmpOp == CcmpOp.Cmn)
+ {
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitZNFlagCheck();
+
+ EmitAddsCCheck(Context);
+ EmitAddsVCheck(Context);
+ }
+ else
+ {
+ throw new ArgumentException(nameof(CmpOp));
+ }
+
+ Context.Emit(OpCodes.Pop);
+
+ Context.MarkLabel(LblEnd);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitCsel.cs b/ChocolArm64/Instruction/AInstEmitCsel.cs
new file mode 100644
index 00000000..33080980
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitCsel.cs
@@ -0,0 +1,59 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ private enum CselOperation
+ {
+ None,
+ Increment,
+ Invert,
+ Negate
+ }
+
+ public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None);
+ public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment);
+ public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert);
+ public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate);
+
+ private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp)
+ {
+ AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp;
+
+ AILLabel LblTrue = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.EmitCondBranch(LblTrue, Op.Cond);
+ Context.EmitLdintzr(Op.Rm);
+
+ if (CselOp == CselOperation.Increment)
+ {
+ Context.EmitLdc_I(1);
+
+ Context.Emit(OpCodes.Add);
+ }
+ else if (CselOp == CselOperation.Invert)
+ {
+ Context.Emit(OpCodes.Not);
+ }
+ else if (CselOp == CselOperation.Negate)
+ {
+ Context.Emit(OpCodes.Neg);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+
+ Context.MarkLabel(LblTrue);
+
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitStintzr(Op.Rd);
+
+ Context.MarkLabel(LblEnd);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs
new file mode 100644
index 00000000..209ba56f
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitException.cs
@@ -0,0 +1,65 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
+
+ public static void Brk(AILEmitterCtx Context)
+ {
+ EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
+ }
+
+ public static void Svc(AILEmitterCtx Context)
+ {
+ EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall));
+ }
+
+ private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName)
+ {
+ AOpCodeException Op = (AOpCodeException)Context.CurrOp;
+
+ Context.EmitStoreState();
+
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+ Context.EmitLdc_I4(Op.Id);
+
+ MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
+
+ Context.EmitCall(MthdInfo);
+
+ if (Context.CurrBlock.Next != null)
+ {
+ Context.EmitLoadState(Context.CurrBlock.Next);
+ }
+ }
+
+ public static void Und(AILEmitterCtx Context)
+ {
+ AOpCode Op = Context.CurrOp;
+
+ Context.EmitStoreState();
+
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+ Context.EmitLdc_I8(Op.Position);
+ Context.EmitLdc_I4(Op.RawOpCode);
+
+ string MthdName = nameof(AThreadState.OnUndefined);
+
+ MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
+
+ Context.EmitCall(MthdInfo);
+
+ if (Context.CurrBlock.Next != null)
+ {
+ Context.EmitLoadState(Context.CurrBlock.Next);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs
new file mode 100644
index 00000000..be68aa6c
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitFlow.cs
@@ -0,0 +1,124 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void B(AILEmitterCtx Context)
+ {
+ AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
+
+ Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm));
+ }
+
+ public static void B_Cond(AILEmitterCtx Context)
+ {
+ AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp;
+
+ Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond);
+ }
+
+ public static void Bl(AILEmitterCtx Context)
+ {
+ AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
+
+ Context.EmitLdc_I(Op.Position + 4);
+ Context.EmitStint(AThreadState.LRIndex);
+ Context.EmitStoreState();
+
+ if (Context.TryOptEmitSubroutineCall())
+ {
+ //Note: the return value of the called method will be placed
+ //at the Stack, the return value is always a Int64 with the
+ //return address of the function. We check if the address is
+ //correct, if it isn't we keep returning until we reach the dispatcher.
+ Context.Emit(OpCodes.Dup);
+
+ Context.EmitLdc_I8(Op.Position + 4);
+
+ AILLabel LblContinue = new AILLabel();
+
+ Context.Emit(OpCodes.Beq_S, LblContinue);
+ Context.Emit(OpCodes.Ret);
+
+ Context.MarkLabel(LblContinue);
+
+ Context.Emit(OpCodes.Pop);
+
+ if (Context.CurrBlock.Next != null)
+ {
+ Context.EmitLoadState(Context.CurrBlock.Next);
+ }
+ }
+ else
+ {
+ Context.EmitLdc_I8(Op.Imm);
+
+ Context.Emit(OpCodes.Ret);
+ }
+ }
+
+ public static void Blr(AILEmitterCtx Context)
+ {
+ AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
+
+ Context.EmitLdc_I(Op.Position + 4);
+ Context.EmitStint(AThreadState.LRIndex);
+ Context.EmitStoreState();
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.Emit(OpCodes.Ret);
+ }
+
+ public static void Br(AILEmitterCtx Context)
+ {
+ AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
+
+ Context.EmitStoreState();
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.Emit(OpCodes.Ret);
+ }
+
+ public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un);
+ public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq);
+
+ private static void EmitCb(AILEmitterCtx Context, OpCode ILOp)
+ {
+ AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rt);
+ Context.EmitLdc_I(0);
+
+ Context.Emit(ILOp, Context.GetLabel(Op.Imm));
+ }
+
+ public static void Ret(AILEmitterCtx Context)
+ {
+ Context.EmitStoreState();
+ Context.EmitLdint(AThreadState.LRIndex);
+
+ Context.Emit(OpCodes.Ret);
+ }
+
+ public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un);
+ public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq);
+
+ private static void EmitTb(AILEmitterCtx Context, OpCode ILOp)
+ {
+ AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rt);
+ Context.EmitLdc_I(1L << Op.Pos);
+
+ Context.Emit(OpCodes.And);
+
+ Context.EmitLdc_I(0);
+
+ Context.Emit(ILOp, Context.GetLabel(Op.Imm));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitMemory.cs b/ChocolArm64/Instruction/AInstEmitMemory.cs
new file mode 100644
index 00000000..af7de3ba
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitMemory.cs
@@ -0,0 +1,252 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Adr(AILEmitterCtx Context)
+ {
+ AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
+
+ Context.EmitLdc_I(Op.Position + Op.Imm);
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Adrp(AILEmitterCtx Context)
+ {
+ AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
+
+ Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12));
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false);
+ public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true);
+
+ public static void EmitLdr(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+
+ EmitLoadAddress(Context);
+
+ if (Signed && Op.Extend64)
+ {
+ EmitReadSx64Call(Context, Op.Size);
+ }
+ else if (Signed)
+ {
+ EmitReadSx32Call(Context, Op.Size);
+ }
+ else
+ {
+ EmitReadZxCall(Context, Op.Size);
+ }
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitStvec(Op.Rt);
+ }
+ else
+ {
+ Context.EmitStintzr(Op.Rt);
+ }
+
+ EmitWBackIfNeeded(Context);
+ }
+
+ public static void LdrLit(AILEmitterCtx Context)
+ {
+ IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp;
+
+ if (Op.Prefetch)
+ {
+ return;
+ }
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdc_I8(Op.Imm);
+
+ if (Op.Signed)
+ {
+ EmitReadSx64Call(Context, Op.Size);
+ }
+ else
+ {
+ EmitReadZxCall(Context, Op.Size);
+ }
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitStvec(Op.Rt);
+ }
+ else
+ {
+ Context.EmitStint(Op.Rt);
+ }
+ }
+
+ public static void Ldp(AILEmitterCtx Context)
+ {
+ AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
+
+ void EmitReadAndStore(int Rt)
+ {
+ if (Op.Extend64)
+ {
+ EmitReadSx64Call(Context, Op.Size);
+ }
+ else
+ {
+ EmitReadZxCall(Context, Op.Size);
+ }
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitStvec(Rt);
+ }
+ else
+ {
+ Context.EmitStintzr(Rt);
+ }
+ }
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+
+ EmitLoadAddress(Context);
+
+ EmitReadAndStore(Op.Rt);
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdtmp();
+ Context.EmitLdc_I8(1 << Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitReadAndStore(Op.Rt2);
+
+ EmitWBackIfNeeded(Context);
+ }
+
+ public static void Str(AILEmitterCtx Context)
+ {
+ AOpCodeMem Op = (AOpCodeMem)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+
+ EmitLoadAddress(Context);
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitLdvec(Op.Rt);
+ }
+ else
+ {
+ Context.EmitLdintzr(Op.Rt);
+ }
+
+ EmitWriteCall(Context, Op.Size);
+
+ EmitWBackIfNeeded(Context);
+ }
+
+ public static void Stp(AILEmitterCtx Context)
+ {
+ AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+
+ EmitLoadAddress(Context);
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitLdvec(Op.Rt);
+ }
+ else
+ {
+ Context.EmitLdintzr(Op.Rt);
+ }
+
+ EmitWriteCall(Context, Op.Size);
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdtmp();
+ Context.EmitLdc_I8(1 << Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ if (Op is IAOpCodeSimd)
+ {
+ Context.EmitLdvec(Op.Rt2);
+ }
+ else
+ {
+ Context.EmitLdintzr(Op.Rt2);
+ }
+
+ EmitWriteCall(Context, Op.Size);
+
+ EmitWBackIfNeeded(Context);
+ }
+
+ private static void EmitLoadAddress(AILEmitterCtx Context)
+ {
+ switch (Context.CurrOp)
+ {
+ case AOpCodeMemImm Op:
+ Context.EmitLdint(Op.Rn);
+
+ if (!Op.PostIdx)
+ {
+ //Pre-indexing.
+ Context.EmitLdc_I(Op.Imm);
+
+ Context.Emit(OpCodes.Add);
+ }
+ break;
+
+ case AOpCodeMemReg Op:
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdintzr(Op.Rm);
+ Context.EmitCast(Op.IntType);
+
+ if (Op.Shift)
+ {
+ Context.EmitLsl(Op.Size);
+ }
+
+ Context.Emit(OpCodes.Add);
+ break;
+ }
+
+ //Save address to Scratch var since the register value may change.
+ Context.Emit(OpCodes.Dup);
+
+ Context.EmitSttmp();
+ }
+
+ private static void EmitWBackIfNeeded(AILEmitterCtx Context)
+ {
+ //Check whenever the current OpCode has post-indexed write back, if so write it.
+ //Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both.
+ if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack)
+ {
+ Context.EmitLdtmp();
+
+ if (Op.PostIdx)
+ {
+ Context.EmitLdc_I(Op.Imm);
+
+ Context.Emit(OpCodes.Add);
+ }
+
+ Context.EmitStint(Op.Rn);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs
new file mode 100644
index 00000000..a339bb69
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs
@@ -0,0 +1,180 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Memory;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+using System.Threading;
+
+using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ [Flags]
+ private enum AccessType
+ {
+ None = 0,
+ Ordered = 1,
+ Exclusive = 2,
+ OrderedEx = Ordered | Exclusive
+ }
+
+ public static void Clrex(AILEmitterCtx Context)
+ {
+ EmitMemoryCall(Context, nameof(AMemory.ClearExclusive));
+ }
+
+ public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context);
+ public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context);
+
+ public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered);
+ public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx);
+ public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive);
+ public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive);
+ public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx);
+
+ private static void EmitLdr(AILEmitterCtx Context, AccessType AccType)
+ {
+ EmitLoad(Context, AccType, false);
+ }
+
+ private static void EmitLdp(AILEmitterCtx Context, AccessType AccType)
+ {
+ EmitLoad(Context, AccType, true);
+ }
+
+ private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair)
+ {
+ AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+
+ EmitReadZxCall(Context, Op.Size);
+
+ Context.EmitStintzr(Op.Rt);
+
+ if (Pair)
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdc_I(8 << Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitReadZxCall(Context, Op.Size);
+
+ Context.EmitStintzr(Op.Rt2);
+ }
+
+ if (AccType.HasFlag(AccessType.Exclusive))
+ {
+ EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn);
+ }
+
+ if (AccType.HasFlag(AccessType.Ordered))
+ {
+ EmitBarrier(Context);
+ }
+ }
+
+ public static void Pfrm(AILEmitterCtx Context)
+ {
+ //Memory Prefetch, execute as no-op.
+ }
+
+ public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered);
+ public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx);
+ public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive);
+ public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive);
+ public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx);
+
+ private static void EmitStr(AILEmitterCtx Context, AccessType AccType)
+ {
+ EmitStore(Context, AccType, false);
+ }
+
+ private static void EmitStp(AILEmitterCtx Context, AccessType AccType)
+ {
+ EmitStore(Context, AccType, true);
+ }
+
+ private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair)
+ {
+ AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
+
+ if (AccType.HasFlag(AccessType.Ordered))
+ {
+ EmitBarrier(Context);
+ }
+
+ AILLabel LblEx = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ if (AccType.HasFlag(AccessType.Exclusive))
+ {
+ EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn);
+
+ Context.Emit(OpCodes.Brtrue_S, LblEx);
+
+ Context.EmitLdc_I8(1);
+ Context.EmitStintzr(Op.Rs);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+ }
+
+ Context.MarkLabel(LblEx);
+
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdintzr(Op.Rt);
+
+ EmitWriteCall(Context, Op.Size);
+
+ if (Pair)
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdc_I(8 << Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitLdintzr(Op.Rt2);
+
+ EmitWriteCall(Context, Op.Size);
+ }
+
+ if (AccType.HasFlag(AccessType.Exclusive))
+ {
+ Context.EmitLdc_I8(0);
+ Context.EmitStintzr(Op.Rs);
+
+ Clrex(Context);
+ }
+
+ Context.MarkLabel(LblEnd);
+ }
+
+ private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+ if (Rn != -1)
+ {
+ Context.EmitLdint(Rn);
+ }
+
+ Context.EmitCall(typeof(AMemory), Name);
+ }
+
+ private static void EmitBarrier(AILEmitterCtx Context)
+ {
+ //Note: This barrier is most likely not necessary, and probably
+ //doesn't make any difference since we need to do a ton of stuff
+ //(software MMU emulation) to read or write anything anyway.
+ Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs
new file mode 100644
index 00000000..d5a0051b
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs
@@ -0,0 +1,138 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Memory;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static class AInstEmitMemoryHelper
+ {
+ private enum Extension
+ {
+ Zx,
+ Sx32,
+ Sx64
+ }
+
+ public static void EmitReadZxCall(AILEmitterCtx Context, int Size)
+ {
+ EmitReadCall(Context, Extension.Zx, Size);
+ }
+
+ public static void EmitReadSx32Call(AILEmitterCtx Context, int Size)
+ {
+ EmitReadCall(Context, Extension.Sx32, Size);
+ }
+
+ public static void EmitReadSx64Call(AILEmitterCtx Context, int Size)
+ {
+ EmitReadCall(Context, Extension.Sx64, Size);
+ }
+
+ private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
+ {
+ bool IsSimd = GetIsSimd(Context);
+
+ string Name = null;
+
+ if (Size < 0 || Size > (IsSimd ? 4 : 3))
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ if (IsSimd)
+ {
+ switch (Size)
+ {
+ case 0: Name = nameof(AMemory.ReadVector8); break;
+ case 1: Name = nameof(AMemory.ReadVector16); break;
+ case 2: Name = nameof(AMemory.ReadVector32); break;
+ case 3: Name = nameof(AMemory.ReadVector64); break;
+ case 4: Name = nameof(AMemory.ReadVector128); break;
+ }
+ }
+ else
+ {
+ switch (Size)
+ {
+ case 0: Name = nameof(AMemory.ReadByte); break;
+ case 1: Name = nameof(AMemory.ReadUInt16); break;
+ case 2: Name = nameof(AMemory.ReadUInt32); break;
+ case 3: Name = nameof(AMemory.ReadUInt64); break;
+ }
+ }
+
+ Context.EmitCall(typeof(AMemory), Name);
+
+ if (!IsSimd)
+ {
+ if (Ext == Extension.Sx32 ||
+ Ext == Extension.Sx64)
+ {
+ switch (Size)
+ {
+ case 0: Context.Emit(OpCodes.Conv_I1); break;
+ case 1: Context.Emit(OpCodes.Conv_I2); break;
+ case 2: Context.Emit(OpCodes.Conv_I4); break;
+ }
+ }
+
+ if (Size < 3)
+ {
+ Context.Emit(Ext == Extension.Sx64
+ ? OpCodes.Conv_I8
+ : OpCodes.Conv_U8);
+ }
+ }
+ }
+
+ public static void EmitWriteCall(AILEmitterCtx Context, int Size)
+ {
+ bool IsSimd = GetIsSimd(Context);
+
+ string Name = null;
+
+ if (Size < 0 || Size > (IsSimd ? 4 : 3))
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ if (Size < 3 && !IsSimd)
+ {
+ Context.Emit(OpCodes.Conv_I4);
+ }
+
+ if (IsSimd)
+ {
+ switch (Size)
+ {
+ case 0: Name = nameof(AMemory.WriteVector8); break;
+ case 1: Name = nameof(AMemory.WriteVector16); break;
+ case 2: Name = nameof(AMemory.WriteVector32); break;
+ case 3: Name = nameof(AMemory.WriteVector64); break;
+ case 4: Name = nameof(AMemory.WriteVector128); break;
+ }
+ }
+ else
+ {
+ switch (Size)
+ {
+ case 0: Name = nameof(AMemory.WriteByte); break;
+ case 1: Name = nameof(AMemory.WriteUInt16); break;
+ case 2: Name = nameof(AMemory.WriteUInt32); break;
+ case 3: Name = nameof(AMemory.WriteUInt64); break;
+ }
+ }
+
+ Context.EmitCall(typeof(AMemory), Name);
+ }
+
+ private static bool GetIsSimd(AILEmitterCtx Context)
+ {
+ return Context.CurrOp is IAOpCodeSimd &&
+ !(Context.CurrOp is AOpCodeSimdMemMs ||
+ Context.CurrOp is AOpCodeSimdMemSs);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitMove.cs b/ChocolArm64/Instruction/AInstEmitMove.cs
new file mode 100644
index 00000000..719b53d5
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitMove.cs
@@ -0,0 +1,41 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Movk(AILEmitterCtx Context)
+ {
+ AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rd);
+ Context.EmitLdc_I(~(0xffffL << Op.Pos));
+
+ Context.Emit(OpCodes.And);
+
+ Context.EmitLdc_I(Op.Imm);
+
+ Context.Emit(OpCodes.Or);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Movn(AILEmitterCtx Context)
+ {
+ AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
+
+ Context.EmitLdc_I(~Op.Imm);
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Movz(AILEmitterCtx Context)
+ {
+ AOpCodeMov Op = (AOpCodeMov)Context.CurrOp;
+
+ Context.EmitLdc_I(Op.Imm);
+ Context.EmitStintzr(Op.Rd);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitMul.cs b/ChocolArm64/Instruction/AInstEmitMul.cs
new file mode 100644
index 00000000..3713c81f
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitMul.cs
@@ -0,0 +1,80 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add);
+ public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub);
+
+ private static void EmitMul(AILEmitterCtx Context, OpCode ILOp)
+ {
+ AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Ra);
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitLdintzr(Op.Rm);
+
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(ILOp);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true);
+ public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true);
+ public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false);
+ public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false);
+
+ private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed)
+ {
+ AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
+
+ OpCode CastOp = Signed
+ ? OpCodes.Conv_I8
+ : OpCodes.Conv_U8;
+
+ Context.EmitLdintzr(Op.Ra);
+ Context.EmitLdintzr(Op.Rn);
+
+ Context.Emit(OpCodes.Conv_I4);
+ Context.Emit(CastOp);
+
+ Context.EmitLdintzr(Op.Rm);
+
+ Context.Emit(OpCodes.Conv_I4);
+ Context.Emit(CastOp);
+ Context.Emit(OpCodes.Mul);
+
+ Context.Emit(AddSubOp);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Smulh(AILEmitterCtx Context)
+ {
+ AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitLdintzr(Op.Rm);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128));
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Umulh(AILEmitterCtx Context)
+ {
+ AOpCodeMul Op = (AOpCodeMul)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+ Context.EmitLdintzr(Op.Rm);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128));
+
+ Context.EmitStintzr(Op.Rd);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
new file mode 100644
index 00000000..21ec11a7
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
@@ -0,0 +1,367 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Add_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
+ }
+
+ public static void Addp_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
+ EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Addp_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ int Elems = Bytes >> Op.Size;
+ int Half = Elems >> 1;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ int Elem = (Index & (Half - 1)) << 1;
+
+ EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size);
+ EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitVectorInsertTmp(Context, Index, Op.Size);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Addv_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
+
+ for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
+
+ Context.Emit(OpCodes.Add);
+ }
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Cnt_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Index, 0);
+
+ Context.Emit(OpCodes.Conv_U1);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8));
+
+ Context.Emit(OpCodes.Conv_U8);
+
+ EmitVectorInsert(Context, Op.Rd, Index, 0);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Fabs_S(AILEmitterCtx Context)
+ {
+ EmitScalarUnaryOpF(Context, () =>
+ {
+ EmitUnaryMathCall(Context, nameof(Math.Abs));
+ });
+ }
+
+ public static void Fadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
+ }
+
+ public static void Fadd_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
+ }
+
+ public static void Fdiv_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
+ }
+
+ public static void Fdiv_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
+ }
+
+ public static void Fmadd_S(AILEmitterCtx Context)
+ {
+ EmitScalarTernaryRaOpF(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Add);
+ });
+ }
+
+ public static void Fmax_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () =>
+ {
+ EmitBinaryMathCall(Context, nameof(Math.Max));
+ });
+ }
+
+ public static void Fmin_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () =>
+ {
+ EmitBinaryMathCall(Context, nameof(Math.Min));
+ });
+ }
+
+ public static void Fmaxnm_S(AILEmitterCtx Context)
+ {
+ Fmax_S(Context);
+ }
+
+ public static void Fminnm_S(AILEmitterCtx Context)
+ {
+ Fmin_S(Context);
+ }
+
+ public static void Fmla_V(AILEmitterCtx Context)
+ {
+ EmitVectorTernaryOpF(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Add);
+ });
+ }
+
+ public static void Fmla_Ve(AILEmitterCtx Context)
+ {
+ EmitVectorTernaryOpByElemF(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Add);
+ });
+ }
+
+ public static void Fmsub_S(AILEmitterCtx Context)
+ {
+ EmitScalarTernaryRaOpF(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Sub);
+ });
+ }
+
+ public static void Fmul_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
+ }
+
+ public static void Fmul_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
+ }
+
+ public static void Fmul_Ve(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
+ }
+
+ public static void Fneg_S(AILEmitterCtx Context)
+ {
+ EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
+ }
+
+ public static void Fnmul_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Neg);
+ });
+ }
+
+ public static void Fnmsub_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
+ EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
+
+ Context.Emit(OpCodes.Mul);
+
+ EmitVectorExtractF(Context, Op.Ra, 0, SizeF);
+
+ Context.Emit(OpCodes.Sub);
+
+ EmitScalarSetF(Context, Op.Rd, SizeF);
+ }
+
+ public static void Frinta_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Frintm_S(AILEmitterCtx Context)
+ {
+ EmitScalarUnaryOpF(Context, () =>
+ {
+ EmitUnaryMathCall(Context, nameof(Math.Floor));
+ });
+ }
+
+ public static void Fsqrt_S(AILEmitterCtx Context)
+ {
+ EmitScalarUnaryOpF(Context, () =>
+ {
+ EmitUnaryMathCall(Context, nameof(Math.Sqrt));
+ });
+ }
+
+ public static void Fsub_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
+ }
+
+ public static void Fsub_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
+ }
+
+ public static void Mla_V(AILEmitterCtx Context)
+ {
+ EmitVectorTernaryOpZx(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Add);
+ });
+ }
+
+ public static void Mls_V(AILEmitterCtx Context)
+ {
+ EmitVectorTernaryOpZx(Context, () =>
+ {
+ Context.Emit(OpCodes.Mul);
+ Context.Emit(OpCodes.Sub);
+ });
+ }
+
+ public static void Mul_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
+ }
+
+ public static void Neg_V(AILEmitterCtx Context)
+ {
+ EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
+ }
+
+ public static void Saddw_V(AILEmitterCtx Context)
+ {
+ EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
+ }
+
+ public static void Smax_V(AILEmitterCtx Context)
+ {
+ Type[] Types = new Type[] { typeof(long), typeof(long) };
+
+ MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
+
+ EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
+ }
+
+ public static void Smin_V(AILEmitterCtx Context)
+ {
+ Type[] Types = new Type[] { typeof(long), typeof(long) };
+
+ MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types);
+
+ EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
+ }
+
+ public static void Smull_V(AILEmitterCtx Context)
+ {
+ EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));
+ }
+
+ public static void Sub_S(AILEmitterCtx Context)
+ {
+ EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
+ }
+
+ public static void Sub_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
+ }
+
+ public static void Uaddlv_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
+
+ for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
+
+ Context.Emit(OpCodes.Add);
+ }
+
+ EmitScalarSet(Context, Op.Rd, Op.Size + 1);
+ }
+
+ public static void Uaddw_V(AILEmitterCtx Context)
+ {
+ EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs
new file mode 100644
index 00000000..97ccf0ab
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs
@@ -0,0 +1,233 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitAluHelper;
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Cmeq_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Beq_S);
+ }
+
+ public static void Cmge_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Bge_S);
+ }
+
+ public static void Cmgt_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Bgt_S);
+ }
+
+ public static void Cmhi_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Bgt_Un_S);
+ }
+
+ public static void Cmhs_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Bge_Un_S);
+ }
+
+ public static void Cmle_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Ble_S);
+ }
+
+ public static void Cmlt_V(AILEmitterCtx Context)
+ {
+ EmitVectorCmp(Context, OpCodes.Blt_S);
+ }
+
+ public static void Fccmp_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
+
+ AILLabel LblTrue = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.EmitCondBranch(LblTrue, Op.Cond);
+
+ EmitSetNZCV(Context, Op.NZCV);
+
+ Context.Emit(OpCodes.Br, LblEnd);
+
+ Context.MarkLabel(LblTrue);
+
+ Fcmp_S(Context);
+
+ Context.MarkLabel(LblEnd);
+ }
+
+ public static void Fccmpe_S(AILEmitterCtx Context)
+ {
+ Fccmp_S(Context);
+ }
+
+ public static void Fcmp_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
+
+ //Handle NaN case. If any number is NaN, then NZCV = 0011.
+ if (CmpWithZero)
+ {
+ EmitNaNCheck(Context, Op.Rn);
+ }
+ else
+ {
+ EmitNaNCheck(Context, Op.Rn);
+ EmitNaNCheck(Context, Op.Rm);
+
+ Context.Emit(OpCodes.Or);
+ }
+
+ AILLabel LblNaN = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.Emit(OpCodes.Brtrue_S, LblNaN);
+
+ void EmitLoadOpers()
+ {
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ if (CmpWithZero)
+ {
+ EmitLdcImmF(Context, 0, Op.Size);
+ }
+ else
+ {
+ EmitVectorExtractF(Context, Op.Rm, 0, Op.Size);
+ }
+ }
+
+ //Z = Rn == Rm
+ EmitLoadOpers();
+
+ Context.Emit(OpCodes.Ceq);
+ Context.Emit(OpCodes.Dup);
+
+ Context.EmitStflg((int)APState.ZBit);
+
+ //C = Rn >= Rm
+ EmitLoadOpers();
+
+ Context.Emit(OpCodes.Cgt);
+ Context.Emit(OpCodes.Or);
+
+ Context.EmitStflg((int)APState.CBit);
+
+ //N = Rn < Rm
+ EmitLoadOpers();
+
+ Context.Emit(OpCodes.Clt);
+
+ Context.EmitStflg((int)APState.NBit);
+
+ //V = 0
+ Context.EmitLdc_I4(0);
+
+ Context.EmitStflg((int)APState.VBit);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+
+ Context.MarkLabel(LblNaN);
+
+ EmitSetNZCV(Context, 0b0011);
+
+ Context.MarkLabel(LblEnd);
+ }
+
+ public static void Fcmpe_S(AILEmitterCtx Context)
+ {
+ Fcmp_S(Context);
+ }
+
+ private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
+ {
+ if (Size == 0)
+ {
+ Context.EmitLdc_R4((float)ImmF);
+ }
+ else if (Size == 1)
+ {
+ Context.EmitLdc_R8(ImmF);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+ }
+
+ private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
+ {
+ IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Reg, 0, Op.Size);
+
+ if (Op.Size == 0)
+ {
+ Context.EmitCall(typeof(float), nameof(float.IsNaN));
+ }
+ else if (Op.Size == 1)
+ {
+ Context.EmitCall(typeof(double), nameof(double.IsNaN));
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size));
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size);
+
+ if (Op is AOpCodeSimdReg BinOp)
+ {
+ EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size);
+ }
+ else
+ {
+ Context.EmitLdc_I8(0);
+ }
+
+ AILLabel LblTrue = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.Emit(ILOp, LblTrue);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+
+ Context.MarkLabel(LblTrue);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask);
+
+ Context.MarkLabel(LblEnd);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
new file mode 100644
index 00000000..00c2fe9b
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs
@@ -0,0 +1,444 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Fcvt_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ EmitFloatCast(Context, Op.Opc);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Opc);
+ }
+
+ public static void Fcvtas_Gp(AILEmitterCtx Context)
+ {
+ Fcvta__Gp(Context, Signed: true);
+ }
+
+ public static void Fcvtau_Gp(AILEmitterCtx Context)
+ {
+ Fcvta__Gp(Context, Signed: false);
+ }
+
+ public static void Fcvtms_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvt_s_Gp(Context, nameof(Math.Floor));
+ }
+
+ public static void Fcvtps_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvt_s_Gp(Context, nameof(Math.Ceiling));
+ }
+
+ public static void Fcvtzs_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvtz__Gp(Context, Signed: true);
+ }
+
+ public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context)
+ {
+ EmitFcvtz__Gp_Fix(Context, Signed: true);
+ }
+
+ public static void Fcvtzs_V(AILEmitterCtx Context)
+ {
+ EmitVectorFcvt(Context, Signed: true);
+ }
+
+ public static void Fcvtzu_Gp(AILEmitterCtx Context)
+ {
+ EmitFcvtz__Gp(Context, Signed: false);
+ }
+
+ public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context)
+ {
+ EmitFcvtz__Gp_Fix(Context, Signed: false);
+ }
+
+ public static void Fcvtzu_V(AILEmitterCtx Context)
+ {
+ EmitVectorFcvt(Context, Signed: false);
+ }
+
+ public static void Scvtf_Gp(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U4);
+ }
+
+ EmitFloatCast(Context, Op.Size);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Scvtf_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size + 2);
+
+ EmitFloatCast(Context, Op.Size);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Scvtf_V(AILEmitterCtx Context)
+ {
+ EmitVectorCvtf(Context, Signed: true);
+ }
+
+ public static void Ucvtf_Gp(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U4);
+ }
+
+ Context.Emit(OpCodes.Conv_R_Un);
+
+ EmitFloatCast(Context, Op.Size);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Ucvtf_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2);
+
+ Context.Emit(OpCodes.Conv_R_Un);
+
+ EmitFloatCast(Context, Op.Size);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Ucvtf_V(AILEmitterCtx Context)
+ {
+ EmitVectorCvtf(Context, Signed: false);
+ }
+
+ private static int GetFBits(AILEmitterCtx Context)
+ {
+ if (Context.CurrOp is AOpCodeSimdShImm Op)
+ {
+ return GetImmShr(Op);
+ }
+
+ return 0;
+ }
+
+ private static void EmitFloatCast(AILEmitterCtx Context, int Size)
+ {
+ if (Size == 0)
+ {
+ Context.Emit(OpCodes.Conv_R4);
+ }
+ else if (Size == 1)
+ {
+ Context.Emit(OpCodes.Conv_R8);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+ }
+
+ private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
+
+ if (Signed)
+ {
+ EmitScalarFcvts(Context, Op.Size, 0);
+ }
+ else
+ {
+ EmitScalarFcvtu(Context, Op.Size, 0);
+ }
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ EmitUnaryMathCall(Context, Name);
+
+ EmitScalarFcvts(Context, Op.Size, 0);
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ if (Signed)
+ {
+ EmitScalarFcvts(Context, Op.Size, 0);
+ }
+ else
+ {
+ EmitScalarFcvtu(Context, Op.Size, 0);
+ }
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ if (Signed)
+ {
+ EmitScalarFcvts(Context, Op.Size, Op.FBits);
+ }
+ else
+ {
+ EmitScalarFcvtu(Context, Op.Size, Op.FBits);
+ }
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+ int SizeI = SizeF + 2;
+
+ int FBits = GetFBits(Context);
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> SizeI); Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed);
+
+ if (!Signed)
+ {
+ Context.Emit(OpCodes.Conv_R_Un);
+ }
+
+ Context.Emit(SizeF == 0
+ ? OpCodes.Conv_R4
+ : OpCodes.Conv_R8);
+
+ EmitI2fFBitsMul(Context, SizeF, FBits);
+
+ EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+ int SizeI = SizeF + 2;
+
+ int FBits = GetFBits(Context);
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> SizeI); Index++)
+ {
+ EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+
+ EmitF2iFBitsMul(Context, SizeF, FBits);
+
+ if (SizeF == 0)
+ {
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.SatF32ToS32)
+ : nameof(ASoftFallback.SatF32ToU32));
+ }
+ else /* if (SizeF == 1) */
+ {
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.SatF64ToS64)
+ : nameof(ASoftFallback.SatF64ToU64));
+ }
+
+ if (SizeF == 0)
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ EmitVectorInsert(Context, Op.Rd, Index, SizeI);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits)
+ {
+ if (Size < 0 || Size > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ EmitF2iFBitsMul(Context, Size, FBits);
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS32));
+ }
+ else /* if (Size == 1) */
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS32));
+ }
+ }
+ else
+ {
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS64));
+ }
+ else /* if (Size == 1) */
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS64));
+ }
+ }
+ }
+
+ private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits)
+ {
+ if (Size < 0 || Size > 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ EmitF2iFBitsMul(Context, Size, FBits);
+
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU32));
+ }
+ else /* if (Size == 1) */
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU32));
+ }
+ }
+ else
+ {
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU64));
+ }
+ else /* if (Size == 1) */
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU64));
+ }
+ }
+ }
+
+ private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits)
+ {
+ if (FBits != 0)
+ {
+ if (Size == 0)
+ {
+ Context.EmitLdc_R4(MathF.Pow(2, FBits));
+ }
+ else if (Size == 1)
+ {
+ Context.EmitLdc_R8(Math.Pow(2, FBits));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.Emit(OpCodes.Mul);
+ }
+ }
+
+ private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits)
+ {
+ if (FBits != 0)
+ {
+ if (Size == 0)
+ {
+ Context.EmitLdc_R4(1f / MathF.Pow(2, FBits));
+ }
+ else if (Size == 1)
+ {
+ Context.EmitLdc_R8(1 / Math.Pow(2, FBits));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.Emit(OpCodes.Mul);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
new file mode 100644
index 00000000..20c8be26
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
@@ -0,0 +1,616 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection;
+
+namespace ChocolArm64.Instruction
+{
+ static class AInstEmitSimdHelper
+ {
+ [Flags]
+ public enum OperFlags
+ {
+ Rd = 1 << 0,
+ Rn = 1 << 1,
+ Rm = 1 << 2,
+ Ra = 1 << 3,
+
+ RnRm = Rn | Rm,
+ RdRn = Rd | Rn,
+ RaRnRm = Ra | Rn | Rm,
+ RdRnRm = Rd | Rn | Rm
+ }
+
+ public static int GetImmShl(AOpCodeSimdShImm Op)
+ {
+ return Op.Imm - (8 << Op.Size);
+ }
+
+ public static int GetImmShr(AOpCodeSimdShImm Op)
+ {
+ return (8 << (Op.Size + 1)) - Op.Imm;
+ }
+
+ public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name)
+ {
+ IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+ MethodInfo MthdInfo;
+
+ if (Op.Size == 0)
+ {
+ MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) });
+ }
+ else if (Op.Size == 1)
+ {
+ MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) });
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+
+ Context.EmitCall(MthdInfo);
+ }
+
+ public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name)
+ {
+ IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+ MethodInfo MthdInfo;
+
+ if (Op.Size == 0)
+ {
+ MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) });
+ }
+ else if (Op.Size == 1)
+ {
+ MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) });
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+
+ Context.EmitCall(MthdInfo);
+ }
+
+ public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode)
+ {
+ IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+ Context.EmitLdc_I4((int)RoundMode);
+
+ MethodInfo MthdInfo;
+
+ Type[] Types = new Type[] { null, typeof(MidpointRounding) };
+
+ Types[0] = Op.Size == 0
+ ? typeof(float)
+ : typeof(double);
+
+ if (Op.Size == 0)
+ {
+ MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
+ }
+ else if (Op.Size == 1)
+ {
+ MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+
+ Context.EmitCall(MthdInfo);
+ }
+
+ public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOp(Context, Emit, OperFlags.Rn, true);
+ }
+
+ public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOp(Context, Emit, OperFlags.RnRm, true);
+ }
+
+ public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOp(Context, Emit, OperFlags.Rn, false);
+ }
+
+ public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOp(Context, Emit, OperFlags.RnRm, false);
+ }
+
+ public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false);
+ }
+
+ public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ if (Opers.HasFlag(OperFlags.Rd))
+ {
+ EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rn))
+ {
+ EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rm))
+ {
+ EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed);
+ }
+
+ Emit();
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOpF(Context, Emit, OperFlags.Rn);
+ }
+
+ public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOpF(Context, Emit, OperFlags.RnRm);
+ }
+
+ public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit)
+ {
+ EmitScalarOpF(Context, Emit, OperFlags.RaRnRm);
+ }
+
+ public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+
+ if (Opers.HasFlag(OperFlags.Ra))
+ {
+ EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rn))
+ {
+ EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rm))
+ {
+ EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF);
+ }
+
+ Emit();
+
+ EmitScalarSetF(Context, Op.Rd, SizeF);
+ }
+
+ public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOpF(Context, Emit, OperFlags.RnRm);
+ }
+
+ public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOpF(Context, Emit, OperFlags.RdRnRm);
+ }
+
+ public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
+ {
+ AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
+
+ EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false);
+ }
+
+ public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
+ {
+ AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
+
+ EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true);
+ }
+
+ public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++)
+ {
+ if (Opers.HasFlag(OperFlags.Rd))
+ {
+ EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rn))
+ {
+ EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rm))
+ {
+ EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
+ }
+
+ Emit();
+
+ EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int SizeF = Op.Size & 1;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++)
+ {
+ if (Ternary)
+ {
+ EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
+ }
+
+ EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+ EmitVectorExtractF(Context, Op.Rm, Elem, SizeF);
+
+ Emit();
+
+ EmitVectorInsertTmpF(Context, Index, SizeF);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOp(Context, Emit, OperFlags.Rn, true);
+ }
+
+ public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOp(Context, Emit, OperFlags.RnRm, true);
+ }
+
+ public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOp(Context, Emit, OperFlags.Rn, false);
+ }
+
+ public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOp(Context, Emit, OperFlags.RnRm, false);
+ }
+
+ public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false);
+ }
+
+ public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ if (Opers.HasFlag(OperFlags.Rd))
+ {
+ EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rn))
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
+ }
+
+ if (Opers.HasFlag(OperFlags.Rm))
+ {
+ EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed);
+ }
+
+ Emit();
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorImmOp(Context, Emit, false);
+ }
+
+ public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorImmOp(Context, Emit, true);
+ }
+
+ public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary)
+ {
+ AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ if (Binary)
+ {
+ EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
+ }
+
+ Context.EmitLdc_I8(Op.Imm);
+
+ Emit();
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void EmitVectorWidenRmBinaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorWidenRmBinaryOp(Context, Emit, true);
+ }
+
+ public static void EmitVectorWidenRmBinaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorWidenRmBinaryOp(Context, Emit, false);
+ }
+
+ public static void EmitVectorWidenRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
+ EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);
+
+ Emit();
+
+ EmitVectorInsertTmp(Context, Index, Op.Size + 1);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+ }
+
+ public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorWidenRnRmBinaryOp(Context, Emit, true);
+ }
+
+ public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit)
+ {
+ EmitVectorWidenRnRmBinaryOp(Context, Emit, false);
+ }
+
+ public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
+ EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);
+
+ Emit();
+
+ EmitVectorInsertTmp(Context, Index, Op.Size + 1);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+ }
+
+ public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size)
+ {
+ EmitVectorZeroAll(Context, Reg);
+ EmitVectorInsert(Context, Reg, 0, Size);
+ }
+
+ public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size)
+ {
+ EmitVectorZeroAll(Context, Reg);
+ EmitVectorInsertF(Context, Reg, 0, Size);
+ }
+
+ public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size)
+ {
+ EmitVectorExtract(Context, Reg, Index, Size, true);
+ }
+
+ public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size)
+ {
+ EmitVectorExtract(Context, Reg, Index, Size, false);
+ }
+
+ public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
+ {
+ if (Size < 0 || Size > 3)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+ Context.EmitLdvec(Reg);
+ Context.EmitLdc_I4(Index);
+ Context.EmitLdc_I4(Size);
+
+ ASoftFallback.EmitCall(Context, Signed
+ ? nameof(ASoftFallback.VectorExtractIntSx)
+ : nameof(ASoftFallback.VectorExtractIntZx));
+ }
+
+ public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
+ {
+ Context.EmitLdvec(Reg);
+ Context.EmitLdc_I4(Index);
+
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle));
+ }
+ else if (Size == 1)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+ }
+
+ public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd)
+ {
+ EmitVectorZeroLower(Context, Rd);
+ EmitVectorZeroUpper(Context, Rd);
+ }
+
+ public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd)
+ {
+ EmitVectorInsert(Context, Rd, 0, 3, 0);
+ }
+
+ public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd)
+ {
+ EmitVectorInsert(Context, Rd, 1, 3, 0);
+ }
+
+ public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
+ {
+ if (Size < 0 || Size > 3)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.EmitLdvec(Reg);
+ Context.EmitLdc_I4(Index);
+ Context.EmitLdc_I4(Size);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
+
+ Context.EmitStvec(Reg);
+ }
+
+ public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
+ {
+ if (Size < 0 || Size > 3)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitLdc_I4(Index);
+ Context.EmitLdc_I4(Size);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
+
+ Context.EmitStvectmp();
+ }
+
+ public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
+ {
+ if (Size < 0 || Size > 3)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.EmitLdc_I8(Value);
+ Context.EmitLdvec(Reg);
+ Context.EmitLdc_I4(Index);
+ Context.EmitLdc_I4(Size);
+
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt));
+
+ Context.EmitStvec(Reg);
+ }
+
+ public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
+ {
+ Context.EmitLdvec(Reg);
+ Context.EmitLdc_I4(Index);
+
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle));
+ }
+ else if (Size == 1)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.EmitStvec(Reg);
+ }
+
+ public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
+ {
+ Context.EmitLdvectmp();
+ Context.EmitLdc_I4(Index);
+
+ if (Size == 0)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle));
+ }
+ else if (Size == 1)
+ {
+ ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ Context.EmitStvectmp();
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs
new file mode 100644
index 00000000..ea4b17b3
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs
@@ -0,0 +1,69 @@
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void And_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And));
+ }
+
+ public static void Bic_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () =>
+ {
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.And);
+ });
+ }
+
+ public static void Bic_Vi(AILEmitterCtx Context)
+ {
+ EmitVectorImmBinaryOp(Context, () =>
+ {
+ Context.Emit(OpCodes.Not);
+ Context.Emit(OpCodes.And);
+ });
+ }
+
+ public static void Bsl_V(AILEmitterCtx Context)
+ {
+ EmitVectorTernaryOpZx(Context, () =>
+ {
+ Context.EmitSttmp();
+ Context.EmitLdtmp();
+
+ Context.Emit(OpCodes.Xor);
+ Context.Emit(OpCodes.And);
+
+ Context.EmitLdtmp();
+
+ Context.Emit(OpCodes.Xor);
+ });
+ }
+
+ public static void Eor_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor));
+ }
+
+ public static void Not_V(AILEmitterCtx Context)
+ {
+ EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not));
+ }
+
+ public static void Orr_V(AILEmitterCtx Context)
+ {
+ EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or));
+ }
+
+ public static void Orr_Vi(AILEmitterCtx Context)
+ {
+ EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or));
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdMemory.cs b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs
new file mode 100644
index 00000000..d98ec012
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs
@@ -0,0 +1,184 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Ld__Vms(AILEmitterCtx Context)
+ {
+ EmitSimdMemMs(Context, IsLoad: true);
+ }
+
+ public static void Ld__Vss(AILEmitterCtx Context)
+ {
+ EmitSimdMemSs(Context, IsLoad: true);
+ }
+
+ public static void St__Vms(AILEmitterCtx Context)
+ {
+ EmitSimdMemMs(Context, IsLoad: false);
+ }
+
+ public static void St__Vss(AILEmitterCtx Context)
+ {
+ EmitSimdMemSs(Context, IsLoad: false);
+ }
+
+ private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad)
+ {
+ AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp;
+
+ int Offset = 0;
+
+ for (int Rep = 0; Rep < Op.Reps; Rep++)
+ for (int Elem = 0; Elem < Op.Elems; Elem++)
+ for (int SElem = 0; SElem < Op.SElems; SElem++)
+ {
+ int Rtt = (Op.Rt + Rep + SElem) & 0x1f;
+
+ if (IsLoad)
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdc_I8(Offset);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitReadZxCall(Context, Op.Size);
+
+ EmitVectorInsert(Context, Rtt, Elem, Op.Size);
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1)
+ {
+ EmitVectorZeroUpper(Context, Rtt);
+ }
+ }
+ else
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdc_I8(Offset);
+
+ Context.Emit(OpCodes.Add);
+
+ EmitVectorExtractZx(Context, Rtt, Elem, Op.Size);
+
+ EmitWriteCall(Context, Op.Size);
+ }
+
+ Offset += 1 << Op.Size;
+ }
+
+ if (Op.WBack)
+ {
+ EmitSimdMemWBack(Context, Offset);
+ }
+ }
+
+ private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad)
+ {
+ AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp;
+
+ int Offset = 0;
+
+ void EmitMemAddress()
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rn);
+ Context.EmitLdc_I8(Offset);
+
+ Context.Emit(OpCodes.Add);
+ }
+
+ if (Op.Replicate)
+ {
+ //Only loads uses the replicate mode.
+ if (!IsLoad)
+ {
+ throw new InvalidOperationException();
+ }
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int SElem = 0; SElem < Op.SElems; SElem++)
+ {
+ int Rt = (Op.Rt + SElem) & 0x1f;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitMemAddress();
+
+ EmitReadZxCall(Context, Op.Size);
+
+ EmitVectorInsert(Context, Rt, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Rt);
+ }
+
+ Offset += 1 << Op.Size;
+ }
+ }
+ else
+ {
+ for (int SElem = 0; SElem < Op.SElems; SElem++)
+ {
+ int Rt = (Op.Rt + SElem) & 0x1f;
+
+ if (IsLoad)
+ {
+ EmitMemAddress();
+
+ EmitReadZxCall(Context, Op.Size);
+
+ EmitVectorInsert(Context, Rt, Op.Index, Op.Size);
+ }
+ else
+ {
+ EmitMemAddress();
+
+ EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size);
+
+ EmitWriteCall(Context, Op.Size);
+ }
+
+ Offset += 1 << Op.Size;
+ }
+ }
+
+ if (Op.WBack)
+ {
+ EmitSimdMemWBack(Context, Offset);
+ }
+ }
+
+ private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset)
+ {
+ AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp;
+
+ Context.EmitLdint(Op.Rn);
+
+ if (Op.Rm != AThreadState.ZRIndex)
+ {
+ Context.EmitLdint(Op.Rm);
+ }
+ else
+ {
+ Context.EmitLdc_I8(Offset);
+ }
+
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitStint(Op.Rn);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs
new file mode 100644
index 00000000..aabb8f34
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs
@@ -0,0 +1,333 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Dup_Gp(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ Context.EmitLdintzr(Op.Rn);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Dup_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Dup_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Fcsel_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp;
+
+ AILLabel LblTrue = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ Context.EmitCondBranch(LblTrue, Op.Cond);
+
+ EmitVectorExtractF(Context, Op.Rm, 0, Op.Size);
+
+ Context.Emit(OpCodes.Br_S, LblEnd);
+
+ Context.MarkLabel(LblTrue);
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ Context.MarkLabel(LblEnd);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Fmov_Ftoi(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, 3);
+
+ EmitIntZeroHigherIfNeeded(Context);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Fmov_Ftoi1(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, 1, 3);
+
+ EmitIntZeroHigherIfNeeded(Context);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Fmov_Itof(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ EmitIntZeroHigherIfNeeded(Context);
+
+ EmitScalarSet(Context, Op.Rd, 3);
+ }
+
+ public static void Fmov_Itof1(AILEmitterCtx Context)
+ {
+ AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ EmitIntZeroHigherIfNeeded(Context);
+
+ EmitVectorInsert(Context, Op.Rd, 1, 3);
+ }
+
+ public static void Fmov_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+ EmitScalarSetF(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Fmov_Si(AILEmitterCtx Context)
+ {
+ AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp;
+
+ Context.EmitLdc_I8(Op.Imm);
+
+ EmitScalarSet(Context, Op.Rd, Op.Size + 2);
+ }
+
+ public static void Fmov_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp;
+
+ int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 4 : 2;
+
+ for (int Index = 0; Index < (Elems >> Op.Size); Index++)
+ {
+ Context.EmitLdc_I8(Op.Imm);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Ins_Gp(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ Context.EmitLdintzr(Op.Rn);
+
+ EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size);
+ }
+
+ public static void Ins_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size);
+
+ EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size);
+ }
+
+ public static void Movi_V(AILEmitterCtx Context)
+ {
+ EmitVectorImmUnaryOp(Context, () => { });
+ }
+
+ public static void Mvni_V(AILEmitterCtx Context)
+ {
+ EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not));
+ }
+
+ public static void Tbl_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp;
+
+ Context.EmitLdvec(Op.Rm);
+
+ for (int Index = 0; Index < Op.Size; Index++)
+ {
+ Context.EmitLdvec((Op.Rn + Index) & 0x1f);
+ }
+
+ switch (Op.Size)
+ {
+ case 1: ASoftFallback.EmitCall(Context,
+ nameof(ASoftFallback.Tbl1_V64),
+ nameof(ASoftFallback.Tbl1_V128)); break;
+
+ case 2: ASoftFallback.EmitCall(Context,
+ nameof(ASoftFallback.Tbl2_V64),
+ nameof(ASoftFallback.Tbl2_V128)); break;
+
+ case 3: ASoftFallback.EmitCall(Context,
+ nameof(ASoftFallback.Tbl3_V64),
+ nameof(ASoftFallback.Tbl3_V128)); break;
+
+ case 4: ASoftFallback.EmitCall(Context,
+ nameof(ASoftFallback.Tbl4_V64),
+ nameof(ASoftFallback.Tbl4_V128)); break;
+
+ default: throw new InvalidOperationException();
+ }
+
+ Context.EmitStvec(Op.Rd);
+ }
+
+ public static void Umov_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size);
+
+ Context.EmitStintzr(Op.Rd);
+ }
+
+ public static void Uzp1_V(AILEmitterCtx Context)
+ {
+ EmitVectorUnzip(Context, Part: 0);
+ }
+
+ public static void Uzp2_V(AILEmitterCtx Context)
+ {
+ EmitVectorUnzip(Context, Part: 1);
+ }
+
+ public static void Xtn_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
+
+ EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
+ }
+
+ if (Part == 0)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ public static void Zip1_V(AILEmitterCtx Context)
+ {
+ EmitVectorZip(Context, Part: 0);
+ }
+
+ public static void Zip2_V(AILEmitterCtx Context)
+ {
+ EmitVectorZip(Context, Part: 1);
+ }
+
+ private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context)
+ {
+ if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
+ {
+ Context.Emit(OpCodes.Conv_U4);
+ Context.Emit(OpCodes.Conv_U8);
+ }
+ }
+
+ private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ int Elems = Bytes >> Op.Size;
+ int Half = Elems >> 1;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ int Elem = Part + ((Index & (Half - 1)) << 1);
+
+ EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ private static void EmitVectorZip(AILEmitterCtx Context, int Part)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ int Elems = Bytes >> Op.Size;
+ int Half = Elems >> 1;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ int Elem = Part * Half + (Index >> 1);
+
+ EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size);
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs
new file mode 100644
index 00000000..8740ba4d
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs
@@ -0,0 +1,297 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instruction.AInstEmitSimdHelper;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Shl_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size);
+
+ Context.EmitLdc_I4(GetImmShl(Op));
+
+ Context.Emit(OpCodes.Shl);
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Shl_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Shift = Op.Imm - (8 << Op.Size);
+
+ EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
+ }
+
+ public static void Shrn_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Shift = (8 << (Op.Size + 1)) - Op.Imm;
+
+ EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift);
+ }
+
+ public static void Sshl_V(AILEmitterCtx Context)
+ {
+ EmitVectorShl(Context, Signed: true);
+ }
+
+ public static void Sshll_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Shift = Op.Imm - (8 << Op.Size);
+
+ EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), Shift);
+ }
+
+ public static void Sshr_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size);
+
+ Context.EmitLdc_I4(GetImmShr(Op));
+
+ Context.Emit(OpCodes.Shr);
+
+ EmitScalarSet(Context, Op.Rd, Op.Size);
+ }
+
+ public static void Sshr_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Shift = (8 << (Op.Size + 1)) - Op.Imm;
+
+ EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift);
+ }
+
+ public static void Ushl_V(AILEmitterCtx Context)
+ {
+ EmitVectorShl(Context, Signed: false);
+ }
+
+ public static void Ushll_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Shift = Op.Imm - (8 << Op.Size);
+
+ EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
+ }
+
+ public static void Ushr_S(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ EmitScalarUnaryOpZx(Context, () =>
+ {
+ Context.EmitLdc_I4(GetImmShr(Op));
+
+ Context.Emit(OpCodes.Shr_Un);
+ });
+ }
+
+ public static void Ushr_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ EmitVectorUnaryOpZx(Context, () =>
+ {
+ Context.EmitLdc_I4(GetImmShr(Op));
+
+ Context.Emit(OpCodes.Shr_Un);
+ });
+ }
+
+ public static void Usra_V(AILEmitterCtx Context)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ Action Emit = () =>
+ {
+ Context.EmitLdc_I4(GetImmShr(Op));
+
+ Context.Emit(OpCodes.Shr_Un);
+ Context.Emit(OpCodes.Add);
+ };
+
+ EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false);
+ }
+
+ private static void EmitVectorShl(AILEmitterCtx Context, bool Signed)
+ {
+ //This instruction shifts the value on vector A by the number of bits
+ //specified on the signed, lower 8 bits of vector B. If the shift value
+ //is greater or equal to the data size of each lane, then the result is zero.
+ //Additionally, negative shifts produces right shifts by the negated shift value.
+ AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+ int MaxShift = 8 << Op.Size;
+
+ Action Emit = () =>
+ {
+ AILLabel LblShl = new AILLabel();
+ AILLabel LblZero = new AILLabel();
+ AILLabel LblEnd = new AILLabel();
+
+ void EmitShift(OpCode ILOp)
+ {
+ Context.Emit(OpCodes.Dup);
+
+ Context.EmitLdc_I4(MaxShift);
+
+ Context.Emit(OpCodes.Bge_S, LblZero);
+ Context.Emit(ILOp);
+ Context.Emit(OpCodes.Br_S, LblEnd);
+ }
+
+ Context.Emit(OpCodes.Conv_I1);
+ Context.Emit(OpCodes.Dup);
+
+ Context.EmitLdc_I4(0);
+
+ Context.Emit(OpCodes.Bge_S, LblShl);
+ Context.Emit(OpCodes.Neg);
+
+ EmitShift(Signed
+ ? OpCodes.Shr
+ : OpCodes.Shr_Un);
+
+ Context.MarkLabel(LblShl);
+
+ EmitShift(OpCodes.Shl);
+
+ Context.MarkLabel(LblZero);
+
+ Context.Emit(OpCodes.Pop);
+ Context.Emit(OpCodes.Pop);
+
+ Context.EmitLdc_I8(0);
+
+ Context.MarkLabel(LblEnd);
+ };
+
+ if (Signed)
+ {
+ EmitVectorBinaryOpSx(Context, Emit);
+ }
+ else
+ {
+ EmitVectorBinaryOpZx(Context, Emit);
+ }
+ }
+
+ private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmBinaryOp(Context, Emit, Imm, true);
+ }
+
+ private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmBinaryOp(Context, Emit, Imm, false);
+ }
+
+ private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+ for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
+
+ Context.EmitLdc_I4(Imm);
+
+ Emit();
+
+ EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+ }
+
+ if (Op.RegisterSize == ARegisterSize.SIMD64)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true);
+ }
+
+ private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false);
+ }
+
+ private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
+
+ Context.EmitLdc_I4(Imm);
+
+ Emit();
+
+ EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
+ }
+
+ if (Part == 0)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
+ private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true);
+ }
+
+ private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
+ {
+ EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false);
+ }
+
+ private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
+ {
+ AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
+
+ Context.EmitLdc_I4(Imm);
+
+ Emit();
+
+ EmitVectorInsertTmp(Context, Index, Op.Size + 1);
+ }
+
+ Context.EmitLdvectmp();
+ Context.EmitStvec(Op.Rd);
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs
new file mode 100644
index 00000000..6754be7a
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitSystem.cs
@@ -0,0 +1,122 @@
+using ChocolArm64.Decoder;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instruction
+{
+ static partial class AInstEmit
+ {
+ public static void Mrs(AILEmitterCtx Context)
+ {
+ AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+ string PropName;
+
+ switch (GetPackedId(Op))
+ {
+ case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break;
+ case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break;
+ case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
+ case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
+ case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
+ case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
+ case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
+
+ default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
+ }
+
+ Context.EmitCallPropGet(typeof(AThreadState), PropName);
+
+ PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName);
+
+ if (PropInfo.PropertyType != typeof(long) &&
+ PropInfo.PropertyType != typeof(ulong))
+ {
+ Context.Emit(OpCodes.Conv_U8);
+ }
+
+ Context.EmitStintzr(Op.Rt);
+ }
+
+ public static void Msr(AILEmitterCtx Context)
+ {
+ AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
+
+ Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+ Context.EmitLdintzr(Op.Rt);
+
+ string PropName;
+
+ switch (GetPackedId(Op))
+ {
+ case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
+ case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
+ case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
+
+ default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}");
+ }
+
+ PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName);
+
+ if (PropInfo.PropertyType != typeof(long) &&
+ PropInfo.PropertyType != typeof(ulong))
+ {
+ Context.Emit(OpCodes.Conv_U4);
+ }
+
+ Context.EmitCallPropSet(typeof(AThreadState), PropName);
+ }
+
+ public static void Nop(AILEmitterCtx Context)
+ {
+ //Do nothing.
+ }
+
+ public static void Sys(AILEmitterCtx Context)
+ {
+ //This instruction is used to do some operations on the CPU like cache invalidation,
+ //address translation and the like.
+ //We treat it as no-op here since we don't have any cache being emulated anyway.
+ AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
+
+ switch (GetPackedId(Op))
+ {
+ case 0b11_011_0111_0100_001:
+ {
+ //DC ZVA
+ for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8)
+ {
+ Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
+ Context.EmitLdint(Op.Rt);
+ Context.EmitLdc_I(Offs);
+
+ Context.Emit(OpCodes.Add);
+
+ Context.EmitLdc_I8(0);
+
+ AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
+ }
+ break;
+ }
+ }
+ }
+
+ private static int GetPackedId(AOpCodeSystem Op)
+ {
+ int Id;
+
+ Id = Op.Op2 << 0;
+ Id |= Op.CRm << 3;
+ Id |= Op.CRn << 7;
+ Id |= Op.Op1 << 11;
+ Id |= Op.Op0 << 14;
+
+ return Id;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitter.cs b/ChocolArm64/Instruction/AInstEmitter.cs
new file mode 100644
index 00000000..8712a736
--- /dev/null
+++ b/ChocolArm64/Instruction/AInstEmitter.cs
@@ -0,0 +1,6 @@
+using ChocolArm64.Translation;
+
+namespace ChocolArm64.Instruction
+{
+ delegate void AInstEmitter(AILEmitterCtx Context);
+} \ No newline at end of file
diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs
new file mode 100644
index 00000000..a57966ba
--- /dev/null
+++ b/ChocolArm64/Instruction/ASoftFallback.cs
@@ -0,0 +1,316 @@
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace ChocolArm64.Instruction
+{
+ static class ASoftFallback
+ {
+ public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128)
+ {
+ bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64;
+
+ Context.EmitCall(typeof(ASoftFallback), IsSimd64 ? Name64 : Name128);
+ }
+
+ public static void EmitCall(AILEmitterCtx Context, string MthdName)
+ {
+ Context.EmitCall(typeof(ASoftFallback), MthdName);
+ }
+
+ public static uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32);
+ public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
+
+ private static ulong CountLeadingZeros(ulong Value, int Size)
+ {
+ int HighBit = Size - 1;
+
+ for (int Bit = HighBit; Bit >= 0; Bit--)
+ {
+ if (((Value >> Bit) & 1) != 0)
+ {
+ return (ulong)(HighBit - Bit);
+ }
+ }
+
+ return (ulong)Size;
+ }
+
+ public static uint ReverseBits32(uint Value)
+ {
+ Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1);
+ Value = ((Value & 0xcccccccc) >> 2) | ((Value & 0x33333333) << 2);
+ Value = ((Value & 0xf0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f) << 4);
+ Value = ((Value & 0xff00ff00) >> 8) | ((Value & 0x00ff00ff) << 8);
+
+ return (Value >> 16) | (Value << 16);
+ }
+
+ public static ulong ReverseBits64(ulong Value)
+ {
+ Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((Value & 0x5555555555555555) << 1);
+ Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2);
+ Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4);
+ Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8);
+ Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
+
+ return (Value >> 32) | (Value << 32);
+ }
+
+ public static uint ReverseBytes16_32(uint Value) => (uint)ReverseBytes16_64(Value);
+ public static uint ReverseBytes32_32(uint Value) => (uint)ReverseBytes32_64(Value);
+
+ public static ulong ReverseBytes16_64(ulong Value) => ReverseBytes(Value, RevSize.Rev16);
+ public static ulong ReverseBytes32_64(ulong Value) => ReverseBytes(Value, RevSize.Rev32);
+ public static ulong ReverseBytes64(ulong Value) => ReverseBytes(Value, RevSize.Rev64);
+
+ private enum RevSize
+ {
+ Rev16,
+ Rev32,
+ Rev64
+ }
+
+ private static ulong ReverseBytes(ulong Value, RevSize Size)
+ {
+ Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8);
+
+ if (Size == RevSize.Rev16)
+ {
+ return Value;
+ }
+
+ Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
+
+ if (Size == RevSize.Rev32)
+ {
+ return Value;
+ }
+
+ Value = ((Value & 0xffffffff00000000) >> 32) | ((Value & 0x00000000ffffffff) << 32);
+
+ if (Size == RevSize.Rev64)
+ {
+ return Value;
+ }
+
+ throw new ArgumentException(nameof(Size));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SatF32ToS32(float Value)
+ {
+ if (float.IsNaN(Value)) return 0;
+
+ return Value > int.MaxValue ? int.MaxValue :
+ Value < int.MinValue ? int.MinValue : (int)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long SatF32ToS64(float Value)
+ {
+ if (float.IsNaN(Value)) return 0;
+
+ return Value > long.MaxValue ? long.MaxValue :
+ Value < long.MinValue ? long.MinValue : (long)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint SatF32ToU32(float Value)
+ {
+ if (float.IsNaN(Value)) return 0;
+
+ return Value > uint.MaxValue ? uint.MaxValue :
+ Value < uint.MinValue ? uint.MinValue : (uint)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong SatF32ToU64(float Value)
+ {
+ if (float.IsNaN(Value)) return 0;
+
+ return Value > ulong.MaxValue ? ulong.MaxValue :
+ Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SatF64ToS32(double Value)
+ {
+ if (double.IsNaN(Value)) return 0;
+
+ return Value > int.MaxValue ? int.MaxValue :
+ Value < int.MinValue ? int.MinValue : (int)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long SatF64ToS64(double Value)
+ {
+ if (double.IsNaN(Value)) return 0;
+
+ return Value > long.MaxValue ? long.MaxValue :
+ Value < long.MinValue ? long.MinValue : (long)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint SatF64ToU32(double Value)
+ {
+ if (double.IsNaN(Value)) return 0;
+
+ return Value > uint.MaxValue ? uint.MaxValue :
+ Value < uint.MinValue ? uint.MinValue : (uint)Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong SatF64ToU64(double Value)
+ {
+ if (double.IsNaN(Value)) return 0;
+
+ return Value > ulong.MaxValue ? ulong.MaxValue :
+ Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
+ }
+
+ public static long SMulHi128(long LHS, long RHS)
+ {
+ return (long)(BigInteger.Multiply(LHS, RHS) >> 64);
+ }
+
+ public static ulong UMulHi128(ulong LHS, ulong RHS)
+ {
+ return (ulong)(BigInteger.Multiply(LHS, RHS) >> 64);
+ }
+
+ public static int CountSetBits8(byte Value)
+ {
+ return (Value >> 0) & 1 + (Value >> 1) & 1 +
+ (Value >> 2) & 1 + (Value >> 3) & 1 +
+ (Value >> 4) & 1 + (Value >> 5) & 1 +
+ (Value >> 6) & 1 + (Value >> 7);
+ }
+
+ public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
+ {
+ return Tbl(Vector, 8, Tb0);
+ }
+
+ public static AVec Tbl1_V128(AVec Vector, AVec Tb0)
+ {
+ return Tbl(Vector, 16, Tb0);
+ }
+
+ public static AVec Tbl2_V64(AVec Vector, AVec Tb0, AVec Tb1)
+ {
+ return Tbl(Vector, 8, Tb0, Tb1);
+ }
+
+ public static AVec Tbl2_V128(AVec Vector, AVec Tb0, AVec Tb1)
+ {
+ return Tbl(Vector, 16, Tb0, Tb1);
+ }
+
+ public static AVec Tbl3_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2)
+ {
+ return Tbl(Vector, 8, Tb0, Tb1, Tb2);
+ }
+
+ public static AVec Tbl3_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2)
+ {
+ return Tbl(Vector, 16, Tb0, Tb1, Tb2);
+ }
+
+ public static AVec Tbl4_V64(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3)
+ {
+ return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3);
+ }
+
+ public static AVec Tbl4_V128(AVec Vector, AVec Tb0, AVec Tb1, AVec Tb2, AVec Tb3)
+ {
+ return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3);
+ }
+
+ private static AVec Tbl(AVec Vector, int Bytes, params AVec[] Tb)
+ {
+ AVec Res = new AVec();
+
+ byte[] Table = new byte[Tb.Length * 16];
+
+ for (int Index = 0; Index < Tb.Length; Index++)
+ for (int Index2 = 0; Index2 < 16; Index2++)
+ {
+ Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0);
+ }
+
+ for (int Index = 0; Index < Bytes; Index++)
+ {
+ byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0);
+
+ if (TblIdx < Table.Length)
+ {
+ Res = VectorInsertInt(Table[TblIdx], Res, Index, 0);
+ }
+ }
+
+ return Res;
+ }
+
+ public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size)
+ {
+ switch (Size)
+ {
+ case 0: return Vector.ExtractByte (Index);
+ case 1: return Vector.ExtractUInt16(Index);
+ case 2: return Vector.ExtractUInt32(Index);
+ case 3: return Vector.ExtractUInt64(Index);
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ public static long VectorExtractIntSx(AVec Vector, int Index, int Size)
+ {
+ switch (Size)
+ {
+ case 0: return (sbyte)Vector.ExtractByte (Index);
+ case 1: return (short)Vector.ExtractUInt16(Index);
+ case 2: return (int)Vector.ExtractUInt32(Index);
+ case 3: return (long)Vector.ExtractUInt64(Index);
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+
+ public static float VectorExtractSingle(AVec Vector, int Index)
+ {
+ return Vector.ExtractSingle(Index);
+ }
+
+ public static double VectorExtractDouble(AVec Vector, int Index)
+ {
+ return Vector.ExtractDouble(Index);
+ }
+
+ public static AVec VectorInsertSingle(float Value, AVec Vector, int Index)
+ {
+ return AVec.InsertSingle(Vector, Index, Value);
+ }
+
+ public static AVec VectorInsertDouble(double Value, AVec Vector, int Index)
+ {
+ return AVec.InsertDouble(Vector, Index, Value);
+ }
+
+ public static AVec VectorInsertInt(ulong Value, AVec Vector, int Index, int Size)
+ {
+ switch (Size)
+ {
+ case 0: return AVec.InsertByte (Vector, Index, (byte)Value);
+ case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
+ case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
+ case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(Size));
+ }
+ }
+} \ No newline at end of file