Skip to content

Keyboard Reading

Read the ZX Spectrum keyboard matrix by polling I/O ports. Simple, efficient, and no ROM routines required.

Taught in Game 1, Unit 1 keyboardinputz80ports

Overview

Read keyboard input by polling the I/O ports directly. The Spectrum's keyboard is arranged in a matrix of 8 rows, each accessible via a specific port address. Keys are active low (0 = pressed). Use when you need responsive input without relying on the ROM.

Code

; =============================================================================
; KEYBOARD READING - ZX SPECTRUM
; Read keyboard matrix via I/O ports
; Taught: Game 1 (Ink War), Unit 1
; CPU: ~50 cycles | Memory: ~40 bytes
; =============================================================================

KEY_PORT    equ     $fe

; Keyboard row addresses (active low)
ROW_QAOP    equ     $fb             ; Q W E R T (bits: T R E W Q)
ROW_ASDF    equ     $fd             ; A S D F G (bits: G F D S A)
ROW_YUIOP   equ     $df             ; Y U I O P (bits: P O I U Y)
ROW_12345   equ     $f7             ; 1 2 3 4 5 (bits: 5 4 3 2 1)
ROW_09876   equ     $ef             ; 0 9 8 7 6 (bits: 6 7 8 9 0)
ROW_SPACE   equ     $7f             ; Space, Sym, M, N, B

; Read keyboard and return direction code
; Returns: A = 0 (none), 1 (up), 2 (down), 3 (left), 4 (right)
read_keyboard:
    xor     a
    ld      (key_pressed), a    ; Clear previous

    ; Check Q (up)
    ld      a, ROW_QAOP
    in      a, (KEY_PORT)
    bit     0, a                ; Q is bit 0
    jr      nz, .not_q
    ld      a, 1
    ld      (key_pressed), a
    ret
.not_q:
    ; Check A (down)
    ld      a, ROW_ASDF
    in      a, (KEY_PORT)
    bit     0, a                ; A is bit 0
    jr      nz, .not_a
    ld      a, 2
    ld      (key_pressed), a
    ret
.not_a:
    ; Check O (left)
    ld      a, ROW_YUIOP
    in      a, (KEY_PORT)
    bit     1, a                ; O is bit 1
    jr      nz, .not_o
    ld      a, 3
    ld      (key_pressed), a
    ret
.not_o:
    ; Check P (right)
    ld      a, ROW_YUIOP
    in      a, (KEY_PORT)
    bit     0, a                ; P is bit 0
    jr      nz, .not_p
    ld      a, 4
    ld      (key_pressed), a
.not_p:
    ret

key_pressed:
    defb    0

Usage:

main_loop:
    halt                        ; Wait for frame
    call    read_keyboard
    ld      a, (key_pressed)
    or      a
    jr      z, main_loop        ; No key pressed

    cp      1
    jr      z, handle_up
    cp      2
    jr      z, handle_down
    ; ... etc
    jr      main_loop

Trade-offs

AspectCost
CPU~50 cycles per read
Memory~40 bytes
LimitationNo debouncing (keys may auto-repeat)

When to use: Any game needing keyboard input. ROM-free approach works on all Spectrums.

When to avoid: When you need debounced input for menus or single-press detection.

Keyboard Matrix Reference

PortBit 0Bit 1Bit 2Bit 3Bit 4
$F712345
$EF09876
$FBQWERT
$FDASDFG
$DFPOIUY
$BFEnterLKJH
$7FSpaceSymMNB
$FEShiftZXCV

Keyboard ghosting

The Spectrum's keyboard is a passive matrix — keys connect rows to columns. When three or more keys in the same row are held, the matrix returns ambiguous data. Worse, holding two keys in different rows can produce a "phantom" third key reading at the intersection point.

For games that need multi-key combos (e.g. "diagonal-up + fire"), pick keys that span different rows. The classic Spectrum control scheme — Q/A (up/down on different rows) + O/P (left/right on the same row) + Space (separate row) — distributes inputs across the matrix to minimise ghosting.

Reading multiple keys at once

The pattern above returns the first key it finds. To check all directions plus fire on the same frame, store each result rather than returning early:

read_all_keys:
    ld      a, 0
    ld      (key_state), a

    ; Check Q (up): row $FB, bit 0
    ld      a, $fb
    in      a, ($fe)
    bit     0, a
    jr      nz, .not_q
    ld      hl, key_state
    set     0, (hl)         ; Bit 0 of state = up
.not_q:
    ; ... continue for each key, setting different bits ...
    ret

key_state:  defb    0       ; Bit 0=up, 1=down, 2=left, 3=right, 4=fire

Patterns: Game Loop (HALT)

Vault: ULA — port $FE keyboard matrix | ZX Spectrum