Blitter: The Amiga's Graphics Accelerator
Hardware-powered block transfers
The Blitter moved, combined, and transformed graphics data faster than any CPU could—making the Amiga's smooth scrolling and complex visuals possible.
The Blitter (block image transferrer) was a DMA engine inside Agnus that performed graphics operations in hardware. It could copy memory, combine multiple sources with logic operations, fill areas, and draw lines—all while the CPU worked on game logic. This division of labour gave the Amiga its characteristic smooth, fast graphics.
Fast facts
- Location: integrated into Agnus chip.
- Sources: up to 3 inputs (A, B, C) plus 1 output (D).
- Operations: copy, logic combine, area fill, line draw.
- Speed: not a fixed figure — Commodore states bus cycles are “dynamically allocated” and declines to guarantee its own timing chart.
256 ways to combine three sources
The blitter’s central trick is not copying but combining. Three source channels go in, one destination comes out, and what happens between them is chosen from every possible answer:
The blitter can combine the data from the three source DMA channels in up to 256 different ways to generate the values stored by the destination DMA channel.
The 256 is not a designer’s list of useful operations. Three source bits have
eight possible combinations, and for each the programmer says whether the
destination bit is 0 or 1 — eight yes/no answers, so 2⁸ = 256, and every one of
them is available. The eight answers are written as a single byte into BLTCON0,
and that byte is the operation.
The manual works the standard case: the “classic cookie-cut function of AB+AC”,
which is the byte $CA. Masked sprite blitting — the thing the Amiga is
famous for — is not a mode the hardware provides. It is one value out of 256 in a
control register.
Blitter channels
| Channel | Purpose |
|---|---|
| A | Source data, often used as mask |
| B | Source data |
| C | Destination read (for combining) |
| D | Destination write |
Not all channels need be active for every operation.
Blitter nasty
DMACON bit 10 carries a name Commodore printed in its own manual:
BLTPRI Blitter priority. Also known as “blitter-nasty.” When this is a 1, the blitter has full (instead of partial) priority over the 680x0.
The register description elsewhere gives the mechanism: setting it “disables /BLS pin, preventing micro from stealing any bus cycles while blitter DMA is” running. Normally the processor may take slots the blitter is not using. With this bit set, it cannot — the blitter holds the bus until it is finished, and the CPU stops.
This is the Amiga’s contention argument in one bit. The machine’s other components negotiate for memory; the blitter can be told to stop negotiating.
Speed, which Commodore would not promise
The figure usually quoted for blitter throughput is a fixed number of cycles per word. The Hardware Reference Manual prints a timing table and then disclaims it:
Bus cycles are dynamically allocated based on blitter operating mode; competing bus activity from processor, bitplanes, and other DMA channels; and other factors. Commodore Amiga does not guarantee the accuracy of or future adherence to this chart.
A blit’s cost depends on how many bitplanes the display is fetching, what else holds DMA slots, and which blitter mode is running. There is also a pipeline to account for: “typical operation involves fetching all sources twice before the first destination becomes available”, with the warning that overlapping source and destination regions therefore need care.
Basic operations
Copy (A to D)
move.w #$09f0,BLTCON0(a6) ; A->D, no shifts
move.w #$0000,BLTCON1(a6) ; No special modes
move.l #source,BLTAPT(a6) ; Source address
move.l #dest,BLTDPT(a6) ; Destination address
move.w #$0000,BLTAMOD(a6) ; Source modulo
move.w #$0000,BLTDMOD(a6) ; Dest modulo
move.w #64*64+40,BLTSIZE(a6) ; 64 lines × 40 words
Masked copy (A masks B to D)
Cookie-cutter operation for sprites:
move.w #$0fca,BLTCON0(a6) ; A=mask, B=source, C=bg, D=output
Minterm $CA: D = (A AND B) OR (NOT A AND C)
Fill
Fill enclosed areas:
move.w #$0001,BLTCON1(a6) ; Fill mode enabled
Draw outline first, then fill—Blitter toggles fill state at each edge pixel.
Line drawing
Draw lines in hardware:
move.w #$0001,BLTCON1(a6) ; Line mode + octant
move.w #dx,BLTBMOD(a6) ; Delta X
move.w #dy,BLTAMOD(a6) ; Delta Y
; ... additional setup
move.w #1*64+width,BLTSIZE(a6) ; Start line draw
Minterm logic
The Blitter combines sources using an 8-bit minterm:
| Bit | Condition |
|---|---|
| 7 | A AND B AND C |
| 6 | A AND B AND NOT C |
| 5 | A AND NOT B AND C |
| 4 | A AND NOT B AND NOT C |
| 3 | NOT A AND B AND C |
| 2 | NOT A AND B AND NOT C |
| 1 | NOT A AND NOT B AND C |
| 0 | NOT A AND NOT B AND NOT C |
Common minterms
| Minterm | Operation |
|---|---|
| $F0 | D = A (copy A) |
| $CC | D = B (copy B) |
| $AA | D = C (copy C) |
| $CA | D = (A∧B)∨(¬A∧C) (cookie cut) |
| $5A | D = A XOR C |
| $0A | D = A AND C |
Shifting
The Blitter can shift data horizontally:
move.w #$n9f0,BLTCON0(a6) ; n = shift count (0-15)
Essential for pixel-precise positioning in planar graphics.
Blitter registers
| Register | Address | Purpose |
|---|---|---|
| BLTCON0 | $DFF040 | Control: minterm, shift, channels |
| BLTCON1 | $DFF042 | Control: fill, line mode |
| BLTAFWM | $DFF044 | First word mask for A |
| BLTALWM | $DFF046 | Last word mask for A |
| BLTCPT | $DFF048 | Channel C pointer |
| BLTBPT | $DFF04C | Channel B pointer |
| BLTAPT | $DFF050 | Channel A pointer |
| BLTDPT | $DFF054 | Channel D pointer |
| BLTSIZE | $DFF058 | Size and start operation |
| BLTCMOD | $DFF060 | Channel C modulo |
| BLTBMOD | $DFF062 | Channel B modulo |
| BLTAMOD | $DFF064 | Channel A modulo |
| BLTDMOD | $DFF066 | Channel D modulo |
| BLTCDAT | $DFF070 | Channel C data |
| BLTBDAT | $DFF072 | Channel B data |
| BLTADAT | $DFF074 | Channel A data |
Modulo explained
Modulo values handle non-contiguous memory:
Source width = 320 pixels = 40 bytes = 20 words
Screen width = 320 pixels = 40 bytes = 20 words
Blit width = 32 pixels = 4 bytes = 2 words
Modulo = screen width - blit width = 20 - 2 = 18 words
Waiting for Blitter
Always wait before using Blitter results:
wait_blit:
btst #6,$dff002 ; Check BLTBUSY in DMACONR
bne.s wait_blit ; Loop until done
The btst #6,$dff002 idiom is the classic Amiga form. It works because btst with a memory operand is byte-sized: it reads the high byte of DMACONR (which is at $dff002) and tests bit 6 of that byte — which is bit 14 of the full 16-bit register, the BBUSY flag. Equivalent and more obvious if you prefer: btst #14,$dff002 with a word read also works but compiles to a longer instruction.