Ryujinx-ChocolArm64/Instructions/InstEmitMemory.cs

241 lines
6.0 KiB
C#

using ChocolArm64.Decoders;
using ChocolArm64.Translation;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmitMemoryHelper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit
{
public static void Adr(ILEmitterCtx context)
{
OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp;
context.EmitLdc_I(op.Position + op.Imm);
context.EmitStintzr(op.Rd);
}
public static void Adrp(ILEmitterCtx context)
{
OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp;
context.EmitLdc_I((op.Position & ~0xfffL) + (op.Imm << 12));
context.EmitStintzr(op.Rd);
}
public static void Ldr(ILEmitterCtx context) => EmitLdr(context, false);
public static void Ldrs(ILEmitterCtx context) => EmitLdr(context, true);
private static void EmitLdr(ILEmitterCtx context, bool signed)
{
OpCodeMem64 op = (OpCodeMem64)context.CurrOp;
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 IOpCodeSimd64)
{
context.EmitStvec(op.Rt);
}
else
{
context.EmitStintzr(op.Rt);
}
EmitWBackIfNeeded(context);
}
public static void Ldr_Literal(ILEmitterCtx context)
{
IOpCodeLit64 op = (IOpCodeLit64)context.CurrOp;
if (op.Prefetch)
{
return;
}
context.EmitLdc_I8(op.Imm);
if (op.Signed)
{
EmitReadSx64Call(context, op.Size);
}
else
{
EmitReadZxCall(context, op.Size);
}
if (op is IOpCodeSimd64)
{
context.EmitStvec(op.Rt);
}
else
{
context.EmitStint(op.Rt);
}
}
public static void Ldp(ILEmitterCtx context)
{
OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp;
void EmitReadAndStore(int rt)
{
if (op.Extend64)
{
EmitReadSx64Call(context, op.Size);
}
else
{
EmitReadZxCall(context, op.Size);
}
if (op is IOpCodeSimd64)
{
context.EmitStvec(rt);
}
else
{
context.EmitStintzr(rt);
}
}
EmitLoadAddress(context);
EmitReadAndStore(op.Rt);
context.EmitLdtmp();
context.EmitLdc_I8(1 << op.Size);
context.Emit(OpCodes.Add);
EmitReadAndStore(op.Rt2);
EmitWBackIfNeeded(context);
}
public static void Str(ILEmitterCtx context)
{
OpCodeMem64 op = (OpCodeMem64)context.CurrOp;
EmitLoadAddress(context);
if (op is IOpCodeSimd64)
{
context.EmitLdvec(op.Rt);
}
else
{
context.EmitLdintzr(op.Rt);
}
EmitWriteCall(context, op.Size);
EmitWBackIfNeeded(context);
}
public static void Stp(ILEmitterCtx context)
{
OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp;
EmitLoadAddress(context);
if (op is IOpCodeSimd64)
{
context.EmitLdvec(op.Rt);
}
else
{
context.EmitLdintzr(op.Rt);
}
EmitWriteCall(context, op.Size);
context.EmitLdtmp();
context.EmitLdc_I8(1 << op.Size);
context.Emit(OpCodes.Add);
if (op is IOpCodeSimd64)
{
context.EmitLdvec(op.Rt2);
}
else
{
context.EmitLdintzr(op.Rt2);
}
EmitWriteCall(context, op.Size);
EmitWBackIfNeeded(context);
}
private static void EmitLoadAddress(ILEmitterCtx context)
{
switch (context.CurrOp)
{
case OpCodeMemImm64 op:
context.EmitLdint(op.Rn);
if (!op.PostIdx)
{
//Pre-indexing.
context.EmitLdc_I(op.Imm);
context.Emit(OpCodes.Add);
}
break;
case OpCodeMemReg64 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(ILEmitterCtx 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 OpCodeMemImm64 op && op.WBack)
{
context.EmitLdtmp();
if (op.PostIdx)
{
context.EmitLdc_I(op.Imm);
context.Emit(OpCodes.Add);
}
context.EmitStint(op.Rn);
}
}
}
}