Assembly Language Fundamentals

Advanced
Version:
1.0

Keyboard Matrix Scanning

Games require more than single key presses. Players must be able to:

The MCAL input routines (KEYIN, ZGETLIN, etc.) are not suitable for this. They block, pause execution, or return only a single key.

To achieve real-time control, you must read the keyboard matrix manually.
This is done using the AY-3-8910’s two I/O ports.

Before we examine the code, we will explain the matrix system and how it works on the Einstein.

1. The Keyboard Matrix Concept

The keyboard is arranged electrically as an 8×8 grid:

A full scan consists of:

  1. Selecting one row
  2. Reading the 8 columns
  3. Noting any bits that have gone low (0)
  4. Repeating for all 8 rows

Active-Low Operation

The Einstein keyboard is wired as active-low:

This is important: a pressed key shows up as a 0 bit in the column byte.

We will use this behaviour in both example programs.

2. The Role of the AY-3-8910

The Einstein uses the AY-3-8910 not only for sound, but also for keyboard I/O.

It has two ports:

These are accessed through I/O ports:

Configuring the I/O Ports

Before scanning, register 7 must be set so:

This is achieved with:

LD A,7
OUT (2),A
LD A,127
OUT (3),A

Bit 7 = 0 → Port A output
Bit 6 = 1 → Port B input

3. Selecting Rows

Rows are selected by writing a byte with exactly one 0 bit.

Example pattern:

RowByte (binary)Byte (decimal)011111110254111111101253211111011251311110111247411101111239511011111223610111111191701111111127

The example programs use RLC C to rotate the zero bit through all 8 positions.

4. Reading Columns

After selecting a row via Register 14, you select Register 15:

LD A,15
OUT (2),A
IN A,(2)

The value returned in A contains 8 bits:

Because each row can hold as many as 8 keys, you must read all 8 rows to know whether any key is pressed.

5. Which Keys Are Where?

The full key-to-row and key-to-column mapping is included in the appendix at the end of this module.

The appendix includes:

You will use this table frequently when designing controls.

For now, we will move on to the example programs.

6. Example Program 1 — Print the Keyboard Scan

This first program:

This is invaluable for debugging — you can hold keys and watch the pattern change.

Program Listing (unchanged):

ORG 256

; Set Up Input/Output Ports

   LD A,7
   OUT (2),A
   LD A,127
   OUT (3),A

; Begin scanning

   LD C,254                ; Load 11111110 Into register C, Selecting row 0
   LD HL,64485             ; Establish HL as address at memory space 64485

   LD E, 8                 ; Set Counter for eight rows

; Scan all keyboard rows

ScanLoop:
   LD A,14                 ; Select Register 14
   OUT (2),A               ; Send to Port 2
   LD A,C                  ; Load C, the binary for row 0, into A
   OUT (3),A               ; Send row 0 to Port 3
   LD A,15                 ; Select Register 15
   OUT (2),A               ; Send to Port 2

   IN A,(2)                ; Read values from Port 2 in to A (The key pressed as a byte value)
   LD (HL),A               ; Write value in A into HL

   CALL PrintBits          ; Print the byte to screen

   LD A, 13                ; New line
   RST 8
   DEFB 158
   LD A, 10
   RST 8
   DEFB 158

   INC HL                  ; Move HL to the next byte ready for next values
   DEC E
   LD A, E
   CP 0
   RET Z
   RLC C                   ; Rotate the C byte left so the new row is selected
   JR ScanLoop             ; Continue loop until the carry flag is not set

; A = value to print as binary

PrintBits:
   LD B,8                  ; 8 bits to print

BitLoop:
   RLC A                   ; rotate left, bit 7 -> carry
   LD D,A                  ; save current value
   LD A,'1'
   JR C,PrintIt
   LD A,'0'

PrintIt:
   RST 8
   DEFB 158
   LD A,D
   DJNZ BitLoop
   LD A,' '               ; space after the byte
   RST 8
   DEFB 158
   RET

RET

7. Example Program 2 — Movement with WASD

This program uses matrix scanning to control a sprite in Graphics Mode II.

It demonstrates:

Program:

ORG 256

; -----------------------
; Enable Graphics Mode II
; -----------------------

     ; Set Register 0 Values

     LD   A,2
     OUT  (9),A
     LD   A,128
     OUT  (9),A

     ; Set Register 1 Values (16x16 sprites)

     LD   A,226
     OUT  (9),A
     LD   A,129
     OUT  (9),A


; --------------------------
; Fore / Back Global Colours
; --------------------------

     LD   A, 240        ; 11110000 (white,black)
     OUT  (9), A
     LD   A, 135
     OUT  (9), A

; -----------------
; Write Blank Tiles
; -----------------

; Top Third Blank Tile

     LD   A, 0
     OUT  (9), A
     LD   A, 64
     OUT  (9), A

     LD A, 0
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A

; Middle Third Blank Tile

     LD   A, 0
     OUT  (9), A
     LD   A, 72
     OUT  (9), A

     LD A, 0
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A

; Bottom Third Blank Tile

     LD   A, 0
     OUT  (9), A
     LD   A, 80
     OUT  (9), A

     LD A, 0
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A
     OUT (8), A


; --------------------------------
; Clear Name Table with Blank Tile
; --------------------------------

     LD A, 0
     OUT (9), A
     LD A, 120
     OUT (9), A

     LD BC, 768         ; Set Counter for table address space

ClearLoop:
     LD A, 0
     OUT (8), A
     DEC BC
     LD A, B
     OR C
     JP NZ, ClearLoop

; ----------------------
; Define Sprite Patterns
; ----------------------

     LD A,0
     OUT (9),A
     LD A,88
     OUT (9),A

; Player Sprite

     LD A,%01111110 : OUT (8),A
     LD A,%10000001 : OUT (8),A
     LD A,%10111101 : OUT (8),A
     LD A,%10100101 : OUT (8),A
     LD A,%10100101 : OUT (8),A
     LD A,%10111101 : OUT (8),A
     LD A,%10000001 : OUT (8),A
     LD A,%01111110 : OUT (8),A


; --------------
; Main Game Loop
; --------------


MainLoop:

     CALL UpdatePlayerSprite
     CALL CheckKeys
     JP MainLoop

UpdatePlayerSprite:

     LD A,0
     OUT (9),A
     LD A,123
     OUT (9),A

; Player Sprite

     LD A,(PlayerSpriteY)
     OUT (8),A
     LD A,(PlayerSpriteX)
     OUT (8),A
     LD A,0
     OUT (8),A
     LD A,15
     OUT (8),A

     RET

CheckKeys:

   CALL Delay

   ; W Pressed
   LD C, 223               ; Move to row 5, 11011111
   CALL ReadRow
   BIT 5, A
   CALL Z, WPressed

   ; A Pressed
   LD C, 191               ; Move to row 6
   CALL ReadRow
   BIT 6, A
   CALL Z, APressed

   ; S Pressed
   CALL ReadRow
   BIT 5, A
   CALL Z, SPressed

   ; D Pressed
   CALL ReadRow
   BIT 4, A
   CALL Z, DPressed

RET

ReadRow:

   LD A,14                 ; Select Register 14
   OUT (2),A               ; Send to Port 2
   LD A,C                  ; Load C, the binary for row 5, into A
   OUT (3),A               ; Send row 5 to Port 3
   LD A,15                 ; Select Register 15
   OUT (2),A               ; Send to Port 2
   IN A,(2)                ; Read values from Port 2 in to A (The key pressed as a byte value)

RET

WPressed:

     LD A, (PlayerSpriteY)
     DEC A
     LD (PlayerSpriteY), A

     RET

APressed:

     LD A, (PlayerSpriteX)
     DEC A
     LD (PlayerSpriteX), A

     RET

SPressed:

     LD A, (PlayerSpriteY)
     INC A
     LD (PlayerSpriteY), A

     RET

DPressed:

     LD A, (PlayerSpriteX)
     INC A
     LD (PlayerSpriteX), A

     RET


Delay:
     LD BC,3000      ; adjust this number for smooth speed
DLoop:
     DEC BC
     LD A,B
     OR C
     JP NZ,DLoop
     RET

; ---------
; Variables
; ---------


PlayerSpriteY:
      DEFB 50
PlayerSpriteX:
      DEFB 50

      JP $            ; JP Idefinitely

(Your exact supplied listing will be placed here, unchanged.)

8. Exercises

  1. Modify Program 1 to continually scan until ESC is pressed.
  2. Add SPACE detection to Program 2 to perform an action (e.g., shooting).

9. APPENDIX — Complete Keyboard Matrix Reference

Previous Module
Next Module