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();
}
}