An object moves across a patterned strip without leaving its previous image behind. There is no sprite engine doing that job: the CPU saves the eight background bytes, combines the image with them, and restores them before moving.
Original Code198x program captured in Emu198x. This is an eight-pixel primitive; larger and pixel-aligned objects are onward techniques.
Code status: maintained 6502 routine, executed through a BBC BASIC loader on the Model B configuration. Execution record. The sprite family connects it to Spectrum masking, C64 hardware sprites and multiplexing, NES OAM and Amiga bobs.
The useful small case
MODE4 is a 320×256 two-colour bitmap at &5800–&7FFF. Its eight scanlines within one character cell are consecutive bytes. This routine draws one 8×8 object at a byte-aligned character-cell position; it does not pretend that arbitrary pixel positions have the same addressing.
Include masked.inc once. Set screen_ptr (&70/&71) to a complete cell in that bitmap, and pairs_ptr (&72/&73) to 16 readable mask/image bytes. Keep source data separate from the destination, saved state and stack. Clear decimal mode. A/Y/flags change; X survives. IRQ code must preserve the owned zero-page and working registers.
; Set screen_ptr and pairs_ptr first.
jsr save_background
jsr draw_masked_cell
; Before moving the object:
jsr restore_background
; Set the NEW screen_ptr, save it, and draw there.Save, mask and restore one BBC character cell
; SPDX-License-Identifier: MIT
; BBC MODE4, byte-aligned 8x8 object. A character cell is eight adjacent bytes.
; Caller sets screen_ptr=$70/$71 to one cell wholly in $5800..$7fff.
; pairs_ptr=$72/$73 addresses 16 interleaved mask/image bytes.
; saved_background[8] and saved_screen[2] belong to ONE live object.
; save -> draw -> restore before moving. Do not overlap independently saved
; objects: restore all in reverse order, then capture/draw the next frame.
; Mask 1 keeps background; image must be 0 there. No clipping/attributes/shift.
; A/Y/flags change, X preserved. Decimal mode clear. MOS IRQ must preserve ZP.
save_background:
lda screen_ptr
sta saved_screen
lda screen_ptr+1
sta saved_screen+1
ldy #7
save_background_row:
lda (screen_ptr),y
sta saved_background,y
dey
bpl save_background_row
rts
restore_background:
lda saved_screen
sta screen_ptr
lda saved_screen+1
sta screen_ptr+1
ldy #7
restore_background_row:
lda saved_background,y
sta (screen_ptr),y
dey
bpl restore_background_row
rts
draw_masked_cell:
ldy #0
masked_cell_row:
tya
pha
asl
tay
lda (pairs_ptr),y
sta mask_byte
iny
lda (pairs_ptr),y
sta image_byte
pla
tay
lda (screen_ptr),y
and mask_byte
ora image_byte
sta (screen_ptr),y
iny
cpy #8
bne masked_cell_row
rts
screen_ptr=$70
pairs_ptr=$72
saved_screen: !word 0
saved_background: !fill 8,0
mask_byte: !byte 0
image_byte: !byte 0A mask bit of one preserves background. A mask bit of zero clears that position before the image is ORed into it. Image bits must be zero wherever the mask preserves scenery. The component owns one saved address, eight saved bytes and two scratch bytes; one save slot means one live saved object.
restore_background also restores the saved address into screen_ptr. Choose the next address after restoring, rather than accidentally restoring at the new position.
Build and run the example
make creates build/loader.bas. Load or type it into BBC BASIC and RUN. It selects MODE4, reserves HIMEM=&3000, installs the original machine code and moves the object one cell per eight vertical syncs. MOS IRQ remains active; OSBYTE19 provides the sync. Reset to leave.
Complete patterned-strip caller
; MODE4 is selected by the BASIC loader. MOS IRQ remains active.
*=$3000
OSBYTE=$fff4
start:
cld
cli
lda #<shape
sta pairs_ptr
lda #>shape
sta pairs_ptr+1
lda #$6c
sta screen_ptr+1
lda #0
sta screen_ptr
ldy #0
paint:
tya
and #7
bne paint_pixel
lda #$aa
paint_pixel:
sta (screen_ptr),y
iny
bne paint
lda #$a0
sta screen_ptr
jsr save_background
jsr draw_masked_cell
main:
lda #19
jsr OSBYTE
; Move one character cell every eight vertical syncs.
inc tick
lda tick
and #7
bne main
jsr restore_background
lda screen_ptr
clc
adc #8
cmp #$f8
bcc next_cell
lda #0
next_cell:
sta screen_ptr
jsr save_background
jsr draw_masked_cell
inc updates
jmp main
tick: !byte 0
updates: !byte 0
shape: !byte $e7,$18,$c3,$3c,$81,$7e,$24,$db,$00,$ff,$a5,$5a,$db,$24,$ff,0
!source "masked.inc"Grow it without losing the background
The checks draw and restore at five cell addresses, including the bitmap’s first and last cells. They compare the rendered and restored bytes independently, check that the source survives and reject a broken mask operation. The running caller is checked separately; this is not an original-hardware or frame-budget certification.
For several saved objects, restore them in reverse drawing order before saving and drawing the next frame. If scenery changed, old saved pixels can undo that change; redraw affected tiles from the world instead. The shared sprite guide explains the choice.
Further versions can add larger rectangles, clipping, pre-shifted images and arbitrary Y positions. CRTC start-address scrolling also changes how logical positions map into physical screen RAM, including wrap. This primitive requires the ordinary MODE4 mapping; a scrolling renderer must supply its own correct mapping.
The source manual is the BBC Microcomputer Advanced User Guide, the chapter on the screen display and its MODE4 memory organisation. The routine itself uses no MOS drawing calls; the demo uses MOS only for synchronisation.
Explore the cross-platform sprite family for software drawing, hardware limits, restoration and overload policies.
For a complete caller that removes old objects and handles overlapping saved backgrounds, follow the software-object lifecycle.