Skip to content

Blitter Copy

Copy an aligned tile plane with reusable Amiga code, with DMA ownership, pitches and completion made explicit.

The blitter can move a rectangle of pixels while the CPU handles other work. Before building that overlap into a game, start with a copy whose ownership and finish are unambiguous. This small component copies one 16×8 monochrome tile plane, waiting for completion before it returns.

Amiga two-plane region rendered through the hardware blitter, with white, green and red tile patterns
Two calls per tile build this four-colour picture in the original A500 PAL diagnostic. The CPU backend produces identical plane memory and native pixels.

Supply the buffers

Include blit-plane.inc once. Call region_blit_plane with A1=source, A2=destination and A5=$DFF000, the custom-chip base. Both buffers must be even-aligned Chip RAM. The source is eight consecutive words, one word per scanline. The destination has a forty-byte pitch: the next line starts forty bytes after the previous one.

Reserve the whole accessed destination rectangle, not just sixteen consecutive bytes. The helper preserves all address and data registers; CCR and the listed blitter registers change. Allow eight stack bytes for the direct BSR and its nested wait call, beyond your caller and interrupt needs.

You must own the blitter exclusively and enable master/blitter DMA before calling. Keep buffers and register ownership stable until it returns. The helper neither disables interrupts nor obtains OS ownership. An OS application must arrange that separately; the linked reset diagnostic takes over the machine.

The reusable copy

Copy one aligned tile plane and wait for completion

blit-plane.inc
; Copy one aligned 16x8 monochrome tile plane: A1 source, A2 destination, A5 custom base.
; Both buffers are Chip RAM. Source pitch 2 bytes; destination pitch 40 bytes.
; Preserves address/data registers; clobbers CCR and the named blitter registers.
; Caller owns blitter and enables master+blitter DMA; returns after completion.
region_blit_plane:
    bsr region_wait_blitter
    move.w #$09f0,$040(a5)
    clr.w $042(a5)
    move.w #$ffff,$044(a5)
    move.w #$ffff,$046(a5)
    move.l a1,$050(a5)
    move.l a2,$054(a5)
    clr.w $064(a5)
    move.w #38,$066(a5)
    move.w #$0201,$058(a5)
    bsr region_wait_blitter
    rts
region_wait_blitter:
    ; Initial read precedes the busy-bit polling loop (old Agnus precaution).
    tst.w $002(a5)
region_wait_busy:
    btst #6,$002(a5)
    bne region_wait_busy
    rts

BLTCON0=$09F0 enables A as input and D as output, using the minterm for D=A. B and C are not inputs to this copy. Full first/last-word masks preserve all sixteen source bits. Source modulo is zero; BLTDMOD=38 skips the destination’s remaining nineteen words after each copied word. BLTSIZE=$0201 means eight lines of one word, and writing it starts the operation.

The helper changes BLTCON0/1, BLTAFWM/BLTALWM, BLTAPT/BLTDPT, BLTAMOD/BLTDMOD and BLTSIZE. It waits before setting those registers and before returning. The initial hardware read before busy-bit polling follows the Hardware Reference Manual’s older-Agnus precaution. Never change blitter registers while an earlier operation owns them.

Draw a coloured tile

A two-plane tile stores eight words for plane 0 followed by eight for plane 1. The region renderer calls the helper once for each plane. Its maintained caller shows the pointer setup and the direct calls:

Use the copy once per destination plane

m68000.inc, excerpt
region_blitter:
    lea $dff000,a5
    bsr region_blit_plane
    lea 16(a1),a1
    movea.l a3,a2
    bsr region_blit_plane
    rts

The complete region component validates map IDs, bounds, backend selection and pointer alignment before reaching this copy. The leaf helper itself assumes valid pointers, pitches, allocations and DMA ownership. Use the region component when you need those checks.

Choose the right amount of work

Each call copies sixteen bytes, with setup and two waits as well. For a small tile, CPU word copies may be a better choice. Hardware acceleration does not remove setup or Chip RAM contention, and this blocking version does not demonstrate useful overlap. Measure your own workload before choosing a backend.

Larger rectangles can amortise setup. An asynchronous variant can return after submission, but must expose when its buffers and registers become available again. A masked sprite needs a different operation, such as cookie-cut blitting. Changing width also changes source/destination modulo calculations; it is not just a new BLTSIZE value.

What was checked

The original reset diagnostic runs 22 calls per backend on Emu198x 0.39.0 with Asm198x 0.0.71. CPU and blitter complete plane allocations match an independent expected layout and match each other byte for byte. Both mapped guards and every native capture pixel are checked. Fifteen rejected map/bounds/ID requests run separately per backend and leave the destination unchanged.

A deliberately shortened blit completes but fails the memory and pixel checks. That establishes that the selected backend really exercises this hardware-copy path. Backend and alignment rejection cases also complete. These results qualify this A500 PAL configuration, not original-hardware timing, stock-Kickstart integration or throughput.