aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Instructions/InstEmitFlowHelper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ARMeilleure/Instructions/InstEmitFlowHelper.cs')
-rw-r--r--ARMeilleure/Instructions/InstEmitFlowHelper.cs192
1 files changed, 192 insertions, 0 deletions
diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
new file mode 100644
index 00000000..a8eb21d3
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
@@ -0,0 +1,192 @@
+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;
+ }
+ }
+}