Merge pull request #13 from KallDrexx/loopback_instruction_fix

Loopback jumps should occur on the same cpu address of the jump
This commit is contained in:
ApfelTeeSaft
2026-01-17 17:55:48 +01:00
committed by GitHub
3 changed files with 24 additions and 8 deletions
@@ -32,13 +32,17 @@ public class 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 class 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();
}
}
@@ -26,7 +26,7 @@ public static class FunctionDecompiler
// 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 class FunctionDecompiler
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 class FunctionDecompiler
// 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}";
}
@@ -78,7 +78,7 @@ namespace NESDecompiler.Core.Disassembly
/// 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.
/// </summary>
public byte SubAddressOrder { get; set; }
public sbyte SubAddressOrder { get; set; }
/// <summary>
/// Returns a string representation of this instruction