Game Boy Emulator

Erstellt am 1. Februar 2026


Ein von Grund auf in Rust entwickelter Emulator für den Nintendo Game Boy (DMG-01), einer der einflussreichsten tragbaren Spielkonsolen der Computergeschichte. Dieses Projekt konzentriert sich auf Genauigkeit, Verständlichkeit und saubere Architektur statt reiner Performance.


🎯 Kernziele

  • Korrekte Hardware-Emulation: Detaillierte Abbildung der Game-Boy-CPU (LR35902)
  • Lernorientiert: Jede Komponente ist verständlich und dokumentiert
  • Nachvollziehbar: Keine „Magic Code" – alles ist transparent und testbar
  • Modular: Klare Trennung von CPU, Speicher, PPU und Timer

🏗️ Architektur

Das Emulator-Design folgt der echten Hardware-Struktur:

┌──────────────────────────────────────┐
│         Main Emulator Loop           │
│  • Input Processing (Joypad)         │
│  • CPU Step                          │
│  • PPU Step (nach Zyklen)            │
│  • Frame Rendering                   │
└──────────────────────────────────────┘
         ↓         ↓         ↓
    ┌─────────┬──────────┬──────────┐
    │   CPU   │   MMU    │   PPU    │
    │ LR35902 │ Speicher │ Graphics │
    └─────────┴──────────┴──────────┘
         ↓         ↓          ↓
    [Register]   [RAM]    [Display]

CPU (LR35902)

pub struct Cpu {
    pub program_counter: u16,
    pub stack_pointer: u16,
    pub registers: Registers,      // A, F, B, C, D, E, H, L
    pub ime: bool,                 // Interrupt Master Enable
    pub halted: bool,              // HALT-Status
}

Die CPU implementiert das komplette Game Boy Z80-Instruction Set:

  • 256 Standard-Opcodes
  • 256 CB-Prefixed Opcodes (Bit-Operationen)
  • ALU-Operationen: ADD, ADC, SUB, SBC, AND, OR, XOR, CP
  • Flag-Handling (Z, N, H, C)
  • Interrupts & Halt-Zustände

Besonderheit: Das Opcode-Decoding ist vollständig optimiert mit expliziten Match-Armen für maximale Klarheit und bessere Compiler-Optimierungen.

Memory Management Unit (MMU)

pub struct Mmu {
    rom: Vec<u8>,             // 0x0000–0x7FFF (ROM)
    wram: [u8; 0x2000],       // 0xC000–0xDFFF (Working RAM)
    hram: [u8; 0x80],         // 0xFF80–0xFFFE (High RAM)
    io_registers: [u8; 0x80], // Hardware-Register
}

Die MMU ist der ausschließliche Zugriffspunkt für die CPU auf den Speicher. Dies gewährleistet:

  • Korrekte Speicher-Mapping
  • Lazy Loading von ROM-Daten
  • Hardware-Register-Emulation
  • Interrupt-Flag-Verwaltung

PPU (Graphics Processor)

pub enum PpuMode {
    OamScan, // 80 Zyklen – OAM-Scan
    Drawing, // 172 Zyklen – Scanline zeichnen
    HBlank,  // 204 Zyklen – Horizontal Blanking
    VBlank,  // Vertikal Blanking (10 Scanlines)
}

Die PPU folgt der exakten Timing-Spezifikation des Game Boy:

  • Mode-Abläufe nach CPU-Zyklen
  • Scanline-Rendering mit Hintergrund- und Sprite-Layer
  • Framebuffer-Ausgabe (160×144 Pixel)
  • VBlank-Interrupt zur Bildaktualisierung

💻 Technische Highlights

1. Vollständiges Opcode-Decoding

// Beispiel: 8-Bit Addition mit Carry Flag
0x89 => {  // ADD A, C
    let val = self.registers.read8(&Reg8::C);
    self.alu_add_u8(val);
    4  // Zyklen
}

Über 500 Opcodes sind explizit definiert mit korrektem Cycle-Timing.

2. Präzises Flag-Handling

Alle CPU-Flags werden korrekt nach jeder Operation gesetzt:

  • Z-Flag: Gesetzt, wenn Ergebnis = 0
  • N-Flag: Gesetzt bei Subtraktion
  • H-Flag: Gesetzt bei Half-Carry (Bit 3→4)
  • C-Flag: Gesetzt bei Carry/Borrow
fn alu_add_u8(&mut self, value: u8) {
    let result = self.registers.a.wrapping_add(value);
    
    self.registers.flags.z = result == 0;
    self.registers.flags.n = false;
    self.registers.flags.h = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
    self.registers.flags.c = (self.registers.a as u16) + (value as u16) > 0xFF;
    
    self.registers.a = result;
}

3. Hardware-akkurate Timing

Jeder Opcode hat sein korrektes Cycle-Timing:

  • NOP: 4 Zyklen
  • Speicherzugriffe: 8 oder 16 Zyklen
  • Bedingte Sprünge: 12/8 Zyklen (je nach Bedingung)

Dies ermöglicht korrektes Zusammenspiel mit PPU und Timer.

4. Modulare Input-Verarbeitung

pub enum Key {
    Right, Left, Up, Down,
    Start, Select, B, A,
}

// Real-time Input Processing
match key {
    Key::A => self.mmu.key_down(key),
    Key::B => self.mmu.key_down(key),
    _ => {}
}

Das Joypad ist direkt in die MMU integriert und triggert Interrupts bei Tastendrücken.


🎮 Unterstützte ROM-Games

✅ Pokemon Red
✅ Tetris
✅ Mooneye Test Suite (für Korrektheit validiert)

📊 Code-Struktur

src/
├── main.rs              # Event Loop & Fenster-Management
├── gameboy.rs           # Hauptemulator-Struktur
├── rom.rs               # ROM-Laden und -Parsing
└── gameboy/
    ├── cpu.rs           # CPU-Logik
    ├── mmu.rs           # Memory Management Unit
    ├── ppu.rs           # Graphics Processor
    ├── timer.rs         # Timer-Emulation
    ├── joypad.rs        # Input-Handling
    └── screen/
        ├── window.rs    # GUI (minifb)
        └── framebuffer.rs   # Pixel-Buffer

🚀 Performance & Optimierungen

  • Rust Memory Safety: Keine Segmentation Faults dank Borrow Checker
  • Zero-Cost Abstractions: Hohe Performanz trotz klarer Struktur
  • Inlining: Häufig aufgerufene Funktionen werden inline optimiert
  • Direct Memory Access: Schnelle RAM-Zugriffe ohne Indirection

Messungen:

  • ✅ Echtzeitausführung für die meisten ROMs
  • ✅ CPU läuft stabil bei 4,194 MHz

📚 Referenzen & Datenquellen

Dieses Projekt basiert auf den maßgeblichsten Game-Boy-Dokumentationen:


🔧 Build & Run

# ROM-Datei ausführen
cargo run --release -- --rom_path roms/games/Pokemon_Red.gb

# Mit Debugging
cargo run -- --rom_path roms/games/Tetris.gb

Dependencies:

  • minifb: Cross-platform window handling & rendering

📈 Gelernte Konzepte

Low-Level Emulation: Hardware-Timing, Instruction Decoding
Rust Systems Programming: Memory Safety in kritischen Systemen
CPU Architektur: Register, Flags, Interrupts, Stack
Grafik-Pipelines: Scanline-Rendering, Mode-Timing
Timing-Kritische Systeme: Cycle-accurate Emulation


🔮 Zukünftige Erweiterungen

  • Sound (APU) – Audio-Chip-Emulation
  • MBC1/MBC3/MBC5 – Cartridge-Support
  • Speicher-Persistierung (Save States)
  • Debugger mit Breakpoints
  • Performance-Profiling

💡 Warum dieses Projekt?

Gameboy-Emulation ist ein Klassiker der Emulator-Entwicklung – es bietet die perfekte Balance zwischen:

  • Komplexität: Echte CPU, Memory Management, Timing
  • Lernpotential: Hardware-Details sind zugänglich und gut dokumentiert
  • Erfolgserlebnis: Funktionsfähige Games laufen
  • Rust-Anwendung: Perfekter Use-Case für Systems Programming

Dieses Projekt zeigt sowohl technisches Verständnis als auch saubere Software-Architektur in einer Sprache, die Sicherheit und Performance vereint.