A Game Boy Color emulator for the TUI
  • TypeScript 100%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Simon Harms 51c70b8e2e Accuracy: MBC2 mapper, ROM+RAM, and DMG wave RAM lock
MBC2 now decodes A8 across 0000-3FFF for RAM enable vs ROM bank,
with 512 x 4-bit RAM. ROM+RAM carts (0x08/0x09) expose RAM from boot.
DMG locks wave RAM while NR30 is on; CGB still allows access.

Co-authored-by: Cursor <[email protected]>
2026-08-31 14:40:03 -04:00
.commandcode Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
bin Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
config Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
src Accuracy: MBC2 mapper, ROM+RAM, and DMG wave RAM lock 2026-08-31 14:40:03 -04:00
test Accuracy: MBC2 mapper, ROM+RAM, and DMG wave RAM lock 2026-08-31 14:40:03 -04:00
.env.example Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
.gitignore Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
mudah.json Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
package-lock.json Improve hardware accuracy: timer edge model, HALT bug, bus blocking, PPU timing and APU 2026-08-31 13:44:49 -04:00
package.json Improve hardware accuracy: timer edge model, HALT bug, bus blocking, PPU timing and APU 2026-08-31 13:44:49 -04:00
README.md Accuracy: MBC3 RTC ticking, save-state load, APU length extra clock 2026-08-31 13:47:37 -04:00
tsconfig.json Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00
vitest.config.ts Add a Game Boy Color emulator with Crystal-safe HDMA timing. 2026-08-31 12:01:49 -04:00

tameuoyiolor

A Game Boy / Game Boy Color emulator that draws in the terminal. The name is a joke on TUI.

Built with @mudah-cli/mudah. Upscale shaders run through @mudah-cli/vgpu.

Run

npm install
node bin/tameuoyiolor.js info path/to/rom.gb
node bin/tameuoyiolor.js run  path/to/rom.gb
node bin/tameuoyiolor.js test path/to/blargg-rom.gb

No ROM is bundled. Point the commands at any .gb or .gbc file.

--graphics=half is the default. It draws unicode half-blocks, which every truecolor terminal can show. Pass --graphics=kitty on Ghostty, Kitty, or WezTerm for pixel images.

node bin/tameuoyiolor.js run rom.gb --shader=lcd
node bin/tameuoyiolor.js run rom.gb --no-gpu          # CPU filters only
node bin/tameuoyiolor.js run rom.gb --no-graphics --max-frames=60

Shaders: nearest (default), lcd, crt, scale2x. Cycle them with 1–4 or [ ]. LCD and CRT need a 2x or larger scale, so they look right on Kitty or a tall terminal.

Keys

Key Action
Arrows / WASD D-pad
Z / X A / B
Enter / Tab Start / Select
Space Pause
. Step one frame while paused
r Reset PC to 0x0100
1–4 / [ ] Shader
F5 Capture a save state (F6 to load)
q / Esc Quit

How pixels reach the terminal

  1. The PPU writes a 160x144 pixel-record framebuffer.
  2. resolveFrame (CPU) or the WGSL effect (GPU) maps records to RGBA.
  3. The selected upscale shader (nearest / LCD grid / CRT / Scale2x) fills a larger buffer.
  4. @mudah-cli/vgpu FramePresenter blits with Kitty graphics, or half-blocks when the terminal cannot place images.

Layout

src/
  core/        emulator (no Mudah imports)
    system.ts  cpu/  mmu/  ppu/  cart/
  render/      palette resolve, pack uniforms, GPU session, presenter
  shaders/     WGSL source as strings
  tui/         alt-screen play loop
  commands/    run, test, info
  providers/   boot wiring

System does not import @mudah-cli/mudah. The CLI is one consumer of the core.

What is still approximate

  • PPU timing is per-line, not per-pixel. Mid-scanline raster tricks will not match hardware.
  • APU: all four channels (pulse x2 with sweep, wave, noise), envelope decay, length counters (including the trigger next-step extra clock), the 512 Hz frame sequencer, and a coupling-capacitor high-pass, streamed through @mudah-cli/audio.
  • Audio backends: native (audify) → spawn (pw-play/paplay/aplay) → silent. --no-audio disables it.
  • Serial is a stub that returns 0xFF after 4096 cycles (enough for Blargg signatures).
  • MBC3 RTC ticks at wall-clock 1 Hz, with latch 0x00 → 0x01 on 0x6000, halt and day-carry.

Test

npm test
npm run typecheck