mirror of
https://github.com/ApfelTeeSaft/NESDecompiler.git
synced 2026-08-27 11:53:25 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9aec398919 | ||
|
|
8242ffb49b | ||
|
|
e76cec3d6d | ||
|
|
26af1f4aa6 |
@@ -0,0 +1,73 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
---
|
||||
|
||||
## [v1.1.1] - 2025-10-18
|
||||
### Added
|
||||
- Decompilation can now start from the **first byte of a code region** (#12).
|
||||
|
||||
### Changed
|
||||
- Invalid instructions are now considered the **end of a function trace** (#11).
|
||||
- Improved stability of function tracing and edge-case instruction handling.
|
||||
|
||||
### Fixed
|
||||
- Minor internal decompiler logic bugs.
|
||||
- General performance and reliability improvements across builds.
|
||||
|
||||
### Notes
|
||||
- All Windows builds now include both GUI and CLI versions.
|
||||
- Linux and macOS builds are CLI-only.
|
||||
- All binaries are **self-contained** and **do not require a .NET runtime**.
|
||||
|
||||
**Contributors:**
|
||||
[@KallDrexx](https://github.com/KallDrexx)
|
||||
|
||||
---
|
||||
|
||||
## [v1.1.0] - 2025-10-12
|
||||
### Added
|
||||
- Implemented **single function decompiler** with proper tracing.
|
||||
- Added support for **sub-address instructions** and **virtual instructions** (#10).
|
||||
- Added **CI/CD workflow** (`build-release.yml`) for automated builds and packaging.
|
||||
- Added **multiple platform releases**:
|
||||
- Windows (x64, x86, ARM64) with GUI + CLI
|
||||
- Linux (x64, ARM64) CLI
|
||||
- macOS (x64, ARM64) CLI
|
||||
|
||||
### Changed
|
||||
- Improved function discovery to handle **wraparound and disassembly boundaries** (#9).
|
||||
- Reworked `ToString()` formatting for instructions for clarity.
|
||||
- Improved tracing logic for **unreferenced instruction analysis** (#6).
|
||||
- Decompiler now directly jumps to instructions that appear within other instructions.
|
||||
|
||||
### Fixed
|
||||
- Fixed incorrect ordering of instructions in output.
|
||||
- Fixed 16KB ROMs not decompiling (#7).
|
||||
- Fixed nullability warnings.
|
||||
- Fixed various stability issues in the decompiler core.
|
||||
|
||||
**Contributors:**
|
||||
[@ApfelTeeSaft](https://github.com/ApfelTeeSaft), [@KallDrexx](https://github.com/KallDrexx)
|
||||
|
||||
---
|
||||
|
||||
## [v1.0.0] - 2025-05-15
|
||||
### Added
|
||||
- **Initial release** of the NES Decompiler.
|
||||
- Included both **CLI** and **GUI** builds for Windows (x64).
|
||||
- Added base decompilation engine and ROM handling logic.
|
||||
- Added initial README and documentation.
|
||||
|
||||
**Contributors:**
|
||||
[@ApfelTeeSaft](https://github.com/ApfelTeeSaft)
|
||||
|
||||
---
|
||||
|
||||
## [Unreleased]
|
||||
- Planned improvements to function boundary detection.
|
||||
- Optimizations for recursive instruction analysis.
|
||||
- Additional architecture support under evaluation.
|
||||
@@ -23,6 +23,12 @@ public class DecompiledFunction
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<ushort, string> JumpTargets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this function is expected to mutate instruction bytes at runtime and should
|
||||
/// avoid JIT compilation.
|
||||
/// </summary>
|
||||
public bool IsSelfModifying { get; set; }
|
||||
|
||||
public DecompiledFunction(
|
||||
ushort address,
|
||||
IReadOnlyList<DisassembledInstruction> instructions,
|
||||
@@ -32,13 +38,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 +57,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
|
||||
|
||||
Reference in New Issue
Block a user