aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Decoder/ADecoder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ChocolArm64/Decoder/ADecoder.cs')
-rw-r--r--ChocolArm64/Decoder/ADecoder.cs207
1 files changed, 207 insertions, 0 deletions
diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs
new file mode 100644
index 00000000..06a535c1
--- /dev/null
+++ b/ChocolArm64/Decoder/ADecoder.cs
@@ -0,0 +1,207 @@
+using ChocolArm64.Instruction;
+using ChocolArm64.Memory;
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Decoder
+{
+ static class ADecoder
+ {
+ public static (ABlock[] Graph, ABlock Root) DecodeSubroutine(ATranslator Translator, long Start)
+ {
+ Dictionary<long, ABlock> Visited = new Dictionary<long, ABlock>();
+ Dictionary<long, ABlock> VisitedEnd = new Dictionary<long, ABlock>();
+
+ Queue<ABlock> Blocks = new Queue<ABlock>();
+
+ ABlock Enqueue(long Position)
+ {
+ if (!Visited.TryGetValue(Position, out ABlock Output))
+ {
+ Output = new ABlock(Position);
+
+ Blocks.Enqueue(Output);
+
+ Visited.Add(Position, Output);
+ }
+
+ return Output;
+ }
+
+ ABlock Root = Enqueue(Start);
+
+ while (Blocks.Count > 0)
+ {
+ ABlock Current = Blocks.Dequeue();
+
+ FillBlock(Translator.Thread.Memory, Current);
+
+ //Set child blocks. "Branch" is the block the branch instruction
+ //points to (when taken), "Next" is the block at the next address,
+ //executed when the branch is not taken. For Unconditional Branches
+ //(except BL/BLR that are sub calls) or end of executable, Next is null.
+ if (Current.OpCodes.Count > 0)
+ {
+ bool HasCachedSub = false;
+
+ AOpCode LastOp = Current.GetLastOp();
+
+ if (LastOp is AOpCodeBImm Op)
+ {
+ if (Op.Emitter == AInstEmit.Bl)
+ {
+ HasCachedSub = Translator.HasCachedSub(Op.Imm);
+ }
+ else
+ {
+ Current.Branch = Enqueue(Op.Imm);
+ }
+ }
+
+ if ((!(LastOp is AOpCodeBImmAl) &&
+ !(LastOp is AOpCodeBReg)) || HasCachedSub)
+ {
+ Current.Next = Enqueue(Current.EndPosition);
+ }
+ }
+
+ //If we have on the tree two blocks with the same end position,
+ //then we need to split the bigger block and have two small blocks,
+ //the end position of the bigger "Current" block should then be == to
+ //the position of the "Smaller" block.
+ while (VisitedEnd.TryGetValue(Current.EndPosition, out ABlock Smaller))
+ {
+ if (Current.Position > Smaller.Position)
+ {
+ ABlock Temp = Smaller;
+
+ Smaller = Current;
+ Current = Temp;
+ }
+
+ Current.EndPosition = Smaller.Position;
+ Current.Next = Smaller;
+ Current.Branch = null;
+
+ Current.OpCodes.RemoveRange(
+ Current.OpCodes.Count - Smaller.OpCodes.Count,
+ Smaller.OpCodes.Count);
+
+ VisitedEnd[Smaller.EndPosition] = Smaller;
+ }
+
+ VisitedEnd.Add(Current.EndPosition, Current);
+ }
+
+ //Make and sort Graph blocks array by position.
+ ABlock[] Graph = new ABlock[Visited.Count];
+
+ while (Visited.Count > 0)
+ {
+ ulong FirstPos = ulong.MaxValue;
+
+ foreach (ABlock Block in Visited.Values)
+ {
+ if (FirstPos > (ulong)Block.Position)
+ FirstPos = (ulong)Block.Position;
+ }
+
+ ABlock Current = Visited[(long)FirstPos];
+
+ do
+ {
+ Graph[Graph.Length - Visited.Count] = Current;
+
+ Visited.Remove(Current.Position);
+
+ Current = Current.Next;
+ }
+ while (Current != null);
+ }
+
+ return (Graph, Root);
+ }
+
+ private static void FillBlock(AMemory Memory, ABlock Block)
+ {
+ long Position = Block.Position;
+
+ AOpCode OpCode;
+
+ do
+ {
+ OpCode = DecodeOpCode(Memory, Position);
+
+ Block.OpCodes.Add(OpCode);
+
+ Position += 4;
+ }
+ while (!(IsBranch(OpCode) || IsException(OpCode)));
+
+ Block.EndPosition = Position;
+ }
+
+ private static bool IsBranch(AOpCode OpCode)
+ {
+ return OpCode is AOpCodeBImm ||
+ OpCode is AOpCodeBReg;
+ }
+
+ private static bool IsException(AOpCode OpCode)
+ {
+ return OpCode.Emitter == AInstEmit.Brk ||
+ OpCode.Emitter == AInstEmit.Svc ||
+ OpCode.Emitter == AInstEmit.Und;
+ }
+
+ public static AOpCode DecodeOpCode(AMemory Memory, long Position)
+ {
+ int OpCode = Memory.ReadInt32(Position);
+
+ AInst Inst = AOpCodeTable.GetInst(OpCode);
+
+ AOpCode DecodedOpCode = new AOpCode(AInst.Undefined, Position, OpCode);
+
+ if (Inst.Type != null)
+ {
+ DecodedOpCode = CreateOpCode(Inst.Type, Inst, Position, OpCode);
+ }
+
+ return DecodedOpCode;
+ }
+
+ private delegate object OpActivator(AInst Inst, long Position, int OpCode);
+
+ private static Dictionary<Type, OpActivator> Activators = new Dictionary<Type, OpActivator>();
+
+ private static AOpCode CreateOpCode(Type Type, AInst Inst, long Position, int OpCode)
+ {
+ if (Type == null)
+ {
+ throw new ArgumentNullException(nameof(Type));
+ }
+
+ if (!Activators.TryGetValue(Type, out OpActivator CreateInstance))
+ {
+ Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) };
+
+ DynamicMethod Mthd = new DynamicMethod($"{Type.Name}_Create", Type, ArgTypes);
+
+ ILGenerator Generator = Mthd.GetILGenerator();
+
+ Generator.Emit(OpCodes.Ldarg_0);
+ Generator.Emit(OpCodes.Ldarg_1);
+ Generator.Emit(OpCodes.Ldarg_2);
+ Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes));
+ Generator.Emit(OpCodes.Ret);
+
+ CreateInstance = (OpActivator)Mthd.CreateDelegate(typeof(OpActivator));
+
+ Activators.Add(Type, CreateInstance);
+ }
+
+ return (AOpCode)CreateInstance(Inst, Position, OpCode);
+ }
+ }
+} \ No newline at end of file