forked from Mirror/Ryujinx
193 lines
5.6 KiB
C#
193 lines
5.6 KiB
C#
|
using ARMeilleure.Decoders;
|
||
|
using ARMeilleure.IntermediateRepresentation;
|
||
|
using ARMeilleure.State;
|
||
|
using ARMeilleure.Translation;
|
||
|
|
||
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||
|
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||
|
|
||
|
namespace ARMeilleure.Instructions
|
||
|
{
|
||
|
static class InstEmitFlowHelper
|
||
|
{
|
||
|
public const ulong CallFlag = 1;
|
||
|
|
||
|
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
||
|
{
|
||
|
if (cond != Condition.Al)
|
||
|
{
|
||
|
context.BranchIfTrue(target, GetCondTrue(context, cond));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
context.Branch(target);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Operand GetCondTrue(ArmEmitterContext context, Condition condition)
|
||
|
{
|
||
|
Operand cmpResult = context.TryGetComparisonResult(condition);
|
||
|
|
||
|
if (cmpResult != null)
|
||
|
{
|
||
|
return cmpResult;
|
||
|
}
|
||
|
|
||
|
Operand value = Const(1);
|
||
|
|
||
|
Operand Inverse(Operand val)
|
||
|
{
|
||
|
return context.BitwiseExclusiveOr(val, Const(1));
|
||
|
}
|
||
|
|
||
|
switch (condition)
|
||
|
{
|
||
|
case Condition.Eq:
|
||
|
value = GetFlag(PState.ZFlag);
|
||
|
break;
|
||
|
|
||
|
case Condition.Ne:
|
||
|
value = Inverse(GetFlag(PState.ZFlag));
|
||
|
break;
|
||
|
|
||
|
case Condition.GeUn:
|
||
|
value = GetFlag(PState.CFlag);
|
||
|
break;
|
||
|
|
||
|
case Condition.LtUn:
|
||
|
value = Inverse(GetFlag(PState.CFlag));
|
||
|
break;
|
||
|
|
||
|
case Condition.Mi:
|
||
|
value = GetFlag(PState.NFlag);
|
||
|
break;
|
||
|
|
||
|
case Condition.Pl:
|
||
|
value = Inverse(GetFlag(PState.NFlag));
|
||
|
break;
|
||
|
|
||
|
case Condition.Vs:
|
||
|
value = GetFlag(PState.VFlag);
|
||
|
break;
|
||
|
|
||
|
case Condition.Vc:
|
||
|
value = Inverse(GetFlag(PState.VFlag));
|
||
|
break;
|
||
|
|
||
|
case Condition.GtUn:
|
||
|
{
|
||
|
Operand c = GetFlag(PState.CFlag);
|
||
|
Operand z = GetFlag(PState.ZFlag);
|
||
|
|
||
|
value = context.BitwiseAnd(c, Inverse(z));
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Condition.LeUn:
|
||
|
{
|
||
|
Operand c = GetFlag(PState.CFlag);
|
||
|
Operand z = GetFlag(PState.ZFlag);
|
||
|
|
||
|
value = context.BitwiseOr(Inverse(c), z);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Condition.Ge:
|
||
|
{
|
||
|
Operand n = GetFlag(PState.NFlag);
|
||
|
Operand v = GetFlag(PState.VFlag);
|
||
|
|
||
|
value = context.ICompareEqual(n, v);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Condition.Lt:
|
||
|
{
|
||
|
Operand n = GetFlag(PState.NFlag);
|
||
|
Operand v = GetFlag(PState.VFlag);
|
||
|
|
||
|
value = context.ICompareNotEqual(n, v);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Condition.Gt:
|
||
|
{
|
||
|
Operand n = GetFlag(PState.NFlag);
|
||
|
Operand z = GetFlag(PState.ZFlag);
|
||
|
Operand v = GetFlag(PState.VFlag);
|
||
|
|
||
|
value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v));
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case Condition.Le:
|
||
|
{
|
||
|
Operand n = GetFlag(PState.NFlag);
|
||
|
Operand z = GetFlag(PState.ZFlag);
|
||
|
Operand v = GetFlag(PState.VFlag);
|
||
|
|
||
|
value = context.BitwiseOr(z, context.ICompareNotEqual(n, v));
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
public static void EmitCall(ArmEmitterContext context, ulong immediate)
|
||
|
{
|
||
|
context.Return(Const(immediate | CallFlag));
|
||
|
}
|
||
|
|
||
|
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||
|
{
|
||
|
EmitVirtualCallOrJump(context, target, isJump: false);
|
||
|
}
|
||
|
|
||
|
public static void EmitVirtualJump(ArmEmitterContext context, Operand target)
|
||
|
{
|
||
|
EmitVirtualCallOrJump(context, target, isJump: true);
|
||
|
}
|
||
|
|
||
|
private static void EmitVirtualCallOrJump(ArmEmitterContext context, Operand target, bool isJump)
|
||
|
{
|
||
|
context.Return(context.BitwiseOr(target, Const(target.Type, (long)CallFlag)));
|
||
|
}
|
||
|
|
||
|
private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand retVal)
|
||
|
{
|
||
|
// 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.
|
||
|
ulong nextAddr = GetNextOpAddress(context.CurrOp);
|
||
|
|
||
|
if (context.CurrBlock.Next != null)
|
||
|
{
|
||
|
Operand lblContinue = Label();
|
||
|
|
||
|
context.BranchIfTrue(lblContinue, context.ICompareEqual(retVal, Const(nextAddr)));
|
||
|
|
||
|
context.Return(Const(nextAddr));
|
||
|
|
||
|
context.MarkLabel(lblContinue);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
context.Return(Const(nextAddr));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static ulong GetNextOpAddress(OpCode op)
|
||
|
{
|
||
|
return op.Address + (ulong)op.OpCodeSizeInBytes;
|
||
|
}
|
||
|
}
|
||
|
}
|