14 Commits
Author SHA1 Message Date
ApfelTeeSaft 11b9943c09 Update build-release.yml 2025-10-09 14:04:39 +02:00
ApfelTeeSaft 54b9e3d3df Update build-release.yml 2025-10-09 14:01:44 +02:00
ApfelTeeSaft a9f323722f Add CI/CD workflow 2025-10-09 13:58:18 +02:00
ApfelTeeSaft e8a57399ce Merge pull request #7 from KallDrexx/16kb_fix
Fix 16KB roms not decompiling
2025-09-29 08:47:45 +02:00
KallDrexx 9068403d56 Fix 16KB roms not decompiling
Roms with only 16KB of program code had the incorrect base address during the disassembly process. This caused the instructions not to be found.

Fixed by making sure the base address is adjusted based on the prgRomData size.
2025-09-28 21:16:37 -04:00
ApfelTeeSaft bc76d20895 Merge pull request #6 from KallDrexx/loop_unreferenced_instruction_analysis
Loop EnsureReferencedAddressesAreDisassembled
2025-09-28 20:23:11 +02:00
KallDrexx 40743c6889 Loop EnsureReferencedAddressesAreDisassembled
`EnsureReferencedAddressesAreDisassembled is needed to be run because
a jump/branch may occur that targets interwoven instructions that would
not have been picked up on the first pass.

However, an interwoven instruction may actually end up with a branch
into another interwoven instruction. These instructions wouldn't be
picked seen by the end of the 2nd pass.

So the logic in this function is now set to loop (with a maximum) until
all target addresses that are directly referenced have been
disassembled.
2025-09-28 11:20:12 -04:00
ApfelTeeSaft 32e3d78e6d Merge pull request #5 from KallDrexx/nmi_decompiling
Decompile direct jumps to instructions that are in between other instructions
2025-09-26 14:19:51 +02:00
KallDrexx b627bc2a48 Decompiler direct jumps to instructions that are in between other instructions 2025-09-25 21:59:20 -04:00
ApfelTeeSaft 1e9899b9bc Merge pull request #4 from KallDrexx/nmi_decompiling
Add decompililng of other entrypoints, and add specific support for NMI and IRQ vectors
2025-09-25 02:52:33 +02:00
KallDrexx e592c31a0c Add decompililng of other entrypoints, and add specific support for NMI and IRQ vectors 2025-09-24 20:33:38 -04:00
KallDrexx b968b151d0 Fix nullability warnings 2025-09-10 21:30:08 -04:00
ApfelTeeSaft 588841b46b Merge pull request #2 from KallDrexx/instruction_tostring_fix
Fixed instruction ToString() formatting.
2025-09-08 02:03:51 +02:00
KallDrexx c1e0b17bb3 Fixed instruction ToString() formatting.
`GetOperandFormat` was accidentally using C++ style format strings
instead of C# ones, and thus the values were not being properly
displayed. For example, `LDA #00` was showing as `LDA #$02X`, making it
a bit hard to reason about what value the operand is.
2025-09-07 14:03:35 -04:00
7 changed files with 418 additions and 41 deletions
+291
View File
@@ -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
+1 -1
View File
@@ -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();
+11 -11
View File
@@ -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",
_ => "???"
};
}
+10 -9
View File
@@ -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)
{
+63 -11
View File
@@ -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>
+11 -1
View File
@@ -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)
+31 -8
View File
@@ -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);
}
}