diff --git a/NESDecompiler.Core/Decompilation/DecompiledFunction.cs b/NESDecompiler.Core/Decompilation/DecompiledFunction.cs index 6192e7a..8fc6e41 100644 --- a/NESDecompiler.Core/Decompilation/DecompiledFunction.cs +++ b/NESDecompiler.Core/Decompilation/DecompiledFunction.cs @@ -32,13 +32,17 @@ public DecompiledFunction( 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) + .Where(x => x.CPUAddress > address) .OrderBy(x => x.CPUAddress) .ThenBy(x => x.SubAddressOrder); @@ -47,6 +51,17 @@ public DecompiledFunction( .OrderBy(x => x.CPUAddress) .ThenBy(x => x.SubAddressOrder); // real instructions before virtual ones - OrderedInstructions = initialInstructions.Concat(trailingInstructions).ToArray(); + // 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(); } } \ No newline at end of file diff --git a/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs b/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs index faab97b..5a28b10 100644 --- a/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs +++ b/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs @@ -26,7 +26,7 @@ public static DecompiledFunction Decompile(ushort functionAddress, IReadOnlyList // This means a branch occurred that caused the flow to wrap around to instructions preceding // the function entrance. This usually happens when there is a jump/branch to right before the // entrypoint, usually due to decompiling in the middle of a loop. To fix this, we need to add - // a jump back to the function entrypoint + // a jump back to the function entrypoint. if (functionAddress == 0x00) { const string message = "Wrap around instruction detected for a function at 0000, but that " + @@ -41,12 +41,12 @@ public static DecompiledFunction Decompile(ushort functionAddress, IReadOnlyList var jumpInstruction = new DisassembledInstruction { Info = InstructionSet.GetInstruction(0x4C), - CPUAddress = (ushort)(nextAddress - 1), + CPUAddress = nextAddress, Bytes = [0x4C, (byte)addressLow, (byte)addressHigh], TargetAddress = functionAddress, - // Make sure they appear after any instruction that already occupies that address - SubAddressOrder = 1, + // Make sure they appear before the function address + SubAddressOrder = -1, }; instructions.Add(jumpInstruction); @@ -92,7 +92,8 @@ public static DecompiledFunction Decompile(ushort functionAddress, IReadOnlyList // Add labels for any jump targets foreach (var instruction in instructions) { - if (jumpAddresses.Contains(instruction.CPUAddress)) + // Only real instructions should have a label, virtual ones should not + if (jumpAddresses.Contains(instruction.CPUAddress) && instruction.SubAddressOrder == 0) { instruction.Label = $"loc_{instruction.CPUAddress:X4}"; } diff --git a/NESDecompiler.Core/Disassembly/Disassembler.cs b/NESDecompiler.Core/Disassembly/Disassembler.cs index f1efd26..ef4928c 100644 --- a/NESDecompiler.Core/Disassembly/Disassembler.cs +++ b/NESDecompiler.Core/Disassembly/Disassembler.cs @@ -78,7 +78,7 @@ public class DisassembledInstruction /// address location at runtime. Can be used to add runtime hooks or to work around /// decompilation issues. Should be 0 for all native instructions from a ROM. /// - public byte SubAddressOrder { get; set; } + public sbyte SubAddressOrder { get; set; } /// /// Returns a string representation of this instruction