Skip to main content

A Commodore 64 emulator implemented in Python with a text-based interface.

Project description

C64 Emulator (Python)

A Commodore 64 emulator in Python with Textual, headless, and pygame interfaces. Run BASIC, load PRG/D64 media, attach drives over TCP, and debug with optional Rust-accelerated CPU/VIC paths.

BASIC READY with blinking cursor

Features

  • 6502 CPU with optional Rust fast batch (c64py_rust_core) — see docs/rust_core.md
  • Memory map, VIC-II, SID, CIA1/CIA2, IEC bus
  • Interfaces: Textual TUI (default), headless, pygame (--interface graphics)
  • Media: .prg autoload, .d64 attach (auto-spawn drive 8), .bas via petcat
  • Disk & drives: local auto-spawn, standalone c1541_emulator, remote --tcp-drive — see docs/disk_support.md
  • Remote control: TCP/UDP command server, TCP monitor, host-memory mailbox — see sections below
  • Snapshots, UDP/VICE tracing, TOML config — see linked docs

Quick start

Install

pip install c64py          # PyPI
# or, from a clone:
pip install -r requirements.txt

ROM files are not shipped. Point to a directory with C64 ROMs (or let the emulator offer a local install on first interactive run):

export C64PY_ROM_DIR=~/roms   # optional; auto-detect also checks ./roms, ~/.c64py/roms, VICE paths
c64py --rom-dir ~/roms

Run

c64py                        # Textual UI, BASIC READY
c64py game.prg               # load + RUN after boot
c64py disk.d64               # attach as drive 8 (auto-spawn headless 1541)
c64py --interface graphics   # pygame window
c64py --interface headless --max-cycles 3000000 --no-colors --no-config

Positional FILE behavior:

Extension Action
.prg Load at $0801 and inject RUN after BASIC boot
.d64 Auto-spawn drive 8 with the image (unless --tcp-drive is set)
.bas Convert with VICE petcat, then load like a PRG

Common flags

Flag Notes
--rom-dir DIR KERNAL/BASIC/CHAR (and optional DOS) ROM directory
--interface {textual,headless,graphics,...} UI mode (default: textual)
--max-cycles N Stop after N CPU cycles (implies --autoquit; use --no-autoquit to keep running)
--turbo No wall-clock throttling
--no-config Ignore c64py.toml (useful for CI/repro runs)
--vic-emulation {fast,accurate-python,accurate-rust} VIC timing tier — docs/emulation_modes.md
--video-rendering {per-frame,per-raster,per-cycle} Host pixel sampling — docs/per_cycle_vic.md
--audio-emulation {resid,python-sid,disabled} SID backend
--tcp-port / --udp-port Control server (see below)
--monitor-port TCP monitor for stepping — docs/DEBUGGING.md
--host-command-ctrl TX=…,RX=… Guest RAM mailbox — docs/host_memory_command_channel.md

Headless smoke test (no Rust core required):

C64PY_USE_RUST_FAST=0 c64py --no-config --no-colors --interface headless \
  --max-cycles 3000000 --vic-emulation fast

Demos

Captured straight from the emulator with the local capture tool (cycle-accurate accurate-rust VIC + per-cycle compositing, real SID audio in the MP4s). The clips below are short GIF previews — click through for the full-length video with sound.

Demo Preview Full video (with SID audio)
Bruce Lee — title/menu Bruce Lee menu brucelee_01_menu.mp4
Bruce Lee — gameplay (joystick) Bruce Lee gameplay brucelee_03_playing.mp4
Swinth — first song/animation Swinth first swinth_02_first_song.mp4
Swinth — second song/animation Swinth second swinth_04_second_song.mp4

GIFs are silent, downscaled previews. The linked MP4s are full resolution and include the SID soundtrack.

Want to capture your own? See How to capture screenshots & videos.

Configuration

Optional TOML (c64py.toml in cwd → ~/.c64py.toml → XDG config). CLI flags override config.

c64py --write-config    # emit a populated default

See docs/config.md for the full schema.

Video & VIC

Host rendering (--video-rendering) is separate from VIC CPU timing (--vic-emulation).

Per-scanline splits: --video-rendering per-raster with --vic-emulation accurate-python or accurate-rust. Sub-row effects need --video-rendering per-cycle.

Disk & drive emulation

Topic Doc
D64 attach, tiers, KERNAL hooks docs/disk_support.md
Standalone c1541_emulator process docs/drive_emulator.md
Remote drive over TCP (--tcp-drive) docs/tcp_hardware_drive.md
IEC bus status docs/iec_bus.md

Quick examples:

# Local auto-spawn (pass .d64 as positional)
c64py mydisk.d64

# Standalone drive server + C64 client
python -m c64py.drives.c1541_emulator --disk game.d64 --device 8 --port 6408
c64py --tcp-drive 8:localhost:6408 game.prg

Remote control (three channels)

These are different surfaces — do not confuse the TCP control port with the monitor or the guest mailbox.

1. Control server (--tcp-port / --udp-port)

Line-oriented emulator control on localhost. Same grammar as HELP in command_dispatch.py. Examples:

c64py --tcp-port 6464 --interface headless --max-cycles 10000000
# echo STATUS | nc localhost 6464

STATUS, MEMORY, WRITE, DUMP, SCREEN, LOAD, ATTACH-DISK, DETACH-DISKS, SEND_KEY, INJECT, STOP, QUIT, …

INJECT injects keys with friendly syntax and a real-hardware option:

# echo 'INJECT {F1}'            | nc localhost 6464   # single key -> CIA1 matrix (real press)
# echo 'INJECT RUN{return}'     | nc localhost 6464   # multiple keys -> KERNAL buffer
# echo 'INJECT MATRIX {space}'  | nc localhost 6464   # force matrix (one keystroke)
# echo 'INJECT BUFFER HELLO'    | nc localhost 6464   # force KERNAL buffer
# echo 'INJECT JOY 2:up+fire:300ms' | nc localhost 6464   # hold joystick port 2

Auto mode picks matrix for a single keystroke (so games scanning $DC00/$DC01 directly — including F-keys like {F1} — see a real press) and the KERNAL buffer for multi-key strings. --inject-keys follows the same rule. SEND_KEY/SEND_KEYS remain buffer-only (raw PETSCII codes).

2. Monitor server (--monitor-port)

Lightweight CPU debugger (step, breakpoints, memory dump) — not VICE protocol. See docs/DEBUGGING.md §6.

c64py --monitor-port 6510 --interface headless

3. Host-memory command channel (--host-command-ctrl)

Guest program pokes requests into C64 RAM mailboxes; host polls and dispatches the same command grammar as the TCP server. Off by default. See docs/host_memory_command_channel.md.

Textual interface

Default UI when not using --interface headless|graphics and not relying solely on server ports.

  • C64 panel: colored 40×25 screen
  • Debug log and status bar
  • Shortcuts: Ctrl+X quit · F10 turbo · F11 layout · F12 save text snapshot to snapshots/textual_screen_*.txt

Textual UI overview

Architecture

Component Location
Entry point / CLI C64.py
Emulator loop, IEC, KERNAL hooks emulator.py
6502 CPU cpu.py (+ optional c64py_rust_core)
Memory, VIC snapshots memory.py
pygame presenter graphics.py, presenter.py
Textual UI ui.py
Control server server.py, command_dispatch.py
Monitor TCP monitor_tcp.py
Drive stack drives/drive.py, c1541_emulator.py, tcp_drive_client.py, …
Snapshots snapshot.pydocs/snapshots.md

Documentation index

Doc Contents
docs/config.md TOML config schema
docs/emulation_modes.md VIC & audio tiers
docs/per_cycle_vic.md Per-cycle / per-raster rendering
docs/DEBUGGING.md Traces, monitor, repro matrix
docs/rust_core.md Optional Rust extension
docs/disk_support.md D64, tiers, KERNAL LOAD path
docs/drive_emulator.md Standalone 1541 server
docs/tcp_hardware_drive.md TCP-attached drives
docs/iec_bus.md IEC bus implementation status
docs/host_memory_command_channel.md Guest→host mailbox
docs/snapshots.md Save/load emulator state
docs/performance.md Benchmarks & profiling
docs/input_ux.md Keyboard/joystick UX
docs/capturing_screenshots.md Capture screenshots/videos (dev tooling)
AGENTS.md Agent/CI notes

Requirements

  • Python 3.8+
  • Runtime: requirements.txt
  • README screenshot tooling only: requirements-docs.txt

License

BSD 3-Clause — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

c64py-1.0.1.tar.gz (288.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

c64py-1.0.1-py3-none-any.whl (243.9 kB view details)

Uploaded Python 3

File details

Details for the file c64py-1.0.1.tar.gz.

File metadata

  • Download URL: c64py-1.0.1.tar.gz
  • Upload date:
  • Size: 288.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for c64py-1.0.1.tar.gz
Algorithm Hash digest
SHA256 2ce701b4bfc2a1fd1b192cd0b6d45164cef16b65d44ec37121626b3594e1c0ab
MD5 e03c3ac50867cf19cde0cc3d3d6127a3
BLAKE2b-256 4e878fe928fcd2f93fc5987187092d3dfbbc15bcdbbf9a56c1a63d86fd8ca1e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for c64py-1.0.1.tar.gz:

Publisher: publish.yml on cyberplant/c64py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c64py-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: c64py-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 243.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for c64py-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1f8c9e757bdaa9b4b63b9cf5bcb5e4c381afcaf06b171739d3b376b4d95c484b
MD5 96b32c10b5ad416be924dfda38f5db9f
BLAKE2b-256 b45a198d376cf4384d85f35de69b4fa74c050edfec2e1d4cc59aa6a438a14b7b

See more details on using hashes here.

Provenance

The following attestation bundles were made for c64py-1.0.1-py3-none-any.whl:

Publisher: publish.yml on cyberplant/c64py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page