mirror of
https://github.com/ApfelTeeSaft/NESDecompiler.git
synced 2026-08-27 03:43:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11b9943c09 | ||
|
|
54b9e3d3df | ||
|
|
a9f323722f | ||
|
|
e8a57399ce | ||
|
|
9068403d56 | ||
|
|
bc76d20895 | ||
|
|
40743c6889 | ||
|
|
32e3d78e6d | ||
|
|
b627bc2a48 | ||
|
|
1e9899b9bc | ||
|
|
e592c31a0c | ||
|
|
b968b151d0 | ||
|
|
588841b46b | ||
|
|
c1e0b17bb3 |
@@ -0,0 +1,291 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, develop ]
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '8.0.x'
|
||||
PROJECT_NAME: 'NESDecompiler'
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64, x86, arm64]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build CLI
|
||||
run: |
|
||||
dotnet publish NESDecompiler.CLI/NESDecompiler.CLI.csproj `
|
||||
--configuration Release `
|
||||
--runtime win-${{ matrix.arch }} `
|
||||
--self-contained true `
|
||||
--output ./artifacts/cli/win-${{ matrix.arch }} `
|
||||
-p:PublishSingleFile=true `
|
||||
-p:PublishTrimmed=true
|
||||
|
||||
- name: Build GUI
|
||||
run: |
|
||||
dotnet publish NESDecompiler.GUI/NESDecompiler.GUI.csproj `
|
||||
--configuration Release `
|
||||
--runtime win-${{ matrix.arch }} `
|
||||
--self-contained true `
|
||||
--output ./artifacts/gui/win-${{ matrix.arch }} `
|
||||
-p:PublishSingleFile=true `
|
||||
-p:PublishTrimmed=false
|
||||
|
||||
- name: Package Windows Release
|
||||
run: |
|
||||
$version = if ($env:GITHUB_REF -match 'refs/tags/v(.*)') { $matches[1] } else { (Get-Date -Format "yyyy.MM.dd") + "-" + $env:GITHUB_SHA.Substring(0,7) }
|
||||
|
||||
# Package CLI
|
||||
Compress-Archive -Path ./artifacts/cli/win-${{ matrix.arch }}/* `
|
||||
-DestinationPath ./NESDecompiler-CLI-win-${{ matrix.arch }}-$version.zip
|
||||
|
||||
# Package GUI
|
||||
Compress-Archive -Path ./artifacts/gui/win-${{ matrix.arch }}/* `
|
||||
-DestinationPath ./NESDecompiler-GUI-win-${{ matrix.arch }}-$version.zip
|
||||
|
||||
# Package Full (CLI + GUI)
|
||||
New-Item -ItemType Directory -Path ./artifacts/full/win-${{ matrix.arch }} -Force
|
||||
Copy-Item -Path ./artifacts/cli/win-${{ matrix.arch }}/* -Destination ./artifacts/full/win-${{ matrix.arch }} -Recurse
|
||||
Copy-Item -Path ./artifacts/gui/win-${{ matrix.arch }}/* -Destination ./artifacts/full/win-${{ matrix.arch }} -Recurse
|
||||
Copy-Item -Path ./README.md -Destination ./artifacts/full/win-${{ matrix.arch }}
|
||||
Copy-Item -Path ./LICENSE -Destination ./artifacts/full/win-${{ matrix.arch }}
|
||||
|
||||
Compress-Archive -Path ./artifacts/full/win-${{ matrix.arch }}/* `
|
||||
-DestinationPath ./NESDecompiler-Full-win-${{ matrix.arch }}-$version.zip
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-${{ matrix.arch }}
|
||||
path: |
|
||||
NESDecompiler-*.zip
|
||||
retention-days: 7
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64, arm64]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies (CLI only)
|
||||
run: |
|
||||
dotnet restore NESDecompiler.Core/NESDecompiler.Core.csproj
|
||||
dotnet restore NESDecompiler.CLI/NESDecompiler.CLI.csproj
|
||||
|
||||
- name: Build CLI
|
||||
run: |
|
||||
dotnet publish NESDecompiler.CLI/NESDecompiler.CLI.csproj \
|
||||
--configuration Release \
|
||||
--runtime linux-${{ matrix.arch }} \
|
||||
--self-contained true \
|
||||
--output ./artifacts/cli/linux-${{ matrix.arch }} \
|
||||
-p:PublishSingleFile=true \
|
||||
-p:PublishTrimmed=true
|
||||
|
||||
- name: Set execute permissions
|
||||
run: chmod +x ./artifacts/cli/linux-${{ matrix.arch }}/NESDecompiler.CLI
|
||||
|
||||
- name: Package Linux Release
|
||||
run: |
|
||||
if [[ $GITHUB_REF == refs/tags/v* ]]; then
|
||||
version=${GITHUB_REF#refs/tags/v}
|
||||
else
|
||||
version=$(date +%Y.%m.%d)-${GITHUB_SHA:0:7}
|
||||
fi
|
||||
|
||||
# Create release directory
|
||||
mkdir -p ./artifacts/release/linux-${{ matrix.arch }}
|
||||
cp -r ./artifacts/cli/linux-${{ matrix.arch }}/* ./artifacts/release/linux-${{ matrix.arch }}/
|
||||
cp README.md ./artifacts/release/linux-${{ matrix.arch }}/
|
||||
cp LICENSE ./artifacts/release/linux-${{ matrix.arch }}/
|
||||
|
||||
# Create tar.gz
|
||||
cd ./artifacts/release
|
||||
tar -czf ../../NESDecompiler-CLI-linux-${{ matrix.arch }}-${version}.tar.gz linux-${{ matrix.arch }}
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-${{ matrix.arch }}
|
||||
path: |
|
||||
NESDecompiler-*.tar.gz
|
||||
retention-days: 7
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64, arm64]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies (CLI only)
|
||||
run: |
|
||||
dotnet restore NESDecompiler.Core/NESDecompiler.Core.csproj
|
||||
dotnet restore NESDecompiler.CLI/NESDecompiler.CLI.csproj
|
||||
|
||||
- name: Build CLI
|
||||
run: |
|
||||
dotnet publish NESDecompiler.CLI/NESDecompiler.CLI.csproj \
|
||||
--configuration Release \
|
||||
--runtime osx-${{ matrix.arch }} \
|
||||
--self-contained true \
|
||||
--output ./artifacts/cli/osx-${{ matrix.arch }} \
|
||||
-p:PublishSingleFile=true \
|
||||
-p:PublishTrimmed=true
|
||||
|
||||
- name: Set execute permissions
|
||||
run: chmod +x ./artifacts/cli/osx-${{ matrix.arch }}/NESDecompiler.CLI
|
||||
|
||||
- name: Package macOS Release
|
||||
run: |
|
||||
if [[ $GITHUB_REF == refs/tags/v* ]]; then
|
||||
version=${GITHUB_REF#refs/tags/v}
|
||||
else
|
||||
version=$(date +%Y.%m.%d)-${GITHUB_SHA:0:7}
|
||||
fi
|
||||
|
||||
# Create release directory
|
||||
mkdir -p ./artifacts/release/osx-${{ matrix.arch }}
|
||||
cp -r ./artifacts/cli/osx-${{ matrix.arch }}/* ./artifacts/release/osx-${{ matrix.arch }}/
|
||||
cp README.md ./artifacts/release/osx-${{ matrix.arch }}/
|
||||
cp LICENSE ./artifacts/release/osx-${{ matrix.arch }}/
|
||||
|
||||
# Create tar.gz
|
||||
cd ./artifacts/release
|
||||
tar -czf ../../NESDecompiler-CLI-osx-${{ matrix.arch }}-${version}.tar.gz osx-${{ matrix.arch }}
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-${{ matrix.arch }}
|
||||
path: |
|
||||
NESDecompiler-*.tar.gz
|
||||
retention-days: 7
|
||||
|
||||
create-release:
|
||||
needs: [build-windows, build-linux, build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R ./artifacts
|
||||
|
||||
- name: Extract version from tag
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Version: $VERSION"
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: NES Decompiler v${{ steps.get_version.outputs.VERSION }}
|
||||
body: |
|
||||
# NES Decompiler v${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
## Downloads
|
||||
|
||||
### Windows (GUI + CLI)
|
||||
- **x64**: Full GUI and CLI support
|
||||
- **x86**: Full GUI and CLI support (32-bit)
|
||||
- **ARM64**: Full GUI and CLI support (Windows on ARM)
|
||||
|
||||
### Linux (CLI only)
|
||||
- **x64**: Command-line interface
|
||||
- **ARM64**: Command-line interface
|
||||
|
||||
### macOS (CLI only)
|
||||
- **x64**: Command-line interface (Intel Macs)
|
||||
- **ARM64**: Command-line interface (Apple Silicon)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the appropriate package for your platform
|
||||
2. Extract the archive
|
||||
3. Run the executable (Windows: `NESDecompiler.GUI.exe` or `NESDecompiler.CLI.exe`, Linux/macOS: `./NESDecompiler.CLI`)
|
||||
|
||||
## Notes
|
||||
|
||||
- Windows packages include both GUI and CLI versions
|
||||
- Linux and macOS packages include CLI only
|
||||
- All packages are self-contained and don't require .NET runtime installation
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for details.
|
||||
files: |
|
||||
./artifacts/**/*.zip
|
||||
./artifacts/**/*.tar.gz
|
||||
draft: false
|
||||
prerelease: false
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-summary:
|
||||
needs: [build-windows, build-linux, build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Build Summary
|
||||
run: |
|
||||
echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Platform | Architecture | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|--------------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Windows | x64 | ${{ needs.build-windows.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Windows | x86 | ${{ needs.build-windows.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Windows | ARM64 | ${{ needs.build-windows.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Linux | x64 | ${{ needs.build-linux.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Linux | ARM64 | ${{ needs.build-linux.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| macOS | x64 | ${{ needs.build-macos.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| macOS | ARM64 | ${{ needs.build-macos.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -165,7 +165,7 @@ namespace NESDecompiler.CLI
|
||||
writer.WriteLine(" */");
|
||||
writer.WriteLine();
|
||||
|
||||
string guardName = Path.GetFileNameWithoutExtension(decompiler.ROMInfo.RawData[0].ToString()).ToUpper() + "_H";
|
||||
string guardName = Path.GetFileNameWithoutExtension(decompiler.ROMInfo.RawData![0].ToString()).ToUpper() + "_H";
|
||||
writer.WriteLine($"#ifndef {guardName}");
|
||||
writer.WriteLine($"#define {guardName}");
|
||||
writer.WriteLine();
|
||||
|
||||
@@ -113,17 +113,17 @@ namespace NESDecompiler.Core.CPU
|
||||
{
|
||||
AddressingMode.Implied => "",
|
||||
AddressingMode.Accumulator => "A",
|
||||
AddressingMode.Immediate => "#$%02X",
|
||||
AddressingMode.ZeroPage => "$%02X",
|
||||
AddressingMode.ZeroPageX => "$%02X,X",
|
||||
AddressingMode.ZeroPageY => "$%02X,Y",
|
||||
AddressingMode.Relative => "$%02X", // Will be processed specially for branches
|
||||
AddressingMode.Absolute => "$%04X",
|
||||
AddressingMode.AbsoluteX => "$%04X,X",
|
||||
AddressingMode.AbsoluteY => "$%04X,Y",
|
||||
AddressingMode.Indirect => "($%04X)",
|
||||
AddressingMode.IndexedIndirect => "($%02X,X)",
|
||||
AddressingMode.IndirectIndexed => "($%02X),Y",
|
||||
AddressingMode.Immediate => "#{0:X2}",
|
||||
AddressingMode.ZeroPage => "${0:X2}",
|
||||
AddressingMode.ZeroPageX => "${0:X2},X",
|
||||
AddressingMode.ZeroPageY => "${0:X2},Y",
|
||||
AddressingMode.Relative => "${0:X2}", // Will be processed specially for branches
|
||||
AddressingMode.Absolute => "${0:X4}",
|
||||
AddressingMode.AbsoluteX => "${0:X4},X",
|
||||
AddressingMode.AbsoluteY => "${0:X4},Y",
|
||||
AddressingMode.Indirect => "(${0:X4})",
|
||||
AddressingMode.IndexedIndirect => "(${0:X2},X)",
|
||||
AddressingMode.IndirectIndexed => "(${0:X2}),Y",
|
||||
_ => "???"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -592,6 +592,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
|
||||
if (!disassembler.AddressToInstruction.TryGetValue(address, out var instruction))
|
||||
{
|
||||
Console.WriteLine($"Decompilation warning: Instruction {address} (0x{address:X4}) queued to be analyzed but does not exist");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1027,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
{
|
||||
// Process LDA, LDX, LDY
|
||||
string register = instruction.Info.Mnemonic.Substring(2);
|
||||
string variableName = GetVariableName(instruction);
|
||||
string? variableName = GetVariableName(instruction);
|
||||
|
||||
if (variableName != null)
|
||||
{
|
||||
@@ -1046,7 +1047,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
{
|
||||
// Process STA, STX, STY
|
||||
string register = instruction.Info.Mnemonic.Substring(2);
|
||||
string variableName = GetVariableName(instruction);
|
||||
string? variableName = GetVariableName(instruction);
|
||||
|
||||
if (variableName != null)
|
||||
{
|
||||
@@ -1314,7 +1315,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateArithmeticCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle arithmetic operations: ADC, SBC
|
||||
string operand = GetOperandString(instruction);
|
||||
string? operand = GetOperandString(instruction);
|
||||
if (operand == null)
|
||||
return;
|
||||
|
||||
@@ -1359,7 +1360,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateIncrementCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle increment operations: INC, INX, INY
|
||||
string operand = GetOperandString(instruction);
|
||||
string? operand = GetOperandString(instruction);
|
||||
|
||||
switch (instruction.Info.Mnemonic)
|
||||
{
|
||||
@@ -1393,7 +1394,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateDecrementCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle decrement operations: DEC, DEX, DEY
|
||||
string operand = GetOperandString(instruction);
|
||||
string? operand = GetOperandString(instruction);
|
||||
|
||||
switch (instruction.Info.Mnemonic)
|
||||
{
|
||||
@@ -1427,7 +1428,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateShiftCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle shift operations: ASL, LSR, ROL, ROR
|
||||
string operand;
|
||||
string? operand;
|
||||
|
||||
if (instruction.Info.AddressingMode == AddressingMode.Accumulator)
|
||||
{
|
||||
@@ -1489,7 +1490,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateLogicCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle logic operations: AND, ORA, EOR, BIT
|
||||
string operand = GetOperandString(instruction);
|
||||
string? operand = GetOperandString(instruction);
|
||||
if (operand == null)
|
||||
return;
|
||||
|
||||
@@ -1533,7 +1534,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
private void GenerateCompareCode(DisassembledInstruction instruction, StringBuilder sb)
|
||||
{
|
||||
// Handle compare operations: CMP, CPX, CPY
|
||||
string operand = GetOperandString(instruction);
|
||||
string? operand = GetOperandString(instruction);
|
||||
if (operand == null)
|
||||
return;
|
||||
|
||||
@@ -1710,7 +1711,7 @@ namespace NESDecompiler.Core.Decompilation
|
||||
}
|
||||
}
|
||||
|
||||
private string GetOperandString(DisassembledInstruction instruction)
|
||||
private string? GetOperandString(DisassembledInstruction instruction)
|
||||
{
|
||||
if (instruction.Info.AddressingMode == AddressingMode.Immediate)
|
||||
{
|
||||
|
||||
@@ -25,17 +25,17 @@ namespace NESDecompiler.Core.Disassembly
|
||||
/// <summary>
|
||||
/// Information about this instruction's opcode
|
||||
/// </summary>
|
||||
public InstructionInfo Info { get; set; }
|
||||
public required InstructionInfo Info { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw bytes of this instruction (including operands)
|
||||
/// </summary>
|
||||
public byte[] Bytes { get; set; }
|
||||
public byte[]? Bytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The operand bytes of this instruction
|
||||
/// </summary>
|
||||
public byte[] Operands => Bytes.Length > 1 ? Bytes[1..] : Array.Empty<byte>();
|
||||
public byte[] Operands => Bytes!.Length > 1 ? Bytes[1..] : Array.Empty<byte>();
|
||||
|
||||
/// <summary>
|
||||
/// The target address for branch and jump instructions
|
||||
@@ -85,7 +85,7 @@ namespace NESDecompiler.Core.Disassembly
|
||||
}
|
||||
|
||||
sb.Append($"{CPUAddress:X4} ");
|
||||
foreach (var b in Bytes)
|
||||
foreach (var b in Bytes!)
|
||||
{
|
||||
sb.Append($"{b:X2} ");
|
||||
}
|
||||
@@ -185,6 +185,19 @@ namespace NESDecompiler.Core.Disassembly
|
||||
{
|
||||
entryPoints.Add(romInfo.ResetVector);
|
||||
}
|
||||
|
||||
foreach (var entryPoint in romInfo.EntryPoints)
|
||||
{
|
||||
entryPoints.Add(entryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEntyPoint(ushort address)
|
||||
{
|
||||
if (address >= 0x8000)
|
||||
{
|
||||
entryPoints.Add(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -196,6 +209,7 @@ namespace NESDecompiler.Core.Disassembly
|
||||
TraceExecution();
|
||||
IdentifyFunctions();
|
||||
GenerateLabels();
|
||||
EnsureReferencedAddressesAreDisassembled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -212,20 +226,24 @@ namespace NESDecompiler.Core.Disassembly
|
||||
/// <summary>
|
||||
/// Performs a linear disassembly of the code data
|
||||
/// </summary>
|
||||
private void LinearDisassembly()
|
||||
private void LinearDisassembly(int offset = 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
ushort baseAddress = 0x8000;
|
||||
// ushort baseAddress = 0x8000;
|
||||
ushort baseAddress = (ushort)(0x10000 - romInfo.PRGROMSize);
|
||||
|
||||
while (offset < codeData.Length)
|
||||
{
|
||||
ushort cpuAddress = (ushort)(baseAddress + offset);
|
||||
if (addressToInstruction.ContainsKey(cpuAddress))
|
||||
{
|
||||
// We have already disassembled this instruction and progressed from here,
|
||||
// so we can stop.
|
||||
break;
|
||||
}
|
||||
|
||||
byte opcode = codeData[offset];
|
||||
|
||||
var instructionInfo = InstructionSet.GetInstruction(opcode);
|
||||
|
||||
if (!instructionInfo.IsValid)
|
||||
@@ -267,13 +285,18 @@ namespace NESDecompiler.Core.Disassembly
|
||||
/// <summary>
|
||||
/// Traces execution from known entry points
|
||||
/// </summary>
|
||||
private void TraceExecution()
|
||||
private void TraceExecution(ushort? additionalTraceAddress = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var toTrace = new Queue<ushort>(entryPoints);
|
||||
var traced = new HashSet<ushort>();
|
||||
|
||||
if (additionalTraceAddress != null)
|
||||
{
|
||||
toTrace.Enqueue(additionalTraceAddress.Value);
|
||||
}
|
||||
|
||||
while (toTrace.Count > 0)
|
||||
{
|
||||
ushort address = toTrace.Dequeue();
|
||||
@@ -315,6 +338,7 @@ namespace NESDecompiler.Core.Disassembly
|
||||
|
||||
if (instruction.Info.Mnemonic == "JMP")
|
||||
{
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -398,7 +422,7 @@ namespace NESDecompiler.Core.Disassembly
|
||||
if (instruction.TargetAddress.HasValue)
|
||||
{
|
||||
ushort target = instruction.TargetAddress.Value;
|
||||
if (labels.TryGetValue(target, out string label))
|
||||
if (labels.TryGetValue(target, out string? label))
|
||||
{
|
||||
instruction.Comment = $"-> {label}";
|
||||
}
|
||||
@@ -437,6 +461,34 @@ namespace NESDecompiler.Core.Disassembly
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureReferencedAddressesAreDisassembled()
|
||||
{
|
||||
const int baseAddress = 0x8000;
|
||||
|
||||
// Keep tracing until we no longer have unknown referenced addresses. Using a for loop
|
||||
// to ensure we don't get stuck in an infinite loop (can probably happen if one instruction
|
||||
// attempts to jump to an unknown instruction I think).
|
||||
for (var count = 0; count < 100; count++)
|
||||
{
|
||||
var unknownReferencedAddresses = referencedAddresses
|
||||
.Where(x => !addressToInstruction.ContainsKey(x))
|
||||
.Where(x => x > baseAddress)
|
||||
.ToArray();
|
||||
|
||||
foreach (var referencedAddress in unknownReferencedAddresses)
|
||||
{
|
||||
var offset = referencedAddress - baseAddress;
|
||||
LinearDisassembly(offset);
|
||||
TraceExecution(referencedAddress);
|
||||
}
|
||||
|
||||
// Update functions and labels
|
||||
IdentifyFunctions();
|
||||
GenerateLabels();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the disassembly as a formatted string
|
||||
/// </summary>
|
||||
|
||||
@@ -73,10 +73,20 @@ namespace NESDecompiler.Core.ROM
|
||||
/// </summary>
|
||||
public ushort ResetVector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The NMI handler address
|
||||
/// </summary>
|
||||
public ushort NmiVector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IRQ handler address
|
||||
/// </summary>
|
||||
public ushort IrqVector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw ROM data for reference
|
||||
/// </summary>
|
||||
public byte[] RawData { get; set; }
|
||||
public byte[]? RawData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of identified entry points (including reset vector and NMI)
|
||||
|
||||
@@ -21,13 +21,13 @@ namespace NESDecompiler.Core.ROM
|
||||
private const int FLAGS_10_OFFSET = 10;
|
||||
|
||||
// ROM data
|
||||
private byte[] romData;
|
||||
private ROMInfo romInfo;
|
||||
private byte[]? romData;
|
||||
private ROMInfo? romInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Information about the loaded ROM
|
||||
/// </summary>
|
||||
public ROMInfo ROMInfo => romInfo;
|
||||
public ROMInfo? ROMInfo => romInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Loads a NES ROM file from disk
|
||||
@@ -75,7 +75,7 @@ namespace NESDecompiler.Core.ROM
|
||||
private ROMInfo ParseROMHeader()
|
||||
{
|
||||
// Verify iNES header signature "NES" followed by MS-DOS EOF
|
||||
if (romData.Length < HEADER_SIZE ||
|
||||
if (romData!.Length < HEADER_SIZE ||
|
||||
romData[0] != 0x4E || romData[1] != 0x45 || romData[2] != 0x53 || romData[3] != 0x1A)
|
||||
{
|
||||
throw new InvalidROMFormatException("Invalid iNES ROM header");
|
||||
@@ -107,15 +107,38 @@ namespace NESDecompiler.Core.ROM
|
||||
|
||||
romInfo.CHRROMOffset = romInfo.PRGROMOffset + romInfo.PRGROMSize;
|
||||
|
||||
// Identify entry points (reset vector)
|
||||
var end = romInfo.PRGROMOffset + romInfo.PRGROMSize;
|
||||
var nmiOffset = end - 6;
|
||||
var resetOffset = end - 4;
|
||||
var irqOffset = end - 2;
|
||||
|
||||
ushort GetVectorAddress(int offset)
|
||||
{
|
||||
if (offset >= 0 && offset < romData.Length - 1)
|
||||
{
|
||||
return (ushort)(romData[offset] | (romData[offset + 1] << 8));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Identify entry points (reset vector), NMI handler, and irq handler
|
||||
if (romInfo.PRGROMSize > 0)
|
||||
{
|
||||
// In 6502, reset vector is at 0xFFFC-0xFFFD
|
||||
// For NES, this is mapped to the end of the first PRG ROM bank
|
||||
int resetVectorOffset = romInfo.PRGROMOffset + romInfo.PRGROMSize - 4;
|
||||
if (resetVectorOffset >= 0 && resetVectorOffset < romData.Length - 1)
|
||||
romInfo.ResetVector = GetVectorAddress(resetOffset);
|
||||
romInfo.NmiVector = GetVectorAddress(nmiOffset);
|
||||
romInfo.IrqVector = GetVectorAddress(irqOffset);
|
||||
|
||||
if (romInfo.NmiVector > 0)
|
||||
{
|
||||
romInfo.ResetVector = (ushort)(romData[resetVectorOffset] | (romData[resetVectorOffset + 1] << 8));
|
||||
romInfo.EntryPoints.Add(romInfo.NmiVector);
|
||||
}
|
||||
|
||||
if (romInfo.IrqVector > 0)
|
||||
{
|
||||
romInfo.EntryPoints.Add(romInfo.IrqVector);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user