Sub-pages
$BFE001

CIA Overview

The Amiga uses two MOS 8520 Complex Interface Adapters (CIAs) for peripheral I/O that does not require the speed of custom chip DMA. The 8520 is a derivative of the well-known 6526 used in the Commodore 64, differing only in registers 8–11 (the event counter replaces the 6526's TOD clock).

Each 8520 provides:

  • Two 8-bit programmable parallel ports (PA and PB)
  • Two 16-bit countdown timers (Timer A and Timer B)
  • A 24-bit event counter with alarm function
  • A bidirectional serial shift register
  • An interrupt control register (ICR) with 5 interrupt sources

The 8520 was designed for 8-bit 6502-family processors and communicates with the 68000 synchronously, clocked by the 68000's E clock at approximately 716 kHz.

The Two CIAs

CIA Base Addresses
$BFE001CIA-A — connected to data bus D0–D7 (odd addresses, 256-byte spacing)
$BFD000CIA-B — connected to data bus D8–D15 (even addresses, 256-byte spacing)

The address $BFE001 — the base of CIA-A — is where this site gets its name. It is the first address the system reads to check the keyboard, disk status, joystick fire buttons, and the power LED state. For any Amiga programmer, this address is instantly recognizable.

CIA-A sits on the lower byte of the data bus (D0–D7), so all its registers appear at odd addresses. CIA-B sits on the upper byte (D8–D15), so its registers appear at even addresses. The chip select signals are derived from address lines A12 (CIA-A, active low) and A13 (CIA-B, active low), with registers spaced 256 bytes apart ($100) using address lines A8–A11 as the internal register select (A0–A3 on the chip).

Interrupt Routing

  • CIA-A IRQ connects to PAULA's INT2 (PORTS, level 2 interrupt)
  • CIA-B IRQ connects to PAULA's INT6 (EXTER, level 6 interrupt)
$BFE001

Register Map

Each CIA has 16 registers (register 11/$B is unused). Due to the 256-byte spacing, the full register maps span a significant address range.

CIA-A Register Addresses
$BFE001PRA — Port A data register
$BFE101PRB — Port B data register
$BFE201DDRA — Port A data direction register
$BFE301DDRB — Port B data direction register
$BFE401TALO — Timer A low byte
$BFE501TAHI — Timer A high byte
$BFE601TBLO — Timer B low byte
$BFE701TBHI — Timer B high byte
$BFE801EVENT LO — Event counter bits 0–7
$BFE901EVENT MID — Event counter bits 8–15
$BFEA01EVENT HI — Event counter bits 16–23
$BFEB01— (unused)
$BFEC01SP — Serial data register (keyboard)
$BFED01ICR — Interrupt control register
$BFEE01CRA — Control register A
$BFEF01CRB — Control register B
CIA-B Register Addresses
$BFD000PRA — Port A data register
$BFD100PRB — Port B data register
$BFD200DDRA — Port A data direction register
$BFD300DDRB — Port B data direction register
$BFD400TALO — Timer A low byte
$BFD500TAHI — Timer A high byte
$BFD600TBLO — Timer B low byte
$BFD700TBHI — Timer B high byte
$BFD800EVENT LO — Event counter bits 0–7
$BFD900EVENT MID — Event counter bits 8–15
$BFDA00EVENT HI — Event counter bits 16–23
$BFDB00— (unused)
$BFDC00SP — Serial data register
$BFDD00ICR — Interrupt control register
$BFDE00CRA — Control register A
$BFDF00CRB — Control register B
$BFE001

Parallel Ports

Each CIA has two 8-bit bidirectional parallel ports, PA and PB. The direction of each individual bit is controlled by the corresponding Data Direction Register (DDR): a 0 bit configures the pin as input, a 1 bit configures it as output. Reading a port register always returns the actual state of the pins, regardless of direction setting.

CIA-A Port A ($BFE001)

Bit Dir Signal Function
7IN/FIR1Joystick port 1, fire button (active low)
6IN/FIR0Joystick port 0, fire button (active low)
5IN/RDYDisk drive ready (active low)
4IN/TK0Disk head at track 0 (active low)
3IN/WPRODisk write-protected (active low)
2IN/CHNGDisk changed (active low)
1OUT/LEDPower LED control (0 = LED on)
0OUT/OVLMemory overlay (ROM at $000000 when set)

The DDRA default is $03 (bits 0–1 output, bits 2–7 input). The /OVL bit controls whether Kickstart ROM is mapped at address $000000 (overlay active) or not, critical during the boot sequence. The /LED bit directly controls the power LED — the familiar disk activity blink is simply software toggling this bit.

CIA-A Port B ($BFE101)

Port B of CIA-A is connected to the parallel (Centronics) port. All 8 bits carry data for printer communication. The handshaking signals PC (/DRDY — data ready) and FLAG (/ACK — acknowledge) coordinate the transfer.

CIA-B Port A ($BFD000)

Bit Dir Signal Function
7OUT/DTRSerial port DTR output
6OUT/RTSSerial port RTS output
5IN/CDSerial port carrier detect
4IN/CTSSerial port clear to send
3IN/DSRSerial port data set ready
2OUTSELParallel port SELECT
1OUTPOUTParallel port paper out
0INBUSYParallel port busy

DDRA default: $C0 (bits 6–7 output, rest input).

CIA-B Port B ($BFD100)

Bit Dir Signal Function
7OUT/MTRDisk drive motor control
6OUT/SEL3Select drive DF3:
5OUT/SEL2Select drive DF2:
4OUT/SEL1Select drive DF1:
3OUT/SEL0Select internal drive DF0:
2OUT/SIDEDisk side select (0 = upper, 1 = lower)
1OUTDIRDisk head step direction (0 = outward, 1 = inward)
0OUT/STEPDisk head step pulse (active low)

DDRB default: $FF (all outputs). This port controls the entire floppy disk drive mechanism.

Handshaking (PC and FLAG)

Data transfers on port B can be coordinated using two handshaking signals. The PC (port control) pin goes low on the third clock cycle after any access to port B, signaling that data has been written. The FLAG pin triggers when its input transitions from high to low, setting the FLAG bit in the ICR. Connecting PC of one CIA to FLAG of another creates an efficient hardware handshake.

On CIA-A: SP = KDAT (keyboard serial data), CNT = KCLK (keyboard clock), PC = /DRDY, FLAG = /ACK. On CIA-B: FLAG = /INDEX (disk index pulse), PC is unused.

$BFE001

Timers

Each CIA contains two independent 16-bit countdown timers. A timer counts down from a preset value to zero, at which point it sets a flag in the ICR and optionally generates an interrupt.

Timer Registers

Each timer uses two 8-bit registers. When read, they return the current counter value. When written, the value is stored in a latch (prescaler) — it is not loaded into the counter immediately.

Timer Register Addresses (CIA-A)
$BFE401TALO — Timer A, low byte (read: counter; write: latch)
$BFE501TAHI — Timer A, high byte (read: counter; write: latch)
$BFE601TBLO — Timer B, low byte (read: counter; write: latch)
$BFE701TBHI — Timer B, high byte (read: counter; write: latch)

Reading the Counter Safely

Because the timer decrements asynchronously, reading the high and low bytes separately can produce a race condition. For example, if the counter is at $0100, reading the high byte gives $01, but if the counter decrements before the low byte is read, the low byte returns $FF — yielding an incorrect value of $01FF. The safe method: read high, read low, read high again. If the high byte changed, repeat the process.

Clock Sources

Timer A has two input modes controlled by the INMODE bit in CRA:

  • INMODE = 0: decrement on every clock cycle (~716 kHz from the E clock)
  • INMODE = 1: decrement on each positive edge of the CNT signal

Timer B has four input modes controlled by bits 6–5 of CRB:

  • 00: clock cycle (~716 kHz)
  • 01: CNT signal positive edges
  • 10: Timer A underflow (chains A+B into a 32-bit timer)
  • 11: Timer A underflow gated by CNT high (measures CNT pulse duration)

Run Modes

The RUNMODE bit selects between:

  • Continuous mode (RUNMODE = 0): after reaching zero, the counter reloads from the latch and continues counting
  • One-shot mode (RUNMODE = 1): after reaching zero, the counter stops and the START bit is cleared

Loading the Counter

The latch value is transferred into the counter in three ways:

  1. Force load: setting the LOAD bit (strobe) in the control register immediately loads the latch value
  2. Underflow: the latch is automatically reloaded when the counter reaches zero
  3. Auto-load on stop: writing to the timer high byte while the timer is stopped triggers an automatic load

When writing timer values, always write the high byte first, then the low byte, to avoid triggering unintended auto-loads.

Timer Output to Port B

Each timer can optionally output to a port B pin: Timer A to PB6, Timer B to PB7. This is enabled by the PBON bit in the control register. The OUTMODE bit selects:

  • Pulse mode (OUTMODE = 0): a one-cycle positive pulse on each underflow
  • Toggle mode (OUTMODE = 1): the output toggles on each underflow
$BFE001

Control Registers

Control Register A (CRA, register $E)

Bit Name Values Function
7TODIN0=60Hz, 1=50HzTOD input frequency (event counter clock select)
6SPMODE0=input, 1=outputSerial port direction
5INMODE0=clock, 1=CNTTimer A clock source
4LOADstrobeForce load latch into counter (write 1 to trigger)
3RUNMODE0=continuous, 1=one-shotTimer A run mode
2OUTMODE0=pulse, 1=toggleTimer A output waveform on PB6
1PBON0=off, 1=onEnable Timer A output on PB6
0START0=stop, 1=startStart/stop Timer A

Control Register B (CRB, register $F)

Bit Name Values Function
7ALARM0=TOD write, 1=alarm writeEvent counter access mode: write counter or alarm
6–5INMODE00/01/10/11Timer B clock source (see above)
4LOADstrobeForce load latch into counter (write 1 to trigger)
3RUNMODE0=continuous, 1=one-shotTimer B run mode
2OUTMODE0=pulse, 1=toggleTimer B output waveform on PB7
1PBON0=off, 1=onEnable Timer B output on PB7
0START0=stop, 1=startStart/stop Timer B

Timer Usage on the Amiga

  • CIA-A Timer A is used continuously by the OS for keyboard communication
  • CIA-A Timer B is available for general use
  • CIA-B Timer A is used for serial port baud rate generation
  • CIA-B Timer B is used by the Blitter in synchronous mode
$BFE001

Event Counter & Serial Port

Event Counter (Registers $8–$A)

The 8520 replaces the 6526's TOD (Time of Day) clock with a simple 24-bit event counter. It counts from 0 to 16,777,215 ($FFFFFF), incrementing on each positive edge of the TOD input signal. After reaching $FFFFFF, it wraps to 0.

On the Amiga: - CIA-A event counter receives 50 Hz pulses from the power supply (mains frequency), functioning as a simple elapsed-time counter. - CIA-B event counter receives horizontal sync pulses at 15,625 Hz, providing a high-resolution timing source.

Reading and Writing

The counter latches its value when the MSB (register $A) is read. Subsequent reads of registers $9 and $8 return the latched value while the internal counter continues running. The latch is released when the LSB (register $8) is read, ready for the next snapshot.

When writing, the counter stops after writing the MSB. It resumes when the LSB is written. Always write MSB first, then mid byte, then LSB.

Alarm Function

Setting bit 7 (ALARM) in CRB causes writes to registers $8–$A to set the alarm value instead of the counter value. When the counter reaches the alarm value, the Alarm bit in the ICR is set and an interrupt can be generated. Reading registers $8–$A always returns the current counter value, regardless of the ALARM bit.

Serial Shift Register (Register $C)

The serial port consists of a data register (SDR) and an internal shift register. The direction is controlled by the SPMODE bit in CRA:

Input mode (SPMODE = 0): data on the SP pin is shifted in on each positive edge of the CNT signal. After 8 bits, the shift register transfers to SDR and the SP interrupt flag is set.

Output mode (SPMODE = 1): Timer A controls the baud rate. Data written to SDR is transferred to the shift register and clocked out on SP, MSB first, at half the Timer A frequency. The CNT pin outputs the clock signal. Maximum output rate is one quarter of the 8520's clock frequency.

On CIA-A, SP carries keyboard serial data (KDAT) and CNT carries the keyboard clock (KCLK).

$BFE001

Interrupt Control Register

The ICR (register $D) manages five interrupt sources through a dual-register structure: a data register (read) and a mask register (write), both accessed at the same address.

Reading the ICR (Data Register)

Bit Name Source Function
7IRInterrupt occurred (any enabled source fired)
6–5Always 0 (unused)
4FLAGFLAG pinNegative edge detected on FLAG input
3SPSerialShift register full (input) or empty (output)
2ALARMCounterEvent counter matched alarm value
1TBTimer BTimer B underflow (reached zero)
0TATimer ATimer A underflow (reached zero)

Reading the ICR returns the data register and clears all bits, including IR. If you need the value later, save it to RAM immediately after reading.

Writing the ICR (Mask Register)

Bit Name Values Function
7S/C0=clear, 1=setSet/Clear control for mask bits
6–5Unused
4FLAGmaskEnable/disable FLAG interrupt
3SPmaskEnable/disable serial port interrupt
2ALARMmaskEnable/disable alarm interrupt
1TBmaskEnable/disable Timer B interrupt
0TAmaskEnable/disable Timer A interrupt

The mask register uses the same SET/CLR mechanism as DMACON. When S/C = 1, bits set in the written value are enabled in the mask. When S/C = 0, they are disabled. Bits written as 0 are never affected.

An interrupt is generated (IRQ pin goes low) only when a data register bit is set and the corresponding mask bit is enabled. When the ICR is read (clearing the data register), the IRQ line returns high.

    ; Enable Timer A interrupt on CIA-A:
    MOVE.B #$81,$BFED01    ; S/C=1, TA=1 → enable TA in mask

    ; Disable both timer interrupts:
    MOVE.B #$03,$BFED01    ; S/C=0, TA=1, TB=1 → clear TA and TB in mask

CIA Interrupt Flow

When a CIA interrupt fires, the following sequence occurs:

  1. A source sets its bit in the CIA's ICR data register
  2. If the corresponding mask bit is set, the IR bit (bit 7) is set and IRQ goes low
  3. PAULA detects the IRQ and sets the corresponding INTREQ bit (bit 3 for CIA-A, bit 13 for CIA-B)
  4. If INTENA allows it, the 68000 receives the interrupt
  5. The handler reads the CIA's ICR to identify the source (this clears the CIA-side flags)
  6. The handler clears the INTREQ bit in PAULA