Open a door, break a wall, reveal the next stretch of scenery. The map changes first; this routine makes the picture catch up. Give it a rectangle of byte-map IDs and a destination, and it draws just that part of the world.

One picture, two bitplanes
This A500 PAL example draws a 320×256 picture from two separate bitplanes. A cell is 16×8 pixels, so the logical display has twenty columns and thirty-two rows. Each tile takes 32 bytes: eight words of plane 0, then eight words of plane 1. Combining their bits selects one of four colours.
Each destination plane has a forty-byte scanline pitch and takes 10,240 bytes. The original caller places them at $10000 and $12800, provides its Copper list and colours, and draws before enabling display DMA. Choose Chip RAM allocations which fit your own project; the source map and two planes are separate resources.
Give the routine its context
Pass A0 pointing to an even-aligned, private 32-byte context. Bytes 0–5 are the request and byte 6 is the completion flag. Byte 7 selects backend 0 CPU, 1 blitter. Longwords at 8, 12, 16 and 20 point to the byte map, tile pictures, plane 0 and plane 1. Bytes 24–26 hold map width, height and tile count; 27–31 are scratch.
Initialise the pointers, dimensions and selected backend
lea NR,a0
move.b #BACKEND,7(a0)
move.l #MAP,8(a0)
move.l #TILES,12(a0)
move.l #PLANE0,16(a0)
move.l #PLANE1,20(a0)
move.b #12,24(a0)
move.b #8,25(a0)
move.b #4,26(a0)The map may be byte-aligned. Tile and plane pointers must be even; misalignment and unknown backend values are rejected before painting. The caller guarantees allocation sizes, pointer ranges and Chip RAM visibility. No routine can infer those allocations from a bare address.
A0 and the six request bytes survive. D0–D7, A1–A5 and CCR are clobbered. Allow eight stack bytes on the CPU path or sixteen on the blitter path, including the direct BSR, beyond your own caller and interrupt needs. Include m68000.inc with its adjacent blit-plane.inc helper available at the maintained relative path.
Choose how to copy
The CPU path copies eight words per plane. The blitter path calls the separate Blitter Copy component once per plane. Both write 32 bytes per cell, or 480 bytes for the 5×3 patch, in addition to preflight work.
The blitter helper copies one word across eight lines. Source pitch is two bytes; destination pitch is forty, so BLTDMOD=38. BLTSIZE=$0201 starts the copy. It waits before setting registers and again before returning. This is synchronous: the example demonstrates the hardware path, not useful CPU/blitter overlap or an automatic speed win for small tiles.
Your caller owns the blitter exclusively, enables master/blitter DMA and keeps all buffers stable. Neither backend changes interrupt state. An OS application needs an ownership protocol; the reset diagnostic owns the machine. Bigger spans, asynchronous submission, masks and clipping are useful further variants, each with different obligations.
Draw the patch
These are the example caller’s allocations and constants. Use addresses which fit your own memory plan.
The caller owns its memory and display configuration
NR equ $2000
RESULT equ $2040
MAP equ $20f0
TILES equ $6000
PLANE0 equ $10000
PLANE1 equ $12800
SCREEN_COLS equ 20
SCREEN_ROWS equ 32
BACKEND equ 0Supply six unsigned bytes: source column, source row, destination column, destination row, width and height. Then call region_draw. 6(A0) is one for a completed draw or zero for rejection. The six request bytes survive.
Draw a 5×3 patch from map cell (2,1) at display cell (4,3)
case_0:
move.b #2,0(a0)
move.b #1,1(a0)
move.b #4,2(a0)
move.b #3,3(a0)
move.b #5,4(a0)
move.b #3,5(a0)
bsr region_draw
move.b 6(a0),d0
or.b #128,d0
move.b d0,RESULT+1The result store after the call belongs to the diagnostic. In a game, inspect the completion flag and continue with your own logic. The routine rejects zero sizes, invalid starts, source/destination overruns and IDs outside the configured tile set. It checks every selected ID before writing any destination bytes, so a bad ID cannot leave half a patch behind. It rejects rather than clips.
Keep the reusable code
Checked native region drawing
; A0 -> 32-byte even-aligned context. Six request bytes, valid +6,
; backend +7 (0 CPU, 1 blitter), long map/tiles/plane0/plane1 +8/+12/+16/+20,
; byte map width/height/tile count +24/+25/+26; scratch +27..31.
; A0 preserved; D0-D7/A1-A5/CCR clobbered. Caller owns custom chips and DMA.
region_draw:
clr.b 6(a0)
cmp.b #1,7(a0)
bls region_backend_ok
rts
region_backend_ok:
move.l 12(a0),d0
btst #0,d0
beq region_tiles_aligned
rts
region_tiles_aligned:
move.l 16(a0),d0
btst #0,d0
beq region_plane0_aligned
rts
region_plane0_aligned:
move.l 20(a0),d0
btst #0,d0
beq region_plane1_aligned
rts
region_plane1_aligned:
tst.b 4(a0)
bne bounds_nonzero_0
rts
bounds_nonzero_0:
move.b 24(a0),d0
cmp.b 0(a0),d0
bhi bounds_start_0
rts
bounds_start_0:
sub.b 0(a0),d0
cmp.b 4(a0),d0
bhs bounds_fit_0
rts
bounds_fit_0:
tst.b 5(a0)
bne bounds_nonzero_1
rts
bounds_nonzero_1:
move.b 25(a0),d0
cmp.b 1(a0),d0
bhi bounds_start_1
rts
bounds_start_1:
sub.b 1(a0),d0
cmp.b 5(a0),d0
bhs bounds_fit_1
rts
bounds_fit_1:
tst.b 4(a0)
bne bounds_nonzero_2
rts
bounds_nonzero_2:
move.b #SCREEN_COLS,d0
cmp.b 2(a0),d0
bhi bounds_start_2
rts
bounds_start_2:
sub.b 2(a0),d0
cmp.b 4(a0),d0
bhs bounds_fit_2
rts
bounds_fit_2:
tst.b 5(a0)
bne bounds_nonzero_3
rts
bounds_nonzero_3:
move.b #SCREEN_ROWS,d0
cmp.b 3(a0),d0
bhi bounds_start_3
rts
bounds_start_3:
sub.b 3(a0),d0
cmp.b 5(a0),d0
bhs bounds_fit_3
rts
bounds_fit_3:
clr.b 30(a0)
region_pass:
clr.b 27(a0)
region_row:
clr.b 28(a0)
region_cell:
moveq #0,d0
move.b 1(a0),d0
add.b 27(a0),d0
moveq #0,d1
move.b 24(a0),d1
mulu.w d1,d0
moveq #0,d1
move.b (a0),d1
add.b 28(a0),d1
add.l d1,d0
movea.l 8(a0),a1
move.b 0(a1,d0.l),d1
move.b d1,29(a0)
cmp.b 26(a0),d1
blo region_id_ok
rts
region_id_ok:
tst.b 30(a0)
beq region_next
bsr region_paint
region_next:
addq.b #1,28(a0)
move.b 28(a0),d0
cmp.b 4(a0),d0
beq region_row_done
bra region_cell
region_row_done:
addq.b #1,27(a0)
move.b 27(a0),d0
cmp.b 5(a0),d0
beq region_pass_done
bra region_row
region_pass_done:
tst.b 30(a0)
bne region_success
move.b #1,30(a0)
bra region_pass
region_success:
move.b #1,6(a0)
rts
region_paint:
moveq #0,d0
move.b 29(a0),d0
lsl.l #5,d0
movea.l 12(a0),a1
adda.l d0,a1
moveq #0,d0
move.b 3(a0),d0
add.b 27(a0),d0
mulu.w #320,d0
moveq #0,d1
move.b 2(a0),d1
add.b 28(a0),d1
add.w d1,d1
add.l d1,d0
movea.l 16(a0),a2
adda.l d0,a2
movea.l 20(a0),a3
adda.l d0,a3
tst.b 7(a0)
bne region_blitter
moveq #7,d4
region_cpu_plane0:
move.w (a1)+,(a2)
lea 40(a2),a2
dbra d4,region_cpu_plane0
moveq #7,d4
region_cpu_plane1:
move.w (a1)+,(a3)
lea 40(a3),a3
dbra d4,region_cpu_plane1
rts
region_blitter:
lea $dff000,a5
bsr region_blit_plane
lea 16(a1),a1
movea.l a3,a2
bsr region_blit_plane
rts
include "../../../../../cross-platform/patterns/rendering/region-architectures/blit-plane.inc"The context is non-re-entrant. Your caller owns its memory, video setup and interrupt policy. Keep all inputs and display resources stable throughout the call. Drawing an opaque zero tile erases a cell; transparent sprites need masking or background restoration instead.
What was checked
All 22 calls completed per backend in the original reset diagnostic. Fifteen rejected map/bounds/ID requests were also executed in isolation, each after the same three accepted draws. The complete destination and both mapped guards were compared with an independent model, and every native capture pixel was compared with a separate composition. Completed wrong-layout and unsafe-destination controls fail those checks.
Both CPU and blitter outputs match byte for byte. Backend and alignment rejection cases complete on each path; a deliberately shortened hardware blit fails the independent checks.
These results qualify the selected Emu198x 0.39.0 configuration and Asm198x 0.0.71 fixture. They do not establish original-hardware timing, stock-OS loading, interrupt coexistence or a whole-game frame budget. The complete caller keeps reset setup separate from reusable drawing. Copy counts above are not measured timing claims.
- Complete caller and execution evidence
- Calling contracts, costs and primary references
- Other native region routines
- Map storage and queries
CPU pixels, spans and boxes handle arbitrary pixel edges in one plane. Bitmap text adds glyph lookup, clipping and counted layout over that backend. These have their own contexts and costs rather than silently inheriting this aligned region contract.