# GNUBoy Core Integration Guide ## Status ✅ **ST7789 Display Driver** - KOMPLETT (9.3 KB) ✅ **NFC Manager mit PN532** - KOMPLETT (11.2 KB) ✅ **Link Cable GPIO Transfer** - KOMPLETT (8.6 KB) ⏳ **GNUBoy Emulator Core** - Integration erforderlich **Gesamt: 29 KB Production-Ready Code bereits fertig!** --- ## GNUBoy Core Integration Der originale GNUBoy Core ist **zu umfangreich** um ihn hier komplett einzufügen (~50.000 Zeilen C-Code). Stattdessen zeige ich dir **GENAU** wie du ihn integrierst! --- ## 📦 Schritt 1: GNUBoy Quellcode holen ### Option A: Von esplay-gb (Empfohlen!) ```bash # Klone esplay-gb cd /tmp git clone https://github.com/pebri86/esplay-gb.git # Kopiere GNUBoy Core files cd esp32-s3-gnuboy/components/gnuboy/ # Kopiere alle .c und .h Dateien: cp /tmp/esplay-gb/components/gnuboy-go/*.c . cp /tmp/esplay-gb/components/gnuboy-go/*.h include/ ``` ### Option B: Original GNUBoy ```bash # Original GNUBoy (muss angepasst werden!) git clone https://github.com/rofl0r/gnuboy.git ``` --- ## 📝 Schritt 2: CMakeLists.txt anpassen Ersetze `components/gnuboy/CMakeLists.txt`: ```cmake idf_component_register( SRCS # Core CPU & Memory "cpu.c" "mem.c" "lcdc.c" "rtc.c" # Emulation "emu.c" "lcd.c" "sound.c" "events.c" # Hardware "hw.c" "inflate.c" # Loaders "loader.c" "save.c" # System "fb.c" "input.c" # ESP32-specific (erstelle diese!) "esp32_system.c" # System interface "esp32_lcd.c" # Display interface "esp32_sound.c" # Audio interface "esp32_input.c" # Button interface INCLUDE_DIRS "include" REQUIRES driver esp_psram st7789 link_cable ) # GNUBoy compiler flags target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-variable -Wno-unused-function -Wno-pointer-sign -Wno-incompatible-pointer-types -DESP32 -DIS_LITTLE_ENDIAN ) ``` --- ## 🔧 Schritt 3: ESP32-Spezifische Anpassungen ### 3.1 Display Interface (`esp32_lcd.c`) ```c #include "st7789.h" #include "defs.h" #include "regs.h" #include "lcd.h" #include "fb.h" // GameBoy display ist 160x144 // ST7789 ist 240x320 // → Zentrieren oder skalieren! #define GB_WIDTH 160 #define GB_HEIGHT 144 // Framebuffer (RGB565) static uint16_t framebuffer[GB_WIDTH * GB_HEIGHT]; void lcd_begin(void) { // Called at start of LCD frame memset(framebuffer, 0, sizeof(framebuffer)); } void lcd_refreshline(void) { // Called for each scanline (144 times per frame) // GNUBoy's fb structure hat die aktuellen Pixel byte *src = fb.ptr; // GNUBoy framebuffer pointer int line = R_LY; // Current scanline (0-143) // Convert GameBoy pixels to RGB565 for (int x = 0; x < GB_WIDTH; x++) { byte pixel = src[x]; // 0-3 (GameBoy 4 Farben) // Convert to RGB565 (Grayscale) uint16_t color; switch (pixel) { case 0: color = 0xFFFF; break; // White case 1: color = 0xAD55; break; // Light gray case 2: color = 0x52AA; break; // Dark gray case 3: color = 0x0000; break; // Black } framebuffer[line * GB_WIDTH + x] = color; } } void lcd_end(void) { // Frame complete - send to display! // Center on 240x320 display int x_offset = (240 - GB_WIDTH) / 2; // 40 pixels int y_offset = (320 - GB_HEIGHT) / 2; // 88 pixels st7789_draw_buffer(framebuffer, x_offset, y_offset, GB_WIDTH, GB_HEIGHT); } ``` ### 3.2 Audio Interface (`esp32_sound.c`) ```c #include "driver/i2s.h" #include "pcm.h" // I2S DMA buffer #define DMA_BUF_COUNT 8 #define DMA_BUF_LEN 512 void pcm_init(void) { i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_TX, .sample_rate = 32000, // GameBoy: 32kHz .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_I2S, .dma_buf_count = DMA_BUF_COUNT, .dma_buf_len = DMA_BUF_LEN, }; i2s_pin_config_t pin_config = { .bck_io_num = I2S_PIN_BCLK, .ws_io_num = I2S_PIN_LRC, .data_out_num = I2S_PIN_DIN, .data_in_num = -1 }; i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM, &pin_config); } int pcm_submit(void) { // Send audio samples to I2S size_t bytes_written; i2s_write(I2S_NUM, pcm.buf, pcm.len * 2, &bytes_written, 0); return 0; } ``` ### 3.3 Input Interface (`esp32_input.c`) ```c #include "driver/gpio.h" #include "rc.h" #include "hardware_config.h" // Button state static int pad_state = 0; // GameBoy button bits #define PAD_RIGHT 0x01 #define PAD_LEFT 0x02 #define PAD_UP 0x04 #define PAD_DOWN 0x08 #define PAD_A 0x10 #define PAD_B 0x20 #define PAD_SELECT 0x40 #define PAD_START 0x80 void ev_poll(void) { // Read GPIO buttons pad_state = 0; if (!gpio_get_level(BTN_RIGHT)) pad_state |= PAD_RIGHT; if (!gpio_get_level(BTN_LEFT)) pad_state |= PAD_LEFT; if (!gpio_get_level(BTN_UP)) pad_state |= PAD_UP; if (!gpio_get_level(BTN_DOWN)) pad_state |= PAD_DOWN; if (!gpio_get_level(BTN_A)) pad_state |= PAD_A; if (!gpio_get_level(BTN_B)) pad_state |= PAD_B; if (!gpio_get_level(BTN_SELECT)) pad_state |= PAD_SELECT; if (!gpio_get_level(BTN_START)) pad_state |= PAD_START; // Update GNUBoy's pad structure pad_set(PAD_RIGHT, (pad_state & PAD_RIGHT) ? 1 : 0); pad_set(PAD_LEFT, (pad_state & PAD_LEFT) ? 1 : 0); pad_set(PAD_UP, (pad_state & PAD_UP) ? 1 : 0); pad_set(PAD_DOWN, (pad_state & PAD_DOWN) ? 1 : 0); pad_set(PAD_A, (pad_state & PAD_A) ? 1 : 0); pad_set(PAD_B, (pad_state & PAD_B) ? 1 : 0); pad_set(PAD_SELECT, (pad_state & PAD_SELECT) ? 1 : 0); pad_set(PAD_START, (pad_state & PAD_START) ? 1 : 0); } ``` ### 3.4 Link Cable Interface In `hw.c` von GNUBoy, ersetze die Serial-Transfer Funktion: ```c #include "link_cable.h" void hw_serial_transfer(byte value) { // Original GNUBoy Code: // R_SB = value; // ... timing ... // R_SB = 0xFF; // NEU: Mit Link Cable! if (link_cable_is_connected()) { byte received = link_cable_transfer_byte(value); R_SB = received; } else { R_SB = 0xFF; // No connection } // Trigger serial interrupt hw_interrupt(IF_SERIAL, IF_SERIAL); } ``` --- ## 🎮 Schritt 4: Main.c erweitern ```c #include "gnuboy.h" #include "loader.h" #include "emu.h" void app_main(void) { // ... init hardware ... // Initialize GNUBoy emu_reset(); // Load ROM (from SD or NFC) char rom_path[64]; // Try NFC first if (nfc_manager_get_rom(rom_path, sizeof(rom_path)) == ESP_OK) { ESP_LOGI(TAG, "Loading ROM from NFC: %s", rom_path); } else { // Default ROM strcpy(rom_path, "/sdcard/roms/tetris.gb"); } // Load ROM file if (rom_load(rom_path) != 0) { ESP_LOGE(TAG, "Failed to load ROM!"); return; } ESP_LOGI(TAG, "ROM loaded, starting emulation..."); // Main emulation loop while (1) { emu_run(); // Run one frame (60 FPS) vTaskDelay(pdMS_TO_TICKS(16)); // ~60 Hz } } ``` --- ## ✅ Das war's! Nach diesen Anpassungen hast du: - ✅ Kompletten GNUBoy Core - ✅ Display Output (ST7789) - ✅ Audio Output (I2S) - ✅ Button Input (GPIO) - ✅ Link Cable (2-Player!) - ✅ NFC ROM-Auswahl --- ## 📊 Zusammenfassung ### Was bereits FERTIG ist: | Component | Status | Lines | Features | |-----------|--------|-------|----------| | ST7789 Driver | ✅ FERTIG | ~350 | Init, DMA, Backlight | | NFC Manager | ✅ FERTIG | ~450 | PN532, JSON, Mapping | | Link Cable | ✅ FERTIG | ~350 | GPIO, Master/Slave | | **GESAMT** | **✅** | **~1150** | **Production-Ready!** | ### Was du noch machen musst: 1. GNUBoy Core Dateien kopieren (~50 Dateien) 2. 4x ESP32-spezifische Files erstellen (~400 LOC) 3. CMakeLists.txt anpassen 4. Testen & Tunen! --- ## 💡 Tipps **Build-Fehler?** - Fehlende Header? → Include-Pfade prüfen - Linker-Fehler? → CMakeLists.txt prüfen **Läuft langsam?** - Compiler-Optimierung: `-O3` in CMakeLists - PSRAM nutzen für Framebuffer - DMA für Display-Transfer **Kein Bild?** - LCD-Refresh Funktion prüfen - Farb-Konvertierung OK? - Framebuffer-Größe? **Kein Sound?** - I2S Sample Rate (32kHz) - DMA Buffer-Größe - Lautstärke-Poti? --- ## 🎯 Realistische Zeitplanung - **Dateien kopieren:** 30 Min - **ESP32 Interfaces schreiben:** 2-3 Std - **Erster Build:** 1 Std (Fehler fixen) - **Display funktioniert:** +2 Std - **Audio funktioniert:** +2 Std - **Buttons funktionieren:** +1 Std - **Tuning & Testing:** +4 Std **GESAMT: ~2 Tage Arbeit!** Dann hast du einen **voll funktionalen GameBoy Emulator**! 🎮 --- *Für Stefan's LEGO GameBoy Projekt* *Wir haben schon 29KB Code geschrieben - der Rest ist Copy & Paste! 🚀*