diff --git a/NESDecompiler.Core/Decompilation/DecompiledFunction.cs b/NESDecompiler.Core/Decompilation/DecompiledFunction.cs index a8d6eee..6192e7a 100644 --- a/NESDecompiler.Core/Decompilation/DecompiledFunction.cs +++ b/NESDecompiler.Core/Decompilation/DecompiledFunction.cs @@ -39,11 +39,13 @@ public DecompiledFunction( // the first instruction. var initialInstructions = instructions .Where(x => x.CPUAddress >= address) - .OrderBy(x => x.CPUAddress); + .OrderBy(x => x.CPUAddress) + .ThenBy(x => x.SubAddressOrder); var trailingInstructions = instructions .Where(x => x.CPUAddress < address) - .OrderBy(x => x.CPUAddress); + .OrderBy(x => x.CPUAddress) + .ThenBy(x => x.SubAddressOrder); // real instructions before virtual ones OrderedInstructions = initialInstructions.Concat(trailingInstructions).ToArray(); } diff --git a/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs b/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs index f26fc9f..9208de6 100644 --- a/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs +++ b/NESDecompiler.Core/Decompilation/FunctionDecompiler.cs @@ -27,10 +27,10 @@ public static DecompiledFunction Decompile(ushort functionAddress, IReadOnlyList // 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 - if (functionAddress == 0x00 || seenInstructions.Contains((ushort)(functionAddress - 1))) + if (functionAddress == 0x00) { - var message = $"Function 0x{functionAddress:X4} wraps around, but there's not enough " + - $"space to add a jump back to the entrypoint"; + const string message = "Wrap around instruction detected for a function at 0000, but that " + + "doesn't make sense"; throw new InvalidOperationException(message); } @@ -44,6 +44,9 @@ public static DecompiledFunction Decompile(ushort functionAddress, IReadOnlyList CPUAddress = (ushort)(nextAddress - 1), Bytes = [0x4C, (byte)addressLow, (byte)addressHigh], TargetAddress = functionAddress, + + // Make sure they appear after any instruction that already occupies that address + SubAddressOrder = 1, }; instructions.Add(jumpInstruction); @@ -109,9 +112,7 @@ private static DisassembledInstruction GetNextInstruction(ushort address, IReadO var info = InstructionSet.GetInstruction(bytes[0]); if (!info.IsValid) { - var message = $"Attempted to get instruction at address 0x{address:X4}, but byte 0x{bytes[0]:X4} " + - $"is not a valid/known opcode"; - + var message = $"Opcode 0x{bytes[0]:X2} at address 0x{address:X4} is not a known instruction"; throw new InvalidOperationException(message); } diff --git a/NESDecompiler.Core/Disassembly/Disassembler.cs b/NESDecompiler.Core/Disassembly/Disassembler.cs index ee98afb..f1efd26 100644 --- a/NESDecompiler.Core/Disassembly/Disassembler.cs +++ b/NESDecompiler.Core/Disassembly/Disassembler.cs @@ -72,6 +72,14 @@ public class DisassembledInstruction /// public bool IsJump => Info.Mnemonic == "JMP" || Info.Mnemonic == "JSR"; + /// + /// Determines the order of this instruction within a single address space. This is mostly + /// needed in the cases that additional instructions are needed to be added in the same + /// 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; } + /// /// Returns a string representation of this instruction ///