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. If the stream stops (machine switched off, cable out) the window says so and picks the picture up again by itself when it returns.
- 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.
- Discovery - run it with no address and it finds the Ultimate on your LAN (preferring its wired interface, the one that can stream).
- Run files - drop a
.prg,.crt, or.sidonto the window (or--run file) and the machine runs it, cartridge-safe. - Machine control - reset, reboot, pause/resume, and menu button as
hotkeys or one-shot
--docommands; F10 shows the in-window key reference. - Shareable streams -
--multicastlets any number of viewers watch the same machine.
- 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, port 80) | discovery, starting/stopping the streams, the Ctrl+R/P/M machine controls and --do, running dropped .prg/.crt/.sid files, mounting .g64/.d71/.g71/.d81 images, cartridge parking, and matrix-level typing on firmware that has machine:input |
| Ultimate DMA Service (port 64) | typing into the C64 (KERNAL buffer), RUN/STOP, and mount-and-run of a dropped .d64 |
| Telnet Remote Menu Service (port 23) | the F9 menu view |
Everything else the viewer does needs no service: the video/audio streams arrive on UDP 11000/11001 once started. The REST API alone gets you a picture and sound; add the DMA service to type, and telnet to browse the Ultimate's menu from the viewer.
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.64
./c64uv # no address? it scans your LAN for oneWith no --host (and no C64U_HOST) the viewer sweeps your local /24
subnets with one /v1/info request per address and uses the Ultimate it
finds; --discover runs the same scan, lists every Ultimate on the
network, and exits.
The 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 |
| F10 | in-window key reference (same table as --help) |
| Ctrl+R / Ctrl+Shift+R | reset / reboot the machine |
| Ctrl+P | pause / resume the machine |
| Ctrl+M | press the Ultimate's menu button (the on-screen menu then answers the physical keyboard only; use F9 to drive a menu from the viewer) |
| Ctrl+Q | quit |
Drop a .prg, .crt, .sid, or .d64 file onto the window and the
Ultimate runs it (DMA program run, cartridge run, SID player, or for a
.d64: mount on drive A, reset, LOAD"*",8,1 and RUN, all done by the
firmware over the DMA socket). If a cartridge is configured on the machine,
it is parked for the run and restored afterwards, so a freezer cart won't
hijack the program's reset; the cart is back on your next manual reset. The
same works headless: c64uv --run game.prg.
The window title shows the transfer progress ("sending game.d64 42%"). The Ultimate serves one DMA-socket client at a time, so the viewer drops its keyboard connection for the duration and reconnects it on your next keypress; a second drop while one is in flight is refused.
Other disk images (.g64, .d71, .g71, .d81) are mounted on drive A
without touching the machine; type LOAD"*",8,1 yourself. Every image is
copied to the Ultimate's temp area first, so writes never reach the file you
dropped.
The same machine controls work headless: c64uv --do reset (also reboot,
pause, resume, menu, poweroff) issues one REST call and exits. If
your Ultimate has a network password set (firmware 3.12+), pass it with
--password or the C64U_PASSWORD environment variable; it is sent as the
X-Password header on every request, discovery included.
The Ultimate streams to a single destination, so normally the first viewer
wins. --multicast asks it to stream to multicast groups instead
(239.0.1.64 video, 239.0.1.65 audio): every viewer started with
--multicast joins the groups and shows the same machine, and c64uv can
share the stream with other multicast-aware tools watching the same groups.
A custom group works too: --dest 239.x.y.z joins that group for video and
group + 1 for audio. When one multicast viewer quits it stops the stream,
and the remaining viewers' keepalive restarts it within 5 s (a short blip).
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),
--discover (list Ultimates on the network and exit), --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).
make install (respects DESTDIR and PREFIX, default /usr) installs the
binary, a desktop entry, and the icon. Arch users can build a package from
packaging/aur/PKGBUILD; prebuilt static binaries are on the
releases page. Set C64U_HOST
in your environment if you launch it from the desktop menu rather than a
terminal.
- On current official firmware, typed input goes through the KERNAL keyboard
buffer: it works for BASIC, the READY prompt, and anything reading input
normally - not for games that scan the keyboard matrix directly. The
viewer probes for the hardware-level
machine:inputAPI (in upstream firmware betas) on every start and switches to true matrix-level press/release automatically once your firmware has it; games, chords, and held keys then work, with Tab acting as the C64 CTRL key, PageUp as RESTORE, Insert as £ and End as + (both also on the numeric keypad; a PC has no unshifted key for either). - The Ultimate's menu overlay is not part of the VIC stream - that's what the
F9 telnet view is for. The on-screen menu (Ctrl+M) cannot be driven from
the viewer on current firmware: every remote keystroke is a DMA write into
C64 memory, and the firmware closes its menu before any DMA write, so the
first key you press kicks the menu out. Newer firmware with the
machine:inputAPI routes keys into the menu; the viewer switches to it automatically once your machine has it. - IPv4 only. The device streams to one destination, so it's one viewer per
Ultimate unless everyone uses
--multicast.
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).