Overview
Print text directly to the display file by copying character data from the ROM character set at $3C00. Faster than using ROM routines and gives you full control over positioning. Essential for scores, messages, and UI text.
Code
; =============================================================================
; CHARACTER PRINTING - ZX SPECTRUM
; Print characters using ROM character set
; Taught: Game 1 (Ink War), Unit 4
; CPU: ~200 cycles per character | Memory: ~50 bytes
; =============================================================================
DISPLAY_FILE equ $4000 ; Screen memory
CHAR_SET equ $3C00 ; ROM character set (chars 0-127)
; Print single character
; Input: A = ASCII character (32-127)
; B = row (0-23), C = column (0-31)
print_char:
push bc
push de
push hl
push af
; Calculate character data address: CHAR_SET + char * 8
ld l, a
ld h, 0
add hl, hl ; * 2
add hl, hl ; * 4
add hl, hl ; * 8
ld de, CHAR_SET
add hl, de ; HL = character data source
push hl ; Save source address
; Calculate display file address
; The Spectrum's display is organised in thirds with interleaved lines
ld a, b ; A = row (0-23)
and %00011000 ; Which third (0, 8, or 16)?
add a, $40 ; Add display file base ($4000 high byte)
ld d, a ; D = high byte
ld a, b ; A = row
and %00000111 ; Line within character row (0-7)
rrca
rrca
rrca ; Move to bits 5-7
add a, c ; Add column
ld e, a ; E = low byte, DE = screen address
pop hl ; HL = character data
; Copy 8 bytes (8 pixel rows)
ld b, 8
.loop:
ld a, (hl) ; Get character row
ld (de), a ; Write to screen
inc hl ; Next character row
inc d ; Next screen line (add 256)
djnz .loop
pop af
pop hl
pop de
pop bc
ret
Print a string:
; Print null-terminated string
; Input: HL = string address, B = row, C = column
print_string:
push bc
push hl
.loop:
ld a, (hl)
or a ; Check for null terminator
jr z, .done
call print_char
inc hl
inc c ; Next column
jr .loop
.done:
pop hl
pop bc
ret
; Example usage:
; ld hl, message
; ld b, 10 ; Row 10
; ld c, 5 ; Column 5
; call print_string
;
; message:
; defm "HELLO WORLD"
; defb 0
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | ~200 cycles per character |
| Memory | ~50 bytes for routines |
| Limitation | ROM charset only (use custom font for alternatives) |
When to use: Scores, labels, messages, any text display.
When to avoid: When you need custom graphics characters (use a custom charset instead).
Display File Address Calculation
The Spectrum’s display memory is non-linear. For character row R and column C:
High byte: $40 + (R AND $18)
Low byte: ((R AND 7) * 32) + C
= ((R AND 7) RRCA RRCA RRCA) + C
This interleaved layout was designed for TV signal generation, not programmer convenience!
ROM Character Set
The character set at $3C00 contains:
- Characters 0-31: Control characters (don’t use)
- Characters 32-127: Printable ASCII
- Each character is 8 bytes (8x8 pixels)
Related
Patterns: Attribute Writing, Numeric Display
Vault: ZX Spectrum