Skip to content

The console

Three numbers matter to a game more than any other on this page: the composition budget per scanline (a line too busy shows stale, and the budget is per line, not per screen), the display list of 256 entries per frame, and the 320 KiB pool of resident art. All the limits a game lives with are gathered in Constants and limits.

640 × 480 at 60 Hz, 8-bit palettised, 3 000 opaque sprite-pixels per scanline measured, across three genlocked RP2350B running at 252 MHz.

Video

Resolution 640 × 480 at 60 Hz (4:3), 18.4 Mpx/s active
Output HDMI / DVI
Depth 8 bits per pixel, palettised: index → blend table → RGB565 → RGB888 on the wire
Palette one palette of 256 indices, no banks. Default partition — a per-game setting of the bundle, not wiring: index 0 reserved (transparent), 190 opaque colours (1-190), one system ink (191, the white of the built-in 8×8 arcade font) and 64 effect rows (192-255) ; a game can trade effect rows for opaque colours
Blend table 65 536 × RGB565, 128 KB, rebuilt on the chip from the palette: one lookup per pixel resolves transparency, opacity, translucency, additive, shadow and front/behind priority, no branch
Sprites 3 000 opaque sprite-pixels per scanline measured on the board, about 2 200 blended, roughly 90 opaque sprites of 32 px width per line
Sizes arbitrary and animated; no fixed tile grid
Resident art a 320 KiB pool in RP2's SRAM (450 KB target), owned by RP1: 64-byte pages, bitmaps streamed from RP2's NOR flash on demand by load orders carried in the display list packet, no tearing by a three-frame quarantine
Display list 256 entries of 16 bytes per frame, sent by RP1 over SPI at VSYNC with the palette, the background camera and tile-map writes, and up to 16 load orders (6 928-byte packet, 2.22 ms)
Overlay bus 16-bit at 42 MHz between the sprite engine and the mixer; 484 lines per frame: 480 pixel lines plus four control blocks carrying the palette and camera, the blend modes, the tile-map writes, and one reserved
Planes sprite overlay over background, front/behind priority per pixel
Background 8 × 8 tile map scrolled by the mixer from its NOR flash, or a 128 × 64 toroidal map owned by the game: the background camera and tile-map writes are driven by the game and travel with the display list, a text mode at no display-list cost
Latency two frames plus scan-out, deterministic; no tearing by construction

For comparison, a Neo Geo manages 1 536 sprite-pixels per line, about half.

Compute

Processors 3 × RP2350B, dual Cortex-M33 each, six cores total
Clock 252 MHz on all three
Synchronisation genlock from a shared 12 MHz MEMS oscillator on the XIN pins
SRAM 520 KB per chip, 1.5 MB total

Storage

NOR flash 3 × 16 MB, one per chip on its dedicated XIP port: sprite atlas, background, code
Measured throughput 33 MB/s, i.e. 550 KB reloadable per frame
Removable micro-SD card on RP1 (FAT32), with card detect: games live in papydeck/games/<name>/, one folder each, hot-plugged

Launching a game compiles its art bundle into RP2's and RP3's NOR flash: a few seconds the first time, about two seconds from the card's cache, and nothing at all once the NOR flash already holds it. This is a cartridge model, not a loading-screen model.

Audio

Designed, not implemented yet: the mixer runs in DVI mode for now.

Digital HDMI data islands generated by RP3, no extra components
Analogue I2S DAC on RP1: 4-pole jack and a 2.5 W class-D amplifier into 4 Ω
Sample rate 48 kHz, from an exact PIO divider off 252 MHz

Both paths exist because many PC monitors have neither speakers nor a headphone output.

Connectivity

Wireless Wi-Fi (station, network chosen and remembered on the console; HTTPS to the project's server), Bluetooth Classic (BR/EDR) and BLE
Gamepads DualShock 4, DualSense, Switch Pro, 8BitDo… through Bluepad32
Wired USB-C 2.0 on RP1, the only chip carrying USB: console, firmware updates of all four chips
Debug three SWD connectors, one per chip

Bluetooth Classic is not a bonus feature: it is what living-room gamepads speak. BLE-only modules will not do.

The board

Dimensions

60 × 60 mm, four screw holes.

Connectors

HDMI type A, USB-C 2.0, micro-SD with card detect, 4-pole audio jack with switch, speaker connector, gamepads/keyboard/mouse on Bluetooth.

One user button.

Power

5 V from the USB-C input.

USB power alone is not enough for the nominal case

A USB port advertises 500 mA by default, and the board negotiates nothing on its CC lines. Either the firmware measures those lines to find out what is available, or power comes from elsewhere.

Status

Running on the V1 alpha board: genlocked HDMI output; the sprite engine with its pool owned by RP1 and its opaque intervals (3 000 sprite-pixels per line measured); the tile background, a world scrolled from the mixer's NOR flash, or a local text mode driven by the game; games from the micro-SD card, each with its art bundle compiled on the console and installed in the NOR flashes (cached on the card); a game engine (fixed-point actors, collision mask, animations) shared to the byte with the browser simulator; Bluetooth gamepads with pairing from the console's menu, rumble and player LEDs; Wi-Fi with an on-screen keyboard; the published games listed and downloaded onto the card over HTTPS. Watchdogs on the three chips and a universal exit (B or the console button held 3 s).

Still open: audio, pool compaction, an identity for the console (an ATECC608 on the V1 board, signing towards the server), scores sent to the leaderboard.