mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-12-15 17:51:16 +01:00
c1bdf19061
* Implement ARM32 memory instructions: LDM, LDR, LDRB, LDRD, LDRH, LDRSB, LDRSH, STM, STR, STRB, STRD, STRH (immediate and register + immediate variants), implement CMP (immediate and register shifted by immediate variants) * Rename some opcode classes and flag masks for consistency * Fix a few suboptimal ARM32 codegen issues, only loads should be considered on decoder when checking if Rt == PC, and only NZCV flags should be considered for comparison optimizations * Take into account Rt2 for LDRD instructions aswell when checking if the instruction changes PC * Re-align arm32 instructions on the opcode table
463 lines
12 KiB
C#
463 lines
12 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|