Chipset Architecture
The Amiga's power came from its custom chipset — three specialized chips that handled graphics, sound, and DMA independently of the CPU. While the Motorola 68000 ran your code, the custom chips ran the machine.
RAM
Agnus (later Fat Agnus, then Alice) was the brain of the custom chipset. It controlled DMA — managing memory access for all the other chips so they could read data without bothering the CPU. It contained the Copper (a programmable co-processor that modified hardware registers in sync with the video beam) and the Blitter (a hardware block-transfer engine for fast memory copies, line drawing, and fill operations).
Denise (later Lisa in the AGA chipset) handled all video output: bitplane graphics, hardware sprites, collision detection, and the color palette. The Amiga used a planar graphics model — instead of storing pixels as packed values, each bit of a pixel's color came from a separate bitplane. This made scrolling and certain effects extremely efficient.
Paula handled four independent 8-bit audio channels with hardware volume and period control, disk drive access, serial I/O, and interrupt management. MOD music worked by having Paula's DMA channels continuously read waveform samples from chip RAM while the CPU did other work — the music literally played itself.
CIA 8520 (×2) — Complex Interface Adapters. CIA-A at $BFE001 handled
the keyboard, game port fire buttons, the power LED, and the audio low-pass
filter. CIA-B at $BFD000 handled the parallel port, serial control
lines, and disk drive signals. Each CIA contained two 16-bit timers and
a 24-bit time-of-day counter.
Memory Map
The 68000's 24-bit address bus gave 16 MB of address space. Every piece of hardware had its place. Writing to a memory address could store data, draw graphics, play a sound, or toggle the power LED — depending on where in the map you were writing.
At power-on, the overlay bit in CIA-A maps the ROM at address $000000 so
the CPU can read its reset vectors. One of the first things Kickstart does
is clear that bit, swapping chip RAM into $000000 and moving the ROM back
to $F80000. The jump-table architecture of exec.library meant you could
call OS functions at fixed negative offsets from the library base — a
mechanism I spent many evenings exploring.
CIA-A Registers
The register this site is named after. CIA-A sits at odd addresses starting
from $BFE001 (the two CIAs use odd/even address decoding to share the
same region without conflicts).
Bit 1 is the star. Setting it low turns on the power LED and engages the audio low-pass filter. On the Amiga 500, it was a hard on/off. On the Amiga 2000 and later, the LED dimmed instead — and the filter smoothly attenuated frequencies above ~3.3 kHz. Games toggled it to simulate effects like driving through a tunnel: the muffled audio came from the hardware filter, not from software processing.
The instruction MOVE.b #$02,$BFE001 sets bit 1 and clears everything
else — turning the LED off and disabling the filter. To toggle just the
LED without affecting other bits, you'd read the register, XOR bit 1,
and write it back: BCHG #1,$BFE001.
The keyboard was read through CIA-A's serial port mechanism. Each keypress generated an 8-bit scan code sent serially via the KDAT line, clocked by KCLK. The CIA shifted in the bits and triggered an interrupt when a full byte was received. The Amiga keyboard had its own microcontroller running a handshake protocol — the host had to acknowledge each keycode before the next one could be sent.
CIA-B Registers
CIA-B sits at even addresses starting from $BFD000. Where CIA-A faced
the user (keyboard, LED, joystick buttons), CIA-B faced the peripherals —
parallel port, floppy drive control, and serial handshaking.
CIA-B's Port B ($BFD100) carried the parallel port's 8 data lines —
directly mapped, bit for bit. The data direction register at $BFD300
controlled whether each line was input or output, making the port fully
bidirectional.
The two CIAs shared the address range $A00000–$BFFFFF through an
elegant trick: CIA-A responded only to odd addresses (byte-aligned on the 68000's lower data byte), CIA-B only to even ones.
Gary, the address decoder, used address line A12 to select between them
and directly drove their chip selects. This meant you could never accidentally talk
to both at once.
Each CIA's two 16-bit timers could run in one-shot or continuous mode, count system clock pulses or external events, and trigger interrupts on underflow. The 24-bit time-of-day counter was clocked by the mains frequency (50 Hz PAL, 60 Hz NTSC) — giving real wall-clock timing without burning a timer.
DMA System
The Amiga's DMA system was the key to its performance. While the 68000 could only do one thing at a time, the custom chips could independently read and write chip RAM through 25 DMA channels — all orchestrated by Agnus without CPU intervention.
Every scan line was divided into 227.5 color clocks (PAL). Each color clock contained two bus cycles — an even cycle and an odd cycle. Agnus allocated these cycles to different DMA channels in a fixed priority order. The CPU got whatever cycles were left over.
The priority order (highest first) was: disk, audio, sprites, bitplanes, Copper, Blitter, CPU. In practice, the CPU typically had about 40–60% of bus cycles available in a normal low-res display. A 6-bitplane hi-res display consumed so many cycles that the CPU was effectively starved.
DMACON used the SET/CLR convention found throughout the custom chip
registers: writing with bit 15 set turned on the specified bits; writing
with bit 15 clear turned them off. This avoided the read-modify-write
race conditions that would occur if you had to read the register first.
For example, MOVE.w #$8380,$DFF096 enabled master DMA, bitplane DMA,
and Copper DMA without affecting anything else.
Interrupts
The Amiga had 14 interrupt sources mapped across 7 priority levels of the 68000. Paula managed the interrupt request and enable registers, combining all sources into the CPU's IPL lines.
INTENA ($09A write, $01C read) had the same layout. Both the request
and the enable bit had to be set, plus the master enable (bit 14), for
an interrupt to actually reach the CPU. Interrupt handlers had to clear
the request bit in INTREQ before returning, or the interrupt would
re-trigger immediately.
Level 3 (INT3) was the workhorse — vertical blank ran every frame at 50 Hz (PAL) or 60 Hz (NTSC), making it the standard heartbeat for game logic, music playback, and OS scheduling. The Copper interrupt let programs trigger code at specific screen positions, which was essential for split-screen effects.
Copper
The Copper was a simple but extraordinarily powerful coprocessor built into Agnus. It had only three instructions — MOVE, WAIT, and SKIP — and could only write to custom chip registers. But because it executed in sync with the video beam, it could change hardware state at any point on screen.
The Copper list was a program stored in chip RAM. On every frame, the
Copper restarted from the address in COP1LC ($080/$082). Writing to
COPJMP1 ($088) forced an immediate restart. COP2LC/COPJMP2 provided a
second entry point — typically used by the vertical blank interrupt to
switch lists between frames.
A typical Copper list set up the display: load bitplane pointers, set colors, configure scroll registers. But the real magic was mid-screen changes. By waiting for specific beam positions and then writing new values, the Copper could create effects impossible with static register settings — gradient skies, split-screen resolutions, per-scanline color cycling, and even raster bars that cost zero CPU time.
The WAIT $FFDFFFFE instruction was the conventional end-of-list marker.
It waited for beam position V=255 H=223, which in PAL exceeded the
visible area. The Copper would stall there until the next vertical blank
restarted it.
For Copper to write to registers above $07F (the color and sprite
registers), bit 1 of COPCON ($02E) — the "Copper danger" bit — had to
be set. This prevented the Copper from accidentally modifying sensitive
registers like DMACON or disk pointers.
Playfields
The Amiga used a planar graphics model. Instead of storing each pixel as a packed color value, the image was split across up to 6 bitplanes — each bitplane held one bit of every pixel's color index. This made scrolling and certain blitter operations extremely efficient at the cost of more complex pixel manipulation.
The display window defined the visible area. DIWSTRT ($08E) set the
start position and DIWSTOP ($090) set the stop position, both in beam
coordinates. Standard PAL values were $2C81 (start) and $2CC1 (stop)
for a 320×256 display.
Data fetch timing was separate from the display window. DDFSTRT ($092)
and DDFSTOP ($094) controlled when Agnus started and stopped reading
bitplane data from RAM. For standard low-res: DDFSTRT = $0038,
DDFSTOP = $00D0. For high-res: DDFSTRT = $003C, DDFSTOP = $00D4.
Each bitplane had a pointer pair (BPL1PTH/L at $0E0/$0E2 through
BPL6PTH/L at $0EC/$0EE) pointing to its data in chip RAM. After
each scan line, a modulo value was added to the pointer — BPL1MOD
($108) for odd bitplanes, BPL2MOD ($10A) for even. Setting the
modulo to the display width allowed the bitplanes to wrap naturally; larger
values enabled smooth vertical scrolling by skipping lines.
Horizontal scrolling was achieved through BPLCON1 ($102), which held
independent 4-bit delay values for playfield 1 and playfield 2. Combined
with adjusting the data fetch start and bitplane pointers, this gave
pixel-precise smooth scrolling at no CPU cost — the hardware did it all.
Sprites
The Amiga had 8 hardware sprites, each 16 pixels wide and any height, with 3 colors plus transparency. They were completely independent of the playfield — rendered by Denise on top of (or behind) the bitplane display, with zero CPU overhead once the DMA was set up.
Each sprite had a pointer pair (SPR0PTH/L at $120/$122 through
SPR7PTH/L at $13E/$13F) pointing to sprite data in chip RAM. The
data format was simple:
Sprites could be attached in pairs (0+1, 2+3, 4+5, 6+7). When attached, the two sprites combined their bitplanes into a single 4-bitplane sprite with 15 colors plus transparent — at the cost of reducing 8 independent sprites to 4.
Sprite priority relative to the playfield was controlled by BPLCON2
($104). Each sprite pair could be placed in front of, between, or behind
the two playfields. The mouse pointer was traditionally sprite 0.
Blitter
The Blitter was a hardware block-transfer engine inside Agnus, designed for three operations: bulk memory copies (with logic), area filling, and line drawing. It operated on rectangular regions of chip RAM at speeds the 68000 could not match.
The Blitter had four DMA channels: A, B, and C as data sources, and D as the output destination. For each word processed, the three source values were combined through a programmable logic function (the minterm) to produce the output. This single mechanism handled everything from simple copies to cookie-cut sprite rendering.
The minterm byte encoded the Boolean function as a truth table: each bit
position corresponded to a combination of A, B, C inputs. For example,
$F0 = copy A (D=A), $CA = cookie-cut (D=AC+BC̄, i.e., use A where C
is set, B where C is clear), $00 = clear to zero.
The Blitter's area fill mode worked line by line, right to left. It tracked a carry bit that toggled every time it encountered a set bit in the input — filling the space between pairs of boundary pixels. Inclusive fill (IFE) kept the boundary pixels in the output; exclusive fill (EFE) removed them. This made it trivial to draw filled polygons: render the outline with the line-draw mode, then fill it in a single blitter pass.
Line drawing used a hardware Bresenham algorithm. The screen was divided into 8 octants (numbered 0–7); the programmer calculated which octant the line fell in and set the SUD/SUL/AUL bits in BLTCON1 accordingly. The Blitter could draw lines up to 1024 pixels long, with optional 16-bit repeating patterns for dashed or textured lines.
BLTSIZE ($058) started the operation: bits 15–6 held the height
(number of rows), bits 5–0 held the width (in words). Writing to
BLTSIZE triggered the Blitter — it was always the last register written
in a blitter setup sequence.
Audio
Paula provided four independent 8-bit PCM audio channels with hardware DMA. Channels 0 and 3 were routed to the left speaker, channels 1 and 2 to the right. Each channel continuously read waveform samples from chip RAM and fed them through a DAC — the music played itself without CPU intervention.
Channels 1–3 had identical registers at offsets $0B0, $0C0, $0D0.
The period register set the playback rate. The PAL system clock was 3.546895 MHz. For a sample rate of 8000 Hz: period = 3546895 / (2 × 8000) ≈ 222. The minimum useful period was about 124 (28.6 kHz) — below that, audio DMA consumed too many bus cycles and starved the display.
Paula also supported channel modulation: one channel could modulate
the period or volume of the next channel in the pair (0→1, 2→3). ADKCON
($09E) bits 4–7 controlled this. Period modulation enabled FM-like
synthesis effects; volume modulation enabled amplitude modulation (tremolo).
These modes were rarely used by tracker music but appeared in some demo
effects.
When a channel finished playing its buffer (AUDxLEN words exhausted), it triggered an audio interrupt (AUD0–AUD3 in INTREQ). The interrupt handler could then set up the next buffer — enabling double-buffered streaming audio. MOD players used this mechanism: each channel's interrupt pointed to the next set of samples, while the CPU spent its time mixing and applying effects.
Serial Port
Paula contained a UART (Universal Asynchronous Receiver/Transmitter) for serial communication. The DB25 RS-232 connector carried the data lines; CIA-B handled the handshaking signals (DTR, RTS, CTS, DSR, CD).
The baud rate was set through SERPER using the formula:
SERPER = (clock / baud_rate) − 1, where clock = 3,579,545 Hz.
For 9600 baud: SERPER = (3579545 / 9600) − 1 ≈ 372 = $0174.
For 4800 baud: SERPER ≈ 745 = $02E9.
The UART generated two interrupts: TBE (transmit buffer empty, INTREQ bit 0, level 1) when ready for the next byte, and RBF (receive buffer full, INTREQ bit 11, level 5) when a byte arrived. In raw mode (ignoring handshaking), the Amiga could reach about 292 Kbaud — far beyond the standard RS-232 rates.
Disk Controller
Paula's disk controller used DMA to transfer raw MFM-encoded data between chip RAM and the floppy drive. CIA-B controlled the mechanical signals (motor, step, direction, side select), while Paula handled the data stream.
To read a track, the programming sequence was:
- Ensure no write operation is in progress (check DSKLEN)
- Set DSKPTH/L to the buffer address
- Set DSKLEN with the transfer length and DMAEN set
- Write DSKLEN again (safety: requires two writes to start DMA)
- Wait for DSKBLK interrupt (INTREQ bit 1)
- Clear DSKLEN to stop DMA
The write sequence added an extra safety measure: DSKLEN had to be written twice with the WRITE bit set before a write would begin. This prevented accidental disk corruption from a single stray write.
ADKCON ($09E) controlled MFM/GCR encoding selection, precompensation
timing, and the WORDSYNC mode that made the controller wait for the sync
word in DSKSYNC before starting the transfer. The standard Amiga 3.5"
DD drive stored 880 KB per disk: 80 tracks × 2 sides × 11 sectors × 512
bytes.
Keyboard
The Amiga 500's keyboard contained its own 6500/1 microcontroller. It scanned the key matrix independently and transmitted 8-bit scan codes serially to CIA-A through a two-wire protocol: KDAT (data) and KCLK (clock).
The protocol was active-low, MSB first. After transmitting a complete byte, the keyboard waited for the host to acknowledge by pulling KDAT low for at least 85 microseconds. Without this handshake, the keyboard would not send the next keycode — providing built-in flow control.
The scan code format was: bit 7 = 0 for key press, 1 for key release. Bits 6–0 encoded the key identity. The raw codes were position-based, not ASCII — the OS keymap translated them into characters.
A special three-key combination (Ctrl + Amiga + Amiga) triggered a hard reset by asserting the /KBRESET line, which was directly wired to the system reset circuit. This was the infamous "three-finger salute" that every Amiga user knew by instinct.
The keyboard also transmitted a power-up key stream: a sequence of codes at startup that identified the keyboard type and language layout. The system could detect whether the keyboard was present by checking for this stream within a timeout period.
Connectors
The Amiga 500's rear panel carried a distinctive row of connectors, each with a specific purpose:
The 86-pin expansion connector on the bottom provided full access to the 68000's address and data bus, plus control signals. This was the gateway for accelerator boards, RAM expansions, and any add-on that needed to talk directly to the CPU bus. It was the Amiga's greatest strength and limitation — completely open, completely unprotected, completely trusting.
Custom Chip Register Map
All custom chip registers lived in a 512-byte block starting at $DFF000.
The offset (e.g., $096 for DMACON) was added to this base address.
Registers were either read-only (R), write-only (W), or strobe (S —
writing any value triggers the action). Some register pairs shared
addresses for reading and writing different values.
Deep Dive Pages
Each subsystem has a dedicated deep-dive page with full register documentation, instruction encoding, and programming examples — translated from the Bible de l'Amiga.