CIA: The C64's Swiss Army Chip
Two chips for I/O, timing, and everything else
The MOS 6526 Complex Interface Adapter handled keyboard scanning, joystick input, serial communication, and timing—the unsung heroes of the Commodore 64.
Overview
The Commodore 64 contains two CIA chips: CIA1 ($DC00-$DC0F) handles the keyboard and joysticks, while CIA2 ($DD00-$DD0F) manages serial bus communication, VIC-II bank selection, and the user port. Together they provide the glue logic that connects the C64 to the outside world.
Fast facts
- Model: MOS 6526 (original) or MOS 8521 (later revision).
- Chips per C64: two, at $DC00 and $DD00.
- Features: two 8-bit parallel ports, two 16-bit timers, real-time clock, serial shift register.
- Interrupts: can generate IRQ (CIA1) or NMI (CIA2).
CIA1 ($DC00-$DC0F) - User input
| Register | Address | Purpose |
|---|---|---|
| Port A | $DC00 | Keyboard column / joystick 2 |
| Port B | $DC01 | Keyboard row / joystick 1 |
| DDRA | $DC02 | Data direction for Port A |
| DDRB | $DC03 | Data direction for Port B |
| Timer A | $DC04-$DC05 | 16-bit countdown timer |
| Timer B | $DC06-$DC07 | 16-bit countdown timer |
| TOD | $DC08-$DC0B | Time of day clock |
| Serial | $DC0C | Serial shift register |
| ICR | $DC0D | Interrupt control register |
| CRA | $DC0E | Control register A |
| CRB | $DC0F | Control register B |
CIA2 ($DD00-$DD0F) - System control
| Function | Register | Bits |
|---|---|---|
| VIC bank | $DD00 | bits 0-1 (inverted) |
| Serial bus | $DD00 | bits 3-7 |
| User port | $DD01 | 8 lines |
| NMI source | $DD0D | flags and mask |
Keyboard scanning
The keyboard matrix uses CIA1’s ports:
- Write to $DC00 to select a column (pull one bit low).
- Read $DC01 to see which rows have keys pressed.
- Scan all 8 columns to read the full keyboard.
$DC00 bits: column select (directly affects keyboard matrix lines)
$DC01 bits: row read (directly affected by pressed keys)
Joystick reading
Joysticks share lines with the keyboard:
| Direction | CIA1 Port | Bit |
|---|---|---|
| Joy 1 Up | $DC01 | 0 |
| Joy 1 Down | $DC01 | 1 |
| Joy 1 Left | $DC01 | 2 |
| Joy 1 Right | $DC01 | 3 |
| Joy 1 Fire | $DC01 | 4 |
| Joy 2 Up | $DC00 | 0 |
| Joy 2 Down | $DC00 | 1 |
| Joy 2 Left | $DC00 | 2 |
| Joy 2 Right | $DC00 | 3 |
| Joy 2 Fire | $DC00 | 4 |
Important: bits are active low (0 = pressed, 1 = released).
Timer operations
Each CIA has two 16-bit timers useful for:
- Music playback: trigger IRQs at precise intervals.
- Timing loops: measure elapsed time.
- Raster sync: supplement VIC-II raster interrupts.
- Serial timing: generate baud rates for communication.
Basic timer setup
- Set timer low byte ($DC04) and high byte ($DC05).
- Configure control register ($DC0E) for one-shot or continuous.
- Enable timer interrupt in ICR ($DC0D).
- Timer counts down from loaded value to zero.
VIC-II bank selection
CIA2’s Port A bits 0-1 select which 16KB bank the VIC-II sees:
| $DD00 bits 0-1 | Bank | Address range |
|---|---|---|
| 11 | 0 | $0000-$3FFF |
| 10 | 1 | $4000-$7FFF |
| 01 | 2 | $8000-$BFFF |
| 00 | 3 | $C000-$FFFF |
Note: bits are inverted—lower values select higher addresses.