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.

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
; 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
rtsBLTCON0=$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
region_blitter:
lea $dff000,a5
bsr region_blit_plane
lea 16(a1),a1
movea.l a3,a2
bsr region_blit_plane
rtsThe 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.