Skip to main content

SNES-Focused Hardware Specification

Why SNES as Primary Target

The ESP32-S3 makes SNES emulation achievable on a handheld device thanks to three key advantages:

  • SIMD/PIE instructions — 128-bit vector operations provide 2-10x speedup for pixel manipulation, critical for SNES PPU rendering (mode 7, rotation, scaling)
  • Octal PSRAM at ~84 MB/s — 4x faster than Quad SPI PSRAM on the original ESP32, enabling real-time frame buffer access without stalling the CPU
  • Dual-core LX7 at 240 MHz — dedicated cores for CPU emulation and PPU/audio rendering in parallel

Reference implementation: esp-box-emu demonstrates NES, SNES, and Genesis emulation running on ESP32-S3-BOX hardware.

SNES Emulation Requirements vs ESP32-S3

RequirementOriginal SNESESP32-S3 N16R8
CPU65C816 @ 3.58 MHzDual LX7 @ 240 MHz + SIMD (>60x headroom)
PPU (graphics)2 PPU chips, mode 7, 4 BG layersSIMD pixel ops + DMA to parallel display
WRAM128 KB512 KB internal SRAM
VRAM64 KB8 MB Octal PSRAM (shared)
ROM sizeUp to 6 MB (48 Mbit)SD card streaming + PSRAM cache
AudioSPC700 + S-DSP, 8 channels, 32 kHzI2S DMA output at 32 kHz stereo
Frame rate60 fps (NTSC) / 50 fps (PAL)Parallel display required for 60 fps
Resolution256×224 (most games)Scaled to 320×480 display

Display: Parallel vs SPI — Why 8080 is Mandatory

For SNES emulation, the display interface is the critical bottleneck:

ParameterSPI8-bit 8080 Parallel
Max clock~40 MHz~20 MHz
Bits per clock18
Throughput~5 MB/s~20 MB/s
320×480 @ 16-bit, 60 fps18.4 MB/s needed18.4 MB/s needed
Feasible at 60 fps?No (3.6x over capacity)Yes (1.09x margin)

The ILI9488 3.95" with 8-bit 8080 parallel interface (bare panel + 40-pin FPC) is the only viable option for SNES at 60 fps.

GPIO Pin Assignment

Complete pin mapping for the ESP32-S3 N16R8 DevKitC-1:

Display (8080 Parallel) — 12 GPIOs

GPIOFunctionFPC PinNotes
GPIO4LCD_D017Data bus bit 0
GPIO5LCD_D118Data bus bit 1
GPIO6LCD_D219Data bus bit 2
GPIO7LCD_D320Data bus bit 3
GPIO8LCD_D421Data bus bit 4
GPIO9LCD_D522Data bus bit 5
GPIO10LCD_D623Data bus bit 6
GPIO11LCD_D724Data bus bit 7
GPIO12LCD_CS9Chip select (active low)
GPIO13LCD_RST15Reset
GPIO14LCD_DC10Data/Command select
GPIO46LCD_WR11Write strobe

Hardwired on PCB (no GPIO):

  • LCD_RD (FPC pin 12) → tied HIGH to +3V3 (no read-back from ILI9488 needed)
  • LCD_BL / LED-A (FPC pin 33) → +5V through R27 (20 Ω) on net LED_BLA (always-on backlight, no PWM). Boards fabricated through v4.3.1 predate this and tie LED-A straight to +3V3 with no series element.

FPC power pins: 6=VDDI(+3V3), 7=VDDA(+3V3), 5/16/34-36/37=GND, 38=IM0(+3V3), 39=IM1(+3V3), 40=IM2(GND). Interface mode: IM2=0, IM1=1, IM0=1 → 8080 8-bit parallel.

FPC Pin Reversal on PCB

The FPC pin numbers above refer to the display pin numbering. On the PCB, the display is mounted in landscape (CCW rotation) with the FPC tail passing straight through a slot to the J4 connector on the bottom side. Because the cable doesn't twist, display Pin N contacts connector Pad (41−N). For example, display Pin 17 (LCD_D0) connects to J4 Pad 24. The PCB routing accounts for this reversal automatically.

SD Card (SPI) — 4 GPIOs

GPIOFunctionNotes
GPIO44SD_MOSIMaster Out Slave In
GPIO43SD_MISOMaster In Slave Out
GPIO38SD_CLKSPI clock
GPIO39SD_CSChip select

Audio (PDM) — 1 GPIO

GPIOFunctionNotes
GPIO17I2S_DOUTPDM sigma-delta data out → PAM8403 analog input (via C22)

Not used by audio: GPIO15 and GPIO16 were reserved as I2S_BCLK / I2S_LRCK until 2026-07-26 (R10-LOW-2). The path is PDM TX, which drives only DOUT (audio.c sets .clk = I2S_GPIO_UNUSED), so both pins are unconnected on the PCB and free for v2.

Buttons (GPIO Input, active-low) — 12 GPIOs

GPIOButtonNotes
GPIO40D-pad UP10k pull-up + 100nF debounce
GPIO41D-pad DOWN10k pull-up + 100nF debounce
GPIO42D-pad LEFT10k pull-up + 100nF debounce
GPIO1D-pad RIGHT10k pull-up + 100nF debounce
GPIO2A10k pull-up + 100nF debounce
GPIO48B10k pull-up + 100nF debounce
GPIO47X10k pull-up + 100nF debounce
GPIO21Y10k pull-up + 100nF debounce
GPIO0SELECTBoot button (dual-use)
GPIO18START10k pull-up + 100nF debounce
GPIO45L shoulderInternal pull-up only (GPIO45 is VDD_SPI strapping, R14 DNP)
GPIO3R shoulder10k pull-up + 100nF debounce

USB (native) — 2 GPIOs

GPIOFunctionNotes
GPIO19USB D-Native USB data (firmware flash + debug)
GPIO20USB D+Native USB data (firmware flash + debug)

Reserved GPIOs

GPIOsReason
GPIO26–GPIO32Internal SPI flash bus of the WROOM-1 module — not brought out on any module pin
GPIO33–GPIO37Octal PSRAM of the N16R8 variant. GPIO33/34 are not brought out; GPIO35–37 appear on module pins 28–30 but must stay unconnected (explicit no-connect markers in the schematic)

Summary

CategoryGPIOs Used
Display (8080 parallel)12
SD Card (SPI)4
Audio (PDM)1
Buttons12
USB (native)2
Total31
Exposed on the WROOM-1 module36
Remaining5 (GPIO15, GPIO16, and the three PSRAM pins that must stay free)

The bring-up firmware asserts this independently — config.gpio_unique reports 31 GPIO assignments, all distinct (see Bring-Up Protocol).

Audio Architecture

The SNES has a sophisticated audio system (SPC700 + S-DSP) with 8 channels of BRR-compressed audio. Our implementation:

ESP32-S3 (PDM TX + DMA) ──> C22 (DC block) ──> PAM8403 Class-D Amp ──> 28mm 8Ω Speaker
│ │
GPIO17 (I2S_DOUT) R20/R21 bias to VREF (pin 8)
  • Sample rate: 32 kHz (matches SNES native rate)
  • Bit depth: 16-bit
  • Interface: PDM sigma-delta on a single pin — the PAM8403's analog input plus its input RC network reconstructs the waveform, so no external DAC and no BCLK/LRCK are needed
  • DMA buffer: Double-buffered for glitch-free playback
  • Amplifier: PAM8403 2x3W Class-D (only the right channel drives the mono speaker)

Reference Implementations

ProjectPlatformSNES Support
esp-box-emuESP32-S3-BOX-3Yes (with snes9x core)
snes9x2005libretro coreLightweight, suitable for embedded
Retro-GoVarious ESP32Partial (limited performance)