The Spectrum’s Key I/O Port

The ZX Spectrum is minimalistic, relying on just a few I/O ports to manage all peripherals (keyboard, display, cassette, and beeper).

The Main Port: The most crucial I/O address is `FEH′ (254 decimal), which is used for both input and output control.

I/O Port Decoding (The Trick)

The Spectrum simplifies hardware design by only connecting I/O logic to the lowest bit (A0) and the highest byte (A8-A15) of the Z80’s 16-bit address lines.

The Rule: Any I/O instruction that has an even number in the low 8 bits of the port address (i.e., A0=0) is seen by the core hardware. This means the Spectrum responds to IN A, (FEH), IN A, (FCH), and many other addresses. However, all hardware is wired to the same port logic.

The Keyboard Scan Routine

The Spectrum keyboard is arranged as an 8 × 5 matrix. The CPU must strobe (output a signal to) the 8 rows and then read the 5 columns.

Step 1: Strobe the Row (Output) To check a row of keys, the CPU writes a mask to port `FEH′. The bits set to ‘0’ select the target row.

KEYBOARD_PORT EQU 0FEH

LD   A, 0EFH          ; Mask for Row 1 (Bits V, G, F, D, S, A, Caps Shift, 1)
OUT  (KEYBOARD_PORT), A ; Output the strobe mask

Step 2: Read the Columns (Input) The same port (`FEH′) is then used for input. The lowest 5 bits of the input byte indicate which keys in the selected row are pressed.

    IN   A, (KEYBOARD_PORT) ; Read the key status into the Accumulator (A)
    
    ; Bits 0-4 contain key status (0 = key pressed)
    AND  01FH               ; Mask off the high bits (optional, but good practice)

The Full Scan and Key Buffer

The full keyboard routine must loop through all 8 rows, strobing and reading each one sequentially. The results of the 8 reads are stored in an 8-byte key buffer in RAM (e.g., at `5C00H′), which the main program checks before the next screen refresh.

Check for Key Press: A key is pressed if the corresponding bit in the buffer is low (0).

Example: Checking the ‘SPACE’ Key (Assumes ‘SPACE’ is in Row 7, Bit 0)

    LD   HL, KEY_BUFFER + 7 ; HL ← Address of Row 7's status byte
    LD   A, (HL)
    BIT  0, A               ; Check Bit 0 (the SPACE key)
    JP   Z, SPACE_PRESSED   ; Z=1 means the bit is 0 (key is pressed)