using ChocolArm64.Decoders; using ChocolArm64.State; using ChocolArm64.Translation; using System; using System.Reflection.Emit; namespace ChocolArm64.Instructions { static class InstEmitAluHelper { public static void EmitAdcsCCheck(ILEmitterCtx context) { // C = (Rd == Rn && CIn) || Rd < Rn context.EmitSttmp(); context.EmitLdtmp(); context.EmitLdtmp(); EmitAluLoadRn(context); context.Emit(OpCodes.Ceq); context.EmitLdflg((int)PState.CBit); context.Emit(OpCodes.And); context.EmitLdtmp(); EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Or); context.EmitStflg((int)PState.CBit); } public static void EmitAddsCCheck(ILEmitterCtx context) { // C = Rd < Rn context.Emit(OpCodes.Dup); EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); context.EmitStflg((int)PState.CBit); } public static void EmitAddsVCheck(ILEmitterCtx context) { // V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); EmitAluLoadRn(context); context.Emit(OpCodes.Xor); EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.Not); context.Emit(OpCodes.And); context.EmitLdc_I(0); context.Emit(OpCodes.Clt); context.EmitStflg((int)PState.VBit); } public static void EmitSbcsCCheck(ILEmitterCtx context) { // C = (Rn == Rm && CIn) || Rn > Rm EmitAluLoadOpers(context); context.Emit(OpCodes.Ceq); context.EmitLdflg((int)PState.CBit); context.Emit(OpCodes.And); EmitAluLoadOpers(context); context.Emit(OpCodes.Cgt_Un); context.Emit(OpCodes.Or); context.EmitStflg((int)PState.CBit); } public static void EmitSubsCCheck(ILEmitterCtx context) { // C = Rn == Rm || Rn > Rm = !(Rn < Rm) EmitAluLoadOpers(context); context.Emit(OpCodes.Clt_Un); context.EmitLdc_I4(1); context.Emit(OpCodes.Xor); context.EmitStflg((int)PState.CBit); } public static void EmitSubsVCheck(ILEmitterCtx context) { // V = (Rd ^ Rn) & (Rn ^ Rm) < 0 context.Emit(OpCodes.Dup); EmitAluLoadRn(context); context.Emit(OpCodes.Xor); EmitAluLoadOpers(context); context.Emit(OpCodes.Xor); context.Emit(OpCodes.And); context.EmitLdc_I(0); context.Emit(OpCodes.Clt); context.EmitStflg((int)PState.VBit); } public static void EmitAluLoadRm(ILEmitterCtx context) { if (context.CurrOp is IOpCodeAluRs64 op) { context.EmitLdintzr(op.Rm); } else if (context.CurrOp is OpCode32AluRsImm op32) { InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm); } else { throw new InvalidOperationException(); } } public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true) { EmitAluLoadRn(context); EmitAluLoadOper2(context, setCarry); } public static void EmitAluLoadRn(ILEmitterCtx context) { if (context.CurrOp is IOpCodeAlu64 op) { if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) { context.EmitLdintzr(op.Rn); } else { context.EmitLdint(op.Rn); } } else if (context.CurrOp is IOpCode32Alu op32) { InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn); } else { throw new InvalidOperationException(); } } public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true) { switch (context.CurrOp) { // ARM32. case OpCode32AluImm 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 OpCode32AluRsImm op: EmitLoadRmShiftedByImmediate(context, op, setCarry); break; case OpCodeT16AluImm8 op: context.EmitLdc_I4(op.Imm); break; // ARM64. case IOpCodeAluImm64 op: context.EmitLdc_I(op.Imm); break; case IOpCodeAluRs64 op: context.EmitLdintzr(op.Rm); switch (op.ShiftType) { case ShiftType.Lsl: context.EmitLsl(op.Shift); break; case ShiftType.Lsr: context.EmitLsr(op.Shift); break; case ShiftType.Asr: context.EmitAsr(op.Shift); break; case ShiftType.Ror: context.EmitRor(op.Shift); break; } break; case IOpCodeAluRx64 op: context.EmitLdintzr(op.Rm); context.EmitCast(op.IntType); context.EmitLsl(op.Shift); break; default: throw new InvalidOperationException(); } } public static void EmitSetNzcv(ILEmitterCtx context) { context.Emit(OpCodes.Dup); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.And); context.EmitStflg((int)PState.VBit); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.Shr); context.Emit(OpCodes.Dup); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.And); context.EmitStflg((int)PState.CBit); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.Shr); context.Emit(OpCodes.Dup); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.And); context.EmitStflg((int)PState.ZBit); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.Shr); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.And); context.EmitStflg((int)PState.NBit); } // ARM32 helpers. private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm 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); } } } }