Skip to content
Game 0 Unit 16 of 19 1 hr learning time

The Blitter Moves It

Everything so far has been the 68000 alone. The Blitter is a chip built for one job — shifting blocks of graphics through memory far faster than the CPU can. You point it at a source, a destination, and a size, and it moves the block while the CPU does something else.

84% of Meet The Machine

This is where the Amiga stops being a fast 68000 and starts being an Amiga. Until now every pixel was moved by the CPU, one instruction at a time. The custom chips change that — and the first of them is the Blitter.

The Blitter (block image transferrer) does one thing: it shifts rectangular blocks of memory from one place to another, at speed, on its own. Moving a 32×24 graphic with the CPU is hundreds of instructions; the Blitter does it with a handful of register writes and then runs while the 68000 is free to do other work. Every smooth-moving Amiga game leans on it — drawing, erasing, scrolling, all blitter work.

You set up a blit by filling in registers: where the source is, where the destination is, how the chip steps between rows, and — last — the size, which starts the blit. This unit copies one block to a second spot, on the one-bitplane display from Unit 6.

What you'll see by the end

A black screen with two yellow rectangles: one at the top-left, one lower and to the right.
Two blocks. The CPU drew the top-left one; the Blitter copied it down and across. The CPU only ever drew once.

Two yellow blocks on black. The CPU drew exactly one — top-left, the same way as Unit 6. The second, lower and to the right, is that block copied by the Blitter in a single operation.

Set it up, then start it

;──────────────────────────────────────────────────────────────
; Meet the Machine (Amiga) - Unit 16: The Blitter Moves It
;
; The Blitter is a chip that shifts blocks of memory - graphics - far faster
; than the CPU. Here the CPU draws one block; the Blitter copies it somewhere
; else in a single operation. Two blocks appear; the CPU only ever drew one.
; (Display set-up is the same one-bitplane harness from Unit 6.)
;──────────────────────────────────────────────────────────────

CUSTOM      equ $dff000
DMACONR     equ $002            ; DMA status (read) - bit 14 = Blitter busy
DMACON      equ $096
INTENA      equ $09a
INTREQ      equ $09c
COP1LC      equ $080
COPJMP1     equ $088
DIWSTRT     equ $08e
DIWSTOP     equ $090
DDFSTRT     equ $092
DDFSTOP     equ $094
BLTCON0     equ $040
BLTCON1     equ $042
BLTAFWM     equ $044
BLTALWM     equ $046
BLTAPTH     equ $050
BLTDPTH     equ $054
BLTAMOD     equ $064
BLTDMOD     equ $066
BLTSIZE     equ $058
BPLCON0     equ $100
BPL1MOD     equ $108
BPL1PTH     equ $0e0
BPL1PTL     equ $0e2
COLOR00     equ $180
COLOR01     equ $182

ROWBYTES    equ 40                  ; 320 pixels / 8 = 40 bytes per row
BPHEIGHT    equ 256
BPSIZE      equ ROWBYTES*BPHEIGHT
BLTW        equ 2                   ; blit width in words (32 pixels)
BLTH        equ 24                  ; blit height in rows
DESTOFF     equ 80*ROWBYTES+8       ; dest: 80 rows down, 64 pixels across

            section code,code_c

start:
            lea     CUSTOM,a5
            move.w  #$7fff,INTENA(a5)
            move.w  #$7fff,INTREQ(a5)
            move.w  #$7fff,DMACON(a5)

            ; --- clear the bitplane to all 0 ---
            lea     bitplane,a0
            move.w  #(BPSIZE/4)-1,d0
.clr:       clr.l   (a0)+
            dbra    d0,.clr

            ; --- CPU draws ONE block, top-left: 32 wide, 24 rows ---
            lea     bitplane,a0
            move.w  #BLTH-1,d1
.row:       move.l  #$ffffffff,(a0)
            lea     ROWBYTES(a0),a0
            dbra    d1,.row

            ; --- point the display at the bitplane (patch the Copper) ---
            lea     bitplane,a0
            move.l  a0,d0
            swap    d0
            move.w  d0,bplhi
            swap    d0
            move.w  d0,bpllo

            lea     copperlist,a0
            move.l  a0,COP1LC(a5)
            move.w  d0,COPJMP1(a5)
            move.w  #$83c0,DMACON(a5)   ; DMA on: master + bitplane + Copper + Blitter

            ; ----------------------------------------------- YOUR CODE START
            ; --- the Blitter copies the block to a new spot ---
.waitblit:  btst    #6,DMACONR(a5)      ; wait until the Blitter is idle
            bne.s   .waitblit

            move.w  #$ffff,BLTAFWM(a5)  ; no masking of the first/last word
            move.w  #$ffff,BLTALWM(a5)
            move.w  #$09f0,BLTCON0(a5)  ; use A and D; D = A (a straight copy)
            move.w  #$0000,BLTCON1(a5)
            move.w  #ROWBYTES-BLTW*2,BLTAMOD(a5)   ; skip the rest of each source row
            move.w  #ROWBYTES-BLTW*2,BLTDMOD(a5)   ; ...and each dest row

            lea     bitplane,a0
            move.l  a0,BLTAPTH(a5)      ; source = the block the CPU drew
            lea     bitplane+DESTOFF,a0
            move.l  a0,BLTDPTH(a5)      ; dest = down and across

            move.w  #(BLTH<<6)!BLTW,BLTSIZE(a5)    ; height + width - this starts the blit
            ; ------------------------------------------------- YOUR CODE END

forever:
            bra.s   forever

;──────────────────────────────────────────────────────────────
; Copper list: one-bitplane 320x256 display.
;──────────────────────────────────────────────────────────────
copperlist:
            dc.w    DIWSTRT,$2c81
            dc.w    DIWSTOP,$2cc1
            dc.w    DDFSTRT,$0038
            dc.w    DDFSTOP,$00d0
            dc.w    BPLCON0,$1200
            dc.w    BPL1MOD,$0000
            dc.w    BPL1PTH
bplhi:      dc.w    $0000
            dc.w    BPL1PTL
bpllo:      dc.w    $0000
            dc.w    COLOR00,$0000       ; background black
            dc.w    COLOR01,$0ff0       ; lit pixels yellow
            dc.w    $ffff,$fffe

            section bss,bss_c
bitplane:   ds.b    BPSIZE

The copy is the block inside YOUR CODE:

.waitblit:  btst    #6,DMACONR(a5)      ; wait until the Blitter is idle
            bne.s   .waitblit

            move.w  #$ffff,BLTAFWM(a5)  ; no masking of the first/last word
            move.w  #$ffff,BLTALWM(a5)
            move.w  #$09f0,BLTCON0(a5)  ; use A and D; D = A (a straight copy)
            move.w  #$0000,BLTCON1(a5)
            move.w  #ROWBYTES-BLTW*2,BLTAMOD(a5)   ; skip the rest of each row
            move.w  #ROWBYTES-BLTW*2,BLTDMOD(a5)

            lea     bitplane,a0
            move.l  a0,BLTAPTH(a5)      ; source = the block the CPU drew
            lea     bitplane+DESTOFF,a0
            move.l  a0,BLTDPTH(a5)      ; dest = down and across

            move.w  #(BLTH<<6)!BLTW,BLTSIZE(a5)    ; this starts the blit

Read it as filling in a form. First wait for the Blitter to be free — btst #6,DMACONR tests the busy bit, and you never start a blit on top of a running one. Then the settings: BLTCON0 = $09f0 says "read channel A, write channel D, and make D a straight copy of A." The modulo registers (BLTAMOD, BLTDMOD) tell the chip how many bytes to skip at the end of each row, so a 32-pixel-wide block steps correctly down a 320-pixel-wide screen. BLTAPT and BLTDPT are the source and destination addresses.

The last write does the work. BLTSIZE packs the height and width together, and writing it is what starts the blit. The chip then copies all 24 rows by itself; the CPU has already moved on to the forever loop. You described a move; the Blitter performed it.

Assemble, master, and run

make

Two yellow blocks — one drawn, one blitted.

Try this: move the copy

DESTOFF decides where the copy lands — 80*ROWBYTES+8 means 80 rows down and 8 bytes (64 pixels) across. Change it: 40*ROWBYTES puts the copy straight below the original; +20 shifts it further right. The source never moves; only where the Blitter writes does.

Try this: many copies

Wrap the blit in a loop (Unit 12's DBRA), bumping BLTDPT each pass so each copy lands in a new spot — remembering to wait for the Blitter between blits. One drawn block becomes a field of them, and the CPU barely lifts a finger. That is how a game paints a screen full of bricks or stars.

If it doesn't work

  • Only one block shows. The blit didn't start — BLTSIZE must be the last Blitter register you write, because writing it is the trigger.
  • The copy is smeared or skewed. The modulo is wrong. BLTAMOD/BLTDMOD must be the row width minus the blit width in bytes, so the chip lines up each row under the last.
  • The machine locks up. A blit was started while another was running. Always btst #6,DMACONR and wait before setting up the next one.

What you've learnt

The Blitter moves rectangular blocks of graphics through memory far faster than the CPU. You fill in registers — BLTCON0 for the operation, the modulo registers for the row stride, BLTAPT/BLTDPT for source and destination — and writing BLTSIZE starts the blit. It runs on its own while the CPU works, and you wait on the busy bit before starting another. This is the engine behind Amiga graphics.

What's next

The Blitter draws into the bitplane. The next chip puts an object on screen that isn't in the bitplane at all — it floats over the top, moved by setting two numbers. Next — A Sprite of Your Own — the hardware sprite.