diff options
Diffstat (limited to 'ChocolArm64/Decoder/ADecoder.cs')
| -rw-r--r-- | ChocolArm64/Decoder/ADecoder.cs | 207 |
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 |
