Skip to content

Character Printing

Print ASCII characters to the screen using the ROM character set. Direct display file access for fast text rendering.

Taught in Game 1, Unit 4 textcharactersdisplayrom

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

AspectCost
CPU~200 cycles per character
Memory~50 bytes for routines
LimitationROM 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)

Patterns: Attribute Writing, Numeric Display

Vault: ZX Spectrum