Skip to content
Hardware

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.

commodore-64ioprocessorsmos-technologycommodoretimers1981–present

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), 6526A (NMOS revision with bug fixes), or 8521 (HMOS-II shrink in late C64C boards).
  • 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)
RS-232 TX $DD00 bit 2
Serial bus out $DD00 bits 3-5 (ATN, CLK, DATA)
Serial bus in $DD00 bits 6-7 (CLK, DATA)
User port $DD01 8 lines
NMI source $DD0D flags and mask

$DD0D is CIA2’s Interrupt Control Register. Reading returns the active flags and clears them; writing updates the mask using the set/clear protocol. Five events can trigger an NMI through CIA2: Timer A underflow (bit 0), Timer B underflow (bit 1), TOD alarm match (bit 2), serial shift register byte completion (bit 3), and the /FLAG input pulse (bit 4) — typically wired to the user port.

Keyboard scanning

The keyboard matrix uses CIA1’s ports:

  1. Write to $DC00 to select a column (pull one bit low).
  2. Read $DC01 to see which rows have keys pressed.
  3. 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

  1. Set timer low byte ($DC04) and high byte ($DC05).
  2. Configure control register ($DC0E) for one-shot or continuous.
  3. Enable timer interrupt in ICR ($DC0D).
  4. 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.

See also