Skip to content

Latest commit

 

History

History
93 lines (85 loc) · 4.28 KB

File metadata and controls

93 lines (85 loc) · 4.28 KB

You are an expert in Rust, WebAssembly, and Next.js. Help me build a multi-system emulator that runs in the browser, supporting both Game Boy (DMG) and NES.

Tech stack

  • Emulator core: Rust compiled to WebAssembly via wasm-pack + wasm-bindgen
  • Frontend: Next.js 14 (App Router) + React + TypeScript
  • Styling: Tailwind CSS
  • Build: wasm-pack for Rust → WASM, Next.js for the frontend

Project structure

rustboy/ ├── core/ # Rust crate (all emulator backends) │ ├── src/ │ │ ├── lib.rs # wasm-bindgen exports (SystemEmulator enum) │ │ ├── gb/ # Game Boy (DMG) backend │ │ │ ├── mod.rs │ │ │ ├── cpu.rs # Sharp LR35902 CPU │ │ │ ├── ppu.rs # Pixel processing unit │ │ │ ├── apu.rs # Audio processing unit │ │ │ ├── mmu.rs # Memory map / bus │ │ │ └── timer.rs # Timer registers │ │ └── nes/ # NES backend │ │ ├── mod.rs │ │ ├── cpu.rs # Ricoh 2A03 (6502) CPU │ │ ├── ppu.rs # NES PPU (256×240) │ │ ├── apu.rs # NES APU (pulse×2, triangle, noise, DMC) │ │ ├── bus.rs # Memory map / bus │ │ └── cartridge.rs # iNES parser + mapper 0/1/2/3 │ └── Cargo.toml └── web/ # Next.js app ├── app/ ├── components/ │ ├── Emulator.tsx # Main emulator shell (detects system from ROM) │ ├── Screen.tsx # Canvas renderer (variable resolution) │ └── Controls.tsx # Button UI + keyboard input └── hooks/ └── useEmulator.ts # WASM lifecycle hook

Shared WASM API (wasm-bindgen)

The Rust core exposes a single Emulator struct that wraps either backend:

  • Emulator::new(rom: &[u8]) -> Emulator — auto-detects system from ROM header
  • emulator.step_frame() — runs one full frame
  • emulator.frame_buffer() -> *const u8 — RGBA pixels (size depends on system)
  • emulator.frame_width() -> u32
  • emulator.frame_height() -> u32
  • emulator.set_joypad(button: u8, pressed: bool)
  • emulator.audio_buffer() -> Vec<f32> — samples since last frame (44100 Hz)

ROM auto-detection

  • .gb / .gbc: Game Boy — header at 0x0104–0x014F
  • .nes: NES — iNES magic bytes 4E 45 53 1A at offset 0

Hardware reference — Game Boy (DMG)

  • Clock: 4.194304 MHz → 70224 cycles per frame at 59.7 fps
  • Screen: 160×144 pixels, 4 shades of green
  • CPU: Z80-ish, 8-bit registers (A, B, C, D, E, H, L, F), 16-bit PC/SP
  • Memory map:
    • 0x0000–0x7FFF ROM (cartridge)
    • 0x8000–0x9FFF VRAM
    • 0xA000–0xBFFF External RAM
    • 0xC000–0xDFFF Work RAM
    • 0xFE00–0xFE9F OAM (sprites)
    • 0xFF00–0xFF7F I/O registers
    • 0xFF80–0xFFFE High RAM
  • Interrupts: VBlank, LCD STAT, Timer, Serial, Joypad
  • MBC support: ROM-only, MBC1, MBC2, MBC3, MBC5
  • Test ROMs to target: blargg's cpu_instrs, then dmg-acid2 for PPU

Hardware reference — NES

  • Clock: 1.789773 MHz (NTSC) → 29780 CPU cycles per frame at 60.1 fps
  • Screen: 256×240 pixels, 64-color palette (typically 25 on screen at once)
  • CPU: Ricoh 2A03 (6502 without BCD), 8-bit registers (A, X, Y, P, SP), 16-bit PC
  • CPU memory map:
    • 0x0000–0x07FF Internal RAM (mirrored ×4 to 0x1FFF)
    • 0x2000–0x3FFF PPU registers (mirrored)
    • 0x4000–0x401F APU / I/O registers
    • 0x4020–0xFFFF Cartridge space (PRG ROM/RAM)
  • PPU memory map:
    • 0x0000–0x1FFF CHR ROM/RAM (pattern tables)
    • 0x2000–0x2FFF Nametables (mirrored)
    • 0x3F00–0x3FFF Palette RAM
  • APU channels: Pulse 1, Pulse 2, Triangle, Noise, DMC
  • Interrupts: NMI (VBlank), IRQ (APU/mapper), RESET
  • Mappers to support: 0 (NROM), 1 (MMC1), 2 (UxROM), 3 (CNROM)
  • Test ROMs to target: nestest.nes, then blargg's nes_instr_test

Coding rules

  • Rust: no unsafe except where strictly needed for WASM memory
  • TypeScript: strict mode, no any
  • Each hardware component in its own file/struct
  • Prefer accuracy over speed; optimize only after passing test ROMs
  • Each backend (gb/, nes/) is self-contained — no cross-dependencies

When I ask for a specific component, implement it fully with no placeholders.