Zurück zum Projektjournal

PROJEKTNOTIZEN / C++20

NES Emulator

Ein NES-Desktop-Emulator mit zyklusbewusstem CPU- und PPU-Verhalten, Cartridge-Mapping, synchronisierter Darstellung und integrierter Debugging-Oberfläche.

NES-Emulator im Desktopfenster mit ImGui-CPU-Debugger neben der gerenderten Spielausgabe
Hardware-synchronisierte Emulation: Dear ImGui Debugging-Dashboard parallel zur modernen OpenGL 60 FPS Spielausgabe.
Modernes C++20
Zyklusgenaue 6502-CPU
256 × 240 PPU (60 FPS)
4 Cartridge-Mapper
5-Kanal APU (44,1 kHz)
Dear ImGui & OpenGL 3.3
Interaktiver C++20 NES-Emulator & ImGui Live-Debugger
Zyklen: 7Frame: #1
6502 CPU-RegisterMOS 6502 @ 1.79 MHz
A (ACC)
$00
X (IDX)
$00
Y (IDX)
$00
SP (STACK)
$FD
PC (PROG)
$C000
STATUS FLAGS REGISTER (P):%00100110
N
0
V
0
-
1
B
0
D
0
I
1
Z
1
C
0
2C02 PPU Raster-StatusRicoh 2C02 @ 5.37 MHz
Scanline:
0 / 261
PPU Cycle:
21 / 340
VBlank ($2002.7):INACTIVE
Sprite 0 Hit ($2002.6):FALSE
Nametable Mirroring:Vertical (Horiz Scroll)
Aktive Sub-Paletten ($3F00)
6502 Disassembler-AblaufPC Target: $C000
▶$C000SEI
; Disable maskable IRQ interrupts
$C001CLD
; Clear decimal mode flag (NES 2A03 lacks BCD)
$C002LDX #$FF
; Initialize stack pointer index to $FF
$C004TXS
; Transfer X to Stack Pointer (SP = $FF)
$C005LDA #$00
; Clear accumulator (A = $00)
$C007STA $2000
; Write to PPUCTRL: disable NMI on VBlank
$C00ASTA $2001
; Write to PPUMASK: disable screen rendering
$C00DLDX #$0A
; Load X register with loop counter = 10
$C00FSTA $00,X
; Zero-page store indexed: RAM[$00 + X] = $00
$C011BIT $2002
; Poll PPUSTATUS: wait for VBlank and clear flag
$C014LDA #$80
; Load A with $80 (bit 7 = 1: enable NMI)
$C016STA $2000
; Write PPUCTRL: enable NMI on VBlank!
Zero-Page RAM ($0000 - $000F)2KB Internal RAM
$00
00
$01
00
$02
00
$03
00
$04
00
$05
00
$06
00
$07
00
$08
00
$09
00
$0A
00
$0B
00
$0C
00
$0D
00
$0E
00
$0F
00
APU Audio Output:44.1 kHz (SDL2)
Active Mapper:MMC1 (Mapper 1)
Debugger über OpenGL / Dear ImGui Docking-Bus angebunden
OpenGL 3.3 Core•SDL2 Audio Queue•C++20

SYSTEMS ARCHITECTURE / C++20 EMULATION

Zyklusgenaue Konsolen-Emulation & Echtzeit-Debugging in modernem C++20

Das Nintendo Entertainment System (NES) ist ein Paradebeispiel für synchrone Hardware-Architektur. Mit einer 1,789 MHz Ricoh 2A03 CPU, einer 5,37 MHz Ricoh 2C02 Picture Processing Unit (PPU) und einer integrierten Audio Processing Unit (APU) erfordert das System eine exakte Takt- und Interrupt-Koordination. Dieser Desktop-Emulator bildet den gesamten Hardwarepfad in modernem C++20 nach und kombiniert zyklusgenaue Emulation mit einer dockbaren Dear ImGui Debugger-Suite zur Echtzeitanalyse von Registern, Disassemblierung, Speicherkarten und Rasterstrahl-Timing.

01

Ricoh 2A03 (MOS 6502) CPU-Kern

Führt alle 56 offiziellen 6502-Befehle sowie dokumentierte inoffizielle/illegale Opcodes aus. Modelliert Adressierungsarten (Zero Page, Indiziert, Indirekt, Absolut), Zykluszuschläge bei Seitengrenzen-Überschreitung und vollständige Interrupt-Logik (RESET bei $FFFC, NMI bei $FFFA, IRQ/BRK bei $FFFE). Der BCD-Dezimalmodus ist hardwareseitig deaktiviert (Ricoh-Spezifikation).

02

Ricoh 2C02 Picture Processing Unit (PPU)

Rendert mit 256 × 240 Pixeln bei 60 FPS über 262 Scanlines. Enthält die vollständige 'Loopy'-Scrolling-Engine (Register v, t, x, w), 2 Pattern Tables ($0000/$1000), 4 Nametables mit dynamischem Mirroring (Horizontal, Vertikal, Einzelschirm, Vierschirm), 64-Sprite-OAM-Verarbeitung mit 8-Sprites-pro-Scanline-Begrenzung und zyklusgenaue Sprite-0-Treffererkennung.

03

Ricoh 2A03 Audio Processing Unit (APU)

Erzeugt 5 unabhängige Audiokanäle: Puls 1 & Puls 2 (variable Tastgrade, Frequenz-Sweep, Hüllkurvendekrement), Dreieck (32-Stufen-Pseudosinus mit linearem Zähler), Rauschen (15-Bit-LFSR-Zufallsfolge) und DMC (Delta-Modulation für 1-Bit-Samples). Gesteuert über 4- und 5-Stufen-Frame-Sequenzer und nichtlineare Mischung für SDL2-Wiedergabe bei 44,1 kHz.

04

Cartridge-Mapper & Speicher-Multiplexing

Liest das iNES-Dateiformat (.nes) und verwaltet Cartridge-Speicherbereiche. Unterstützt Mapper 0 (NROM: 16KB/32KB PRG, 8KB CHR), Mapper 1 (MMC1: serielles 5-Bit-Schieberegister mit Bankumschaltung und dynamischem Mirroring), Mapper 2 (UxROM: PRG-Bankumschaltung mit fester oberer Bank) und Mapper 3 (CNROM: 8KB CHR-Bankumschaltung).

Architektur-Merkmale in modernem C++20

Compile-Time Opcode-Tabelle

Nutzt constexpr-Lookup-Tabellen für verzweigungsfreie O(1)-Befehlsdecodierung und Zyklusberechnung.

Sichere Speicherfenster

Verwendet std::span zur allokationsfreien Darstellung und Segmentierung von Speicherbereichen.

Bitmanipulation (<bit>)

Setzt Standardbibliotheksfunktionen (std::rotl, std::rotr, std::popcount) für schnelle Bitverschiebungen ein.

Resource Ownership

Kapselt Cartridge, Bus, PPU und APU in std::unique_ptr ohne Speicherlecks oder freie Zeiger.

Cache-optimierte Arrays

Speichert 2KB RAM und 2KB VRAM in flachen std::array-Puffern für maximale L1-Cache-Lokalität.

NES CPU Speicherbus-Architektur ($0000 - $FFFF)

$0000 - $07FF
2KB internes Arbeits-RAM (Zero Page bei $0000-$00FF, Stack bei $0100-$01FF)
$0800 - $1FFF
Spiegelungen von $0000 - $07FF (dreifach gespiegelt)
$2000 - $2007
PPU I/O-Register (PPUCTRL, PPUMASK, PPUSTATUS, OAMADDR, OAMDATA, PPUSCROLL, PPUADDR, PPUDATA)
$2008 - $3FFF
Spiegelungen von $2000 - $2007 (alle 8 Bytes wiederholt)
$4000 - $4017
APU Sound-Register, OAM DMA-Register ($4014), Joypad 1 & 2 ($4016, $4017)
$4020 - $5FFF
Cartridge-Erweiterungs-ROM
$6000 - $7FFF
Batteriegepuffertes Cartridge PRG-RAM (Save RAM)
$8000 - $BFFF
Cartridge PRG-ROM (Untere Bank / über Mapper umschaltbar)
$C000 - $FFFF
Cartridge PRG-ROM (Obere Bank / Vektoren: NMI @ $FFFA, RESET @ $FFFC, IRQ @ $FFFE)

Gamepad-Steuerung & Emulator-Tastaturkürzel

Pfeiltasten
NES Steuerkreuz (Hoch, Runter, Links, Rechts)
Z / X
NES A-Taste / B-Taste
Enter / rechte Umschalttaste
NES Start / Select
F1
Dear ImGui Debugging-Fenster ein-/ausblenden
F2
6502-CPU Einzelschritt ausführen
F3
PPU-Frame weiterschalten (262 Scanlines)
F5 / Leertaste
Ausführung fortsetzen / pausieren
Esc
Emulator beenden

Build- und Toolchain-Umgebung

Moderner C++20-Compiler (MSVC 2022 / Clang / GCC 11+) · CMake 3.20+ · Ninja / MSBuild · Modernes OpenGL 3.3+ · SDL2 · Dear ImGui (Docking Branch)

Desktop NES-Emulator auf GitHub ansehen