using NESDecompiler.Core.Disassembly; namespace NESDecompiler.Core.Decompilation; /// /// Represents an independently decompiled function /// public class DecompiledFunction { /// /// The CPU address where the address' first instruction is located /// public ushort Address { get; } /// /// The instructions that make up this function in the correct order in which they should be /// executed. /// public IReadOnlyList OrderedInstructions { get; } /// /// Location and the labels of all jump and branch targets within this function /// public IReadOnlyDictionary JumpTargets { get; } /// /// Indicates that this function is expected to mutate instruction bytes at runtime and should /// avoid JIT compilation. /// public bool IsSelfModifying { get; set; } public DecompiledFunction( ushort address, IReadOnlyList instructions, IReadOnlySet jumpTargets) { Address = address; JumpTargets = instructions .Where(x => jumpTargets.Contains(x.CPUAddress)) .Where(x => x.Label != null) .Where(x => x.SubAddressOrder == 0) // only real instructions should be jumped to .ToDictionary(x => x.CPUAddress, x => x.Label!); // We need to order the instructions so that the starting instruction is the first one encountered. // We can't just rely on the CPU address, because a function may jump to a code point earlier than // the first instruction. var entryPointInstructions = instructions.Where(x => x.CPUAddress == address) .Where(x => x.SubAddressOrder >= 0); var initialInstructions = instructions .Where(x => x.CPUAddress > address) .OrderBy(x => x.CPUAddress) .ThenBy(x => x.SubAddressOrder); var trailingInstructions = instructions .Where(x => x.CPUAddress < address) .OrderBy(x => x.CPUAddress) .ThenBy(x => x.SubAddressOrder); // real instructions before virtual ones // If there was a loopback jump point at the function address, put that here. This is required // because if an emulator is executing a virtual loopback instruction and an IRQ occurs, this // will cause the virtual instruction to be saved to the stack, and that can cause the entry // point to be wrong. var loopbackInstructions = instructions.Where(x => x.CPUAddress == address) .Where(x => x.SubAddressOrder < 0); OrderedInstructions = entryPointInstructions .Concat(initialInstructions) .Concat(trailingInstructions) .Concat(loopbackInstructions) .ToArray(); } }