diff options
Diffstat (limited to 'ChocolArm64/Instructions/InstEmitAluHelper.cs')
-rw-r--r-- | ChocolArm64/Instructions/InstEmitAluHelper.cs | 317 |
1 files changed, 279 insertions, 38 deletions
diff --git a/ChocolArm64/Instructions/InstEmitAluHelper.cs b/ChocolArm64/Instructions/InstEmitAluHelper.cs index 97c50564..db8fd0e5 100644 --- a/ChocolArm64/Instructions/InstEmitAluHelper.cs +++ b/ChocolArm64/Instructions/InstEmitAluHelper.cs @@ -1,6 +1,7 @@ using ChocolArm64.Decoders; using ChocolArm64.State; using ChocolArm64.Translation; +using System; using System.Reflection.Emit; namespace ChocolArm64.Instructions @@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions context.EmitLdtmp(); context.EmitLdtmp(); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Ceq); @@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions context.EmitLdtmp(); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Or); @@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions //C = Rd < Rn context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); @@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Xor); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.Not); @@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions public static void EmitSbcsCCheck(ILEmitterCtx context) { //C = (Rn == Rm && CIn) || Rn > Rm - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Ceq); @@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.And); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Cgt_Un); context.Emit(OpCodes.Or); @@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions public static void EmitSubsCCheck(ILEmitterCtx context) { //C = Rn == Rm || Rn > Rm = !(Rn < Rm) - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Clt_Un); @@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); - EmitDataLoadRn(context); + EmitAluLoadRn(context); context.Emit(OpCodes.Xor); - EmitDataLoadOpers(context); + EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.And); @@ -120,35 +121,76 @@ namespace ChocolArm64.Instructions context.EmitStflg((int)PState.VBit); } - public static void EmitDataLoadRm(ILEmitterCtx context) + public static void EmitAluLoadRm(ILEmitterCtx context) { - context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm); + if (context.CurrOp is IOpCodeAluRs64 op) + { + context.EmitLdintzr(op.Rm); + } + else if (context.CurrOp is OpCodeAluRsImm32 op32) + { + InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm); + } + else + { + throw new InvalidOperationException(); + } } - public static void EmitDataLoadOpers(ILEmitterCtx context) + public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true) { - EmitDataLoadRn(context); - EmitDataLoadOper2(context); + EmitAluLoadRn(context); + EmitAluLoadOper2(context, setCarry); } - public static void EmitDataLoadRn(ILEmitterCtx context) + public static void EmitAluLoadRn(ILEmitterCtx context) { - IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; - - if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + if (context.CurrOp is IOpCodeAlu64 op) { - context.EmitLdintzr(op.Rn); + if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + { + context.EmitLdintzr(op.Rn); + } + else + { + context.EmitLdint(op.Rn); + } + } + else if (context.CurrOp is IOpCodeAlu32 op32) + { + InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn); } else { - context.EmitLdint(op.Rn); + throw new InvalidOperationException(); } } - public static void EmitDataLoadOper2(ILEmitterCtx context) + public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true) { switch (context.CurrOp) { + //ARM32. + case OpCodeAluImm32 op: + context.EmitLdc_I4(op.Imm); + + if (op.SetFlags && op.IsRotated) + { + context.EmitLdc_I4((int)((uint)op.Imm >> 31)); + + context.EmitStflg((int)PState.CBit); + } + break; + + case OpCodeAluRsImm32 op: + EmitLoadRmShiftedByImmediate(context, op, setCarry); + break; + + case OpCodeAluImm8T16 op: + context.EmitLdc_I4(op.Imm); + break; + + //ARM64. case IOpCodeAluImm64 op: context.EmitLdc_I(op.Imm); break; @@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions context.EmitCast(op.IntType); context.EmitLsl(op.Shift); break; - } - } - public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false); - public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true); - - public static void EmitDataStore(ILEmitterCtx context, bool setFlags) - { - IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; - - if (setFlags || op is IOpCodeAluRs64) - { - context.EmitStintzr(op.Rd); - } - else - { - context.EmitStint(op.Rd); + default: throw new InvalidOperationException(); } } @@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.And); context.EmitStflg((int)PState.NBit); } + + //ARM32 helpers. + private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCodeAluRsImm32 op, bool setCarry) + { + int shift = op.Imm; + + if (shift == 0) + { + switch (op.ShiftType) + { + case ShiftType.Lsr: shift = 32; break; + case ShiftType.Asr: shift = 32; break; + case ShiftType.Ror: shift = 1; break; + } + } + + context.EmitLdint(op.Rm); + + if (shift != 0) + { + setCarry &= op.SetFlags; + + switch (op.ShiftType) + { + case ShiftType.Lsl: EmitLslC(context, setCarry, shift); break; + case ShiftType.Lsr: EmitLsrC(context, setCarry, shift); break; + case ShiftType.Asr: EmitAsrC(context, setCarry, shift); break; + case ShiftType.Ror: + if (op.Imm != 0) + { + EmitRorC(context, setCarry, shift); + } + else + { + EmitRrxC(context, setCarry); + } + break; + } + } + } + + private static void EmitLslC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift > 32) + { + EmitShiftByMoreThan32(context, setCarry); + } + else if (shift == 32) + { + if (setCarry) + { + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + else + { + context.Emit(OpCodes.Pop); + } + + context.EmitLdc_I4(0); + } + else + { + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(32 - shift); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + + context.EmitLsl(shift); + } + } + + private static void EmitLsrC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift > 32) + { + EmitShiftByMoreThan32(context, setCarry); + } + else if (shift == 32) + { + if (setCarry) + { + context.EmitLsr(31); + + context.EmitStflg((int)PState.CBit); + } + else + { + context.Emit(OpCodes.Pop); + } + + context.EmitLdc_I4(0); + } + else + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(shift - 1); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + + context.EmitLsr(shift); + } + } + + private static void EmitShiftByMoreThan32(ILEmitterCtx context, bool setCarry) + { + context.Emit(OpCodes.Pop); + + context.EmitLdc_I4(0); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.CBit); + } + } + + private static void EmitAsrC(ILEmitterCtx context, bool setCarry, int shift) + { + if ((uint)shift >= 32) + { + context.EmitAsr(31); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + } + else + { + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(shift - 1); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitStflg((int)PState.CBit); + } + + context.EmitAsr(shift); + } + } + + private static void EmitRorC(ILEmitterCtx context, bool setCarry, int shift) + { + shift &= 0x1f; + + context.EmitRor(shift); + + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLsr(31); + + context.EmitStflg((int)PState.CBit); + } + } + + private static void EmitRrxC(ILEmitterCtx context, bool setCarry) + { + //Rotate right by 1 with carry. + if (setCarry) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.And); + + context.EmitSttmp(); + } + + context.EmitLsr(1); + + context.EmitLdflg((int)PState.CBit); + + context.EmitLsl(31); + + context.Emit(OpCodes.Or); + + if (setCarry) + { + context.EmitLdtmp(); + context.EmitStflg((int)PState.CBit); + } + } } } |