A cycle-accurate Commodore 64 and 1541 disk drive, written from scratch in C11.
This is two complete machines, not one. The C64 (a 6510 CPU, the VIC-II video chip, the SID 6581, two CIA 6526s) and the 1541 drive (a second 6502 with its own ROM, RAM, two VIAs, and a rotating GCR surface) each run in their own clock domain, modelled cycle by cycle. They talk over the real IEC serial bus: three open-collector lines, with the KERNAL and the drive's DOS running the LISTEN/TALK handshake themselves, one bit at a time. That is what makes fastloaders work. No emulator source was used as a reference; see Provenance.
Design notes on how the emulator works and why it makes its choices are in docs/.
make MODE=release # optimised build -> build/c64
./build/c64 --disk games/yourgame.d64 --autorun--autorun waits for the cold boot to reach READY., then types LOAD"*",8,1
and RUN for you, so a mounted disk loads and starts hands-free. Drop it to stop
at the prompt and type them yourself:
LOAD"*",8,1
RUN
The load runs at authentic 1541 speed (slow, by design). Press F10 for warp if you would rather not wait.
You must supply your own ROM images first, see ROM images.
Runs commercial software. The following are verified against real games, not only against unit tests:
| Subsystem | State |
|---|---|
| 6510 CPU | Full documented instruction set plus undocumented opcodes, cycle-accurate, IRQ/NMI/reset. |
| VIC-II | All five graphics modes (standard/multicolour text, standard/multicolour bitmap, ECM), invalid-mode black, sprites with priority and both collision types, badlines, raster interrupts, the six border flip-flop rules (so open-border and FLD tricks behave), XSCROLL/YSCROLL, RSEL/CSEL, idle state, and the CIA2-selected video bank. |
| SID 6581 | Oscillators, all four waveforms with combined-waveform wired-AND, hard sync, ring modulation, the noise LFSR, ADSR with the free-running rate counter (so the ADSR delay bug is reproduced), a multimode filter, and $D418 volume-register sample playback. Anti-aliased and resampled to 44.1 kHz. |
| CIA 6526 (x2) | Cycle-exact timers and interrupts, the 8x8 keyboard matrix, both joystick ports, the RESTORE NMI, TOD clock, serial shift register, and the CIA2 IEC lines. |
| 1541 drive | A full second machine: 2 KB RAM, 16 KB DOS ROM, two 6522 VIAs, its own 1.0 MHz clock domain, and a modelled rotating GCR surface with per-zone bit rates. Read and write: LOAD, SAVE and NEW (format) all work, with changes written back to the .d64 on clean exit. |
| IEC serial bus | The three-wire link (ATN, CLK, DATA) between the C64 (CIA2) and the drive (VIA1), modelled as the real open-collector wired-AND lines. The two machines run the byte-level handshake themselves, each on its own clock, so bus timing and fastloaders behave. |
| Key | Function |
|---|---|
| F8 | Joystick port: swap between port 2 (default) and port 1. Title shows [JOY2] or [JOY1]. Most games use port 2; some read port 1. |
| F9 | Joystick mode: the cursor keys become the joystick; Right Alt or Left Ctrl fire. Title adds CRSR. |
| F10 | Warp (turbo). Runs unthrottled and mutes audio; a stock 1541 load finishes in seconds instead of ~80. |
| F11 | Keyboard layout: symbolic (default, maps by character) or positional (authentic C64 key positions). |
| F12 | Quit. |
C64 keys that are not obvious:
| C64 | Host |
|---|---|
| RUN/STOP | Escape |
| CTRL | Tab |
| Commodore | Left Alt or Left Meta |
| RESTORE | Page Up |
| INS/DEL | Backspace |
| F1 / F3 / F5 / F7 | F1 / F3 / F5 / F7 (add Shift for F2 / F4 / F6 / F8) |
Joystick: a connected game controller, or the numpad (8/2/4/6 for directions,
0 for fire), or F9 plus the cursor keys. Right Ctrl always fires. F9 exists
because most laptops have no numpad. The joystick drives port 2 by default, which
nearly every game uses; some titles read port 1, so press F8 to switch
ports. The active port shows in the title bar.
Game controller setup: a controller is used only if SDL recognises it as a game controller (it is in SDL's controller database). Then the D-pad and left stick move, the A button fires, and it drives the active port (F8 swaps port 2 / port 1).
- Plug the controller in before or during a run. Prefer a pad that presents as a standard USB HID gamepad; that is the mode SDL maps without extra drivers.
- Confirm it is seen. At startup the emulator logs one line per input device with
its name, GUID, and whether SDL treats it as a game controller:
host: joy 0 name="..." guid=03000000.... gamecontroller=yesgamecontroller=yesmeans it will be used.nomeans SDL has no mapping for that GUID, so it is ignored even though it enumerated fine. - If it says
no, supply a mapping. Take the GUID from the log, build an SDL mapping string, and export it before launching, no rebuild needed:SDL reads that variable at init and the device then reportsexport SDL_GAMECONTROLLERCONFIG="03000000....,My Pad,a:b1,b:b0,dpup:h0.1,..."
gamecontroller=yes.
On Linux the controller must first appear under /dev/input as an event device.
Under WSL2 a USB pad reaches Linux only after it is passed through from Windows
(for example with usbipd); until then no input device exists to detect.
Command line:
--disk <path.d64> mount a disk on device 8
--autorun after boot, type LOAD"*",8,1 and RUN by itself (needs --disk)
--headless run with no window
Environment (audio tuning, optional):
C64_AUDIO_BUF <n> SDL audio device buffer in samples (default 2048)
C64_AUDIO_TARGET <n> pacing cushion in samples (default 3528, about 80 ms); raise
on a jittery host to trade latency for fewer audio dropouts
make # development build (-O0 -g)
make MODE=release # optimised build (-O2) <- use this to play
make cleanMODE=dev is the default and is roughly half the speed of the release build. It
is for debugging, not for playing.
Requires a C11 compiler and SDL2. Builds clean under -Wall -Wextra -Werror.
The KERNAL, BASIC and character ROMs are copyrighted, are not included, and
are never committed. Supply your own under rom/:
| File | Size | Maps to |
|---|---|---|
rom/kernal.rom |
8192 | $E000-$FFFF |
rom/basic.rom |
8192 | $A000-$BFFF |
rom/chargen.rom |
4096 | $D000-$DFFF when banked |
rom/1541.rom |
16384 | the drive's DOS (optional; without it the C64 runs alone) |
Any legally obtained copies work. If you own a C64 you can dump your own; the
open-source VICE distribution also ships the standard set
(kernal-901227-03.bin, basic-901226-01.bin, chargen-901225-01.bin), and its
Open ROMs are a copyright-clean alternative that boots but will not run all
software. Without the ROMs the build still succeeds; the binary reports which
files are missing and exits.
If you downloaded a binary release rather than the source, ROMS.md has this same list and the steps to obtain the files. It is the first thing to do before the emulator will run.
make test # ~965 checks, fast unit suites (~2 s)
make test-slow # 69 checks, DOS/serial integration (~80 s)
make test-cpu # Wolfgang Lorenz 6502/6510 conformance (~10 min)- Lorenz suite: 236 tests pass. The run stops at
TRAP16; the remaining tests are 6510 interrupt-sequencing cases that assume a booted KERNAL with a$0314handler installed, which the bare test runner does not provide. That is an environment limit, not a CPU defect. - Boot-render hash: a full KERNAL boot is hashed and pinned, so any change to VIC/CPU/CIA timing that alters a single pixel is caught.
- Per-subsystem unit tests cite their expected values to Christian Bauer's VIC-II documentation, the Lorenz CIA model, or the MOS datasheets.
- Real software: commercial titles load, run, and are bit-identical across repeated runs.
Written from scratch. No code, structure or tables were taken from reSID, VICE or any other emulator. Where a hardware fact was needed it came from the MOS datasheets, Christian Bauer's VIC-II reference, Wolfgang Lorenz's published CIA timing model, or independent reverse-engineering work by others, each cited in the source at the point it is used.
The unstable illegal opcodes (ANE, LXA and the SH group) are analog-unstable on
real silicon; this core uses the standard deterministic model, and
src/cpu6502.c records exactly what the Lorenz suite does and does not pin about
the magic constants.
MIT, see LICENSE. Copyright (c) 2026 Altug Tatlisu.
The licence covers this source code only. It does not cover the C64 ROM images, disk images, or any other software you use with the emulator.