Use your Commodore 64 Ultimate (or Ultimate 64) from a Linux desktop over the network: live video and audio streamed into an SDL window, your keystrokes typed straight into BASIC, and the Ultimate's menu one keypress away - no second monitor, no capture card, just the Ethernet cable.
./c64uv --host <ultimate-ip>
- Video - the raw VIC stream (PAL 384×272 @ 50 Hz or NTSC 384×240 @ 60 Hz), decoded losslessly and pixel-scaled; latency is roughly one frame on a wired LAN.
- Audio - the SID output as 48 kHz stereo PCM, with an adaptive latency servo that holds the pipeline at ~60 ms.
- Keyboard - what you type lands in the C64's keyboard buffer: full PETSCII mapping, cursor keys, F1–F8, and Esc acting as RUN/STOP.
- Ultimate menu - press F9 to flip to the Ultimate's own menu (file browser, disk mounting, configuration) rendered via its telnet remote screen; F9 again returns to the C64.
- Linux with SDL3 and libcurl (
pacman -S sdl3 curl/apt install libsdl3-dev libcurl4-openssl-dev), C compiler,make. - A C64 Ultimate / Ultimate 64 on your network. The viewer talks to its REST API (HTTP), streaming ports (UDP 11000/11001), DMA socket (TCP 64), and telnet menu (TCP 23).
| Service | Needed for |
|---|---|
| Web Remote Control Service (REST API) | starting/stopping the streams |
| Ultimate DMA Service | keyboard input (port 64) |
| Telnet Remote Menu Service | the F9 menu view (port 23) |
The video/audio streams are generated by the FPGA and only leave through the
Ultimate's Ethernet port. With the machine on WiFi alone, stream start fails
with "No Operational Network Interface". Keyboard, menu, and REST work over
either; plug in a cable for the full experience. (Your PC may be on WiFi,
though wired is smoother - the streams total ~22 Mbit/s.)
make
./c64uv --host 192.168.1.64 # or: export C64U_HOST=192.168.1.64The viewer asks the Ultimate to send its streams to your PC's address on the
Ultimate's subnet (auto-detected, wired interfaces preferred; override with
--dest). It re-issues the start request every 5 s, so streams survive
machine resets - and it pings the Ultimate first, because the firmware
refuses to start a stream toward an address missing from its ARP table.
| Key | Action |
|---|---|
| any typing | typed into the C64 (lowercase = letters, Shift = PETSCII graphics) |
| Esc | RUN/STOP |
| F1–F8, cursors, Home, Del/Ins | the corresponding C64 keys |
| F9 | toggle the Ultimate menu view |
| Ctrl+Q | quit |
The menu view is its own remote session on the firmware side: after you launch something from it (Run Disk, Run Program), the menu stays where it was and the viewer does not switch on its own - press F9 to flip back to the C64 screen and watch it boot.
Useful flags: --no-audio, --no-keyb, --scale N (window size),
--verbose (fps/packet/latency stats), --dump f.ppm (grab one frame
headless), --term-test (print the menu screen as text and exit),
--no-start (listen only; pair with tools/mockstream.py to develop with no
hardware).
- Typed input goes through the KERNAL keyboard buffer, so it works for BASIC,
the READY prompt, and anything reading input normally - not for games
that scan the keyboard matrix directly. Hardware-level injection
(
machine:input) exists in upstream firmware betas and will be adopted once it ships in the official Commodore firmware. - The Ultimate's menu overlay is not part of the VIC stream - that's what the F9 telnet view is for.
- IPv4 only; one viewer per Ultimate (the device streams to one destination).
REST (/v1/streams/*, /v1/machine:*) for control; UDP 11000 carries
4-bit VIC color indices (384 px × 4 lines per 780-byte packet), UDP 11001
carries 192 stereo s16le frames per packet; TCP 64 speaks the firmware's
socket-DMA protocol (KEYB 0xFF03, DMAWRITE 0xFF06); TCP 23 is a VT100
session emulated in src/term.c. Protocol docs:
REST API,
data streams.
MIT for this project's code. src/font8x8.h is public domain (Daniel
Hepper / Marcel Sondaar).