Skip to content

Attribute Writing

Write colours directly to attribute memory for fast, flicker-free graphics without touching pixel data.

Code status: maintained Z80 routines, executed in a bounded CPU fixture. The checks compare actual memory or emitted characters with independent expectations. They establish the stated routine contracts in uncontended 48K RAM; ROM startup, display timing and original hardware are separate checks.

Audit execution record · Scope and remaining checks

Overview

The Spectrum’s attribute system lets you colour 8x8 character cells without drawing pixels. Each attribute byte controls foreground (ink), background (paper), brightness, and flash. This is the fastest way to create colourful displays and is the basis for many classic Spectrum game graphics.

Calling the routines

Include attributes.inc once. Give the program a valid stack and ownership of the attribute memory it writes. No ROM routine or interrupt handler is required.

set_attribute takes A = attribute byte, B = row 0–23 and C = column 0–31. fill_row takes A = row and E = attribute. fill_attributes takes A = attribute, B/C = the top-left cell and D/E = height/width. A zero width or height does nothing. Other rectangles must fit: B + D ≤ 24 and C + E ≤ 32. This routine does not clip.

The cell and rectangle routines preserve AF, BC and DE, and change HL. The row filler changes AF, BC and HL, and preserves DE. The rectangle reserves up to 16 additional stack bytes, plus its caller’s return address. Never share that stack or change its loop registers from an interrupt without preserving them.

Attribute bits are FLASH, BRIGHT, PAPER and INK. Setting a cell changes the colours of all its pixels; it does not draw or erase the bitmap. Try a 4 × 3 rectangle, then one at row 23, column 31. Both the visible footprint and the bytes outside it should agree with the requested area.

For example, after establishing the documented stack, state and data:

Z80 assembly
    ld a,$47          ; bright white ink, black paper
    ld b,2
    ld c,3
    ld d,3            ; height in character cells
    ld e,4            ; width
    call fill_attributes
    ; Include attributes.inc once, outside the executed control flow.

Attribute cells, rows and rectangles

attributes.inc
; ZX Spectrum attributes, Pasmo syntax. Caller supplies stack and writable display.
; Coordinates are character cells: B=row 0..23, C=column 0..31, A=attribute.
; set_attribute preserves AF/BC/DE; changes HL. No ROM or interrupt dependency.
ATTR_BASE equ $5800
set_attribute:
    push af
    push bc
    ld l,b
    ld h,0
    add hl,hl
    add hl,hl
    add hl,hl
    add hl,hl
    add hl,hl
    ld a,c
    or l                       ; column occupies the five zero low bits
    ld l,a
    ld bc,ATTR_BASE
    add hl,bc
    pop bc
    pop af
    ld (hl),a
    ret

; B/C=start row/column; D=height, E=width, A=attribute.
; Zero width or height is a no-op. Otherwise row+height<=24, column+width<=32.
; No clipping: caller validates those bounds before entry.
; Preserves AF/BC/DE; changes HL. Maximum additional stack: 16 bytes including
; set_attribute's return address and saves (plus this routine's return address).
fill_attributes:
    push af
    ld a,d
    or a
    jr z,fill_empty
    ld a,e
    or a
    jr z,fill_empty
    pop af
    push af
    push bc
    push de
fill_next_row:
    push bc                    ; remember the left edge, not the current column
    push de                    ; remember width before consuming its counter
fill_next_column:
    call set_attribute         ; A and both loop counters survive
    inc c
    dec e
    jr nz,fill_next_column
    pop de
    pop bc
    inc b
    dec d
    jr nz,fill_next_row
    pop de
    pop bc
fill_empty:
    pop af
    ret

; A=row 0..23, E=attribute. Changes AF/BC/HL; preserves DE.
fill_row:
    ld l,a
    ld h,0
    add hl,hl
    add hl,hl
    add hl,hl
    add hl,hl
    add hl,hl
    ld bc,ATTR_BASE
    add hl,bc
    ld b,32
fill_row_byte:
    ld (hl),e
    inc hl
    djnz fill_row_byte
    ret

Code

Fill a rectangular area:

Fast row fill (optimised):

Trade-offs

Aspect Cost
CPU Not measured for this caller; include the selected input, drawing and scheduling costs
Memory 768 bytes for full screen
Limitation 8x8 cell resolution only

When to use: Board games, puzzles, status displays, any game using character-cell graphics.

When to avoid: When you need pixel-level colour control (not possible on Spectrum).

Attribute Memory Layout

Code
$5800-$581F: Row 0  (columns 0-31)
$5820-$583F: Row 1
$5840-$585F: Row 2
...
$5AE0-$5AFF: Row 23

Total: 768 bytes (24 rows x 32 columns)

Colour Reference

Value Normal Bright
0 Black Black
1 Blue Bright Blue
2 Red Bright Red
3 Magenta Bright Magenta
4 Green Bright Green
5 Cyan Bright Cyan
6 Yellow Bright Yellow
7 White Bright White

Patterns: Game Loop (HALT)

Vault: ULA — attribute memory layout | ZX Spectrum