Scanning the Keyboard and Joystick
The keyboard and joysticks are scanned using the AY-3-8912 PSG and the 8255 PPI.
It is necessary that you understand how to use these before you continue to read this document.
The keys on the keyboard, and the joystick directions and buttons, are arranged in an 10 by 8 matrix. Each element of the matrix is a switch and represents the state of a key or a joystick button.
The matrix is read a column at a time, each column is read as a byte, and this holds the state of 8 switches.
When the matrix is read, a switch will be "0" or "1".
- If the switch is "0" then the key/joystick button corresponding to that bit is pressed,
- If the switch is "1" then the key/joystick button is not pressed,
Joystick 0 occupies it's own space in the matrix and is accessed at line 9. Joystick 1 shares line 6 with the keyboard. As a result, it is possible to simulate the state of joystick 1 by pressing the appropiate keys on the keyboard.
The matrix
The position of each key/joystick button in the matrix is shown in the table below.
| Bit | Line | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
| 7 | f. | f0 | Ctrl | > , | < . | Space | V | X | Z | Del |
| 6 | Enter | f2 | ` \ | ? / | M | N | B | C | Caps Lock | Unused |
| 5 | f3 | f1 | Shift | * : | K | J | F / Joy 1 Fire 1 | D | A | Joy 0 Fire 1 |
| 4 | f6 | f5 | f4 | + ; | L | H | G / Joy 1 Fire 2 | S | Tab | Joy 0 Fire 2 |
| 3 | f9 | f8 | } ] | P | I | Y | T / Joy 1 Right | W | Q | Joy 0 Right |
| 2 | Cursor Down | f7 | Return | | @ | O | U | R / Joy 1 Left | E | Esc | Joy 0 Left |
| 1 | Cursor Right | Copy | { [ | = - | ) 9 | ' 7 | % 5 / Joy 1 Down | # 3 | " 2 | Joy 0 Down |
| 0 | Cursor Up | Cursor Left | Clr | £ ^ | _ 0 | ( 8 | & 6 / Joy 1 Up | $ 4 | ! 1 | Joy 0 Up |
Note:
- Bit 6 on lines 9 and 6, may be used to report a third fire button on a joystick. This bit is also used as the middle button on an AMX compatible mouse.
- For CPC464 and CPC664 users: Those keys prefixed with an "f" are located on the numeric keypad.
- "f." is the "." key on the numeric keypad.
- For CPC464 and CPC664 users:
- ENTER is the small ENTER key,
- RETURN is the large ENTER key
- If matrix line 11-14 are selected, the byte is always &ff. After testing on a real CPC, it is found that these never change, they always return &FF.
- Watch out for KEYBOARD CLASH!
Algorithm for reading the keyboard and joysticks
The matrix is connected to PSG I/O port A, and PPI Port C. (PSG Port A is accessed through PSG register 14).
Bits 3..0 of PPI Port C are used to define the matrix line to read. The data of the selected matrix line will be present at the inputs to PSG Port A.
The PSG is accessed through PPI Port A and PPI Port C. PPI port A is a databus. PPI Port C bit 7 and 6 are used to define the PSG access function:
PSG access function selection:
| PPI Port C Bit | PSG Function | Notes | |
|---|---|---|---|
| 7 | 6 | ||
| 0 | 0 | Inactive | |
| 0 | 1 | Read from selected PSG register. | The data from the PSG register will be present at PPI port A. |
| 1 | 0 | Write to selected PSG register. | The data at PPI port A defines the data to write to the PSG register. |
| 1 | 1 | Select PSG register. | The data at PPI port A defines the PSG register index. |
To read the keyboard and joystick is a long task.
The following sequence is required to read one or more matrix lines:
- Write 14 to PPI Port A, (This is the index of the I/O Port A register of the PSG)
- Select PSG operation: write register index, by setting bit 7="1" and bit 6="1" of PPI port C.
(At this stage the PSG will have selected the register specified by the data at PPI Port A).
- Select PSG operation: inactive, by setting bit 7="0" and bit 6="0" of PPI Port C.
(This stage is required by the CPC+. If it is missing the scanning operation will fail)
- Set Port A of the PPI to input (use PPI Control register),
(At this stage it is possible to read from PSG register 14)
- Write matrix line into bit 3-0 of PPI Port C.
(At this stage the matrix line data will be presented to PSG register 14).
- Select PSG operation: read register data, by setting bit 7="0 and bit 6="1" of PPI Port C.
(At this stage the matrix line data will be presented to PPI port A)
- Read matrix data from PPI port A.
- If more lines are to be read go to step 5,
- Set Port A of the PPI to output (use PPI Control register),
(At this stage it is possible to write data to the PSG. This algorithm assumes that port A is in this state when it starts)
- Select PSG operation: inactive, by setting bit 7="0" and bit 6="0" of PPI Port C.
(This stage is required by the CPC+. If it is missing the scanning operation will fail)
Commented dissassembly of the system function to scan the keyboard and joysticks: (see examples at the bottom) h2>Keyboard clash
There is a problem with the matrix which is called "Keyboard clash".
If you press more than one key at the same time, another key (a "ghost" key), which has not been pressed, will often be reported. (The rules explaining when this problem occurs are shown below).
This problem is more obvious in two player games, because more buttons will be pressed together at the same time.
The solution is to choose a combination of keys/buttons which will not cause this problem when they are pressed together.
The problem will only occur in the following circumstances:
Using a coordinate system to identify a unique matrix line and bit within that line, (linex, bity), where x is a number between 0 and 10 and y is a number between 0 and 7, the following is observed:
- if (linex1,bity1) is pressed AND (linex2,bity1) is pressed AND (linex2,bity2) is pressed THEN (linex1,bity2) will be reported as pressed.
- OR: if (linex1,bity1) is pressed AND (linex1,bity2) is pressed AND (linex2,bity1) is pressed THEN (linex2,bity2) will be reported as pressed.
- OR: if (linex1,bity1) is pressed AND (linex1,bity2) is pressed AND (linex2,bity2) is pressed THEN (linex2,bity1) will be reported as pressed.
- OR: if (linex1,bity2) is pressed AND (linex2,bity1) is pressed AND (linex2,bity2) is pressed THEN (linex1,bity1) will be reported as pressed.
e.g.
- if C,W and N are pressed then Y is reported as pressed.
- if K,J and Y are pressed then I is reported as pressed.
If many keys are pressed then many "ghost" keys will be reported as pressed following the rules above.
This problem occurs with any combination of keys/joystick buttons pressed in this way.
Interestingly, keyboards on other computers also have keyboard clash, even some PC keyboards. In more modern computers, the keyboard is controlled by a special keyboard processor and this can detect "ghost" keys and prevent them from being reported.
Example - Highlighted
;; This example shows the correct method to read the keyboard and
;; joysticks on the CPC, CPC+ and KC Compact.
;;
;; This source is compatible with the CPC+.
;;
;; The following is assumed before executing of this algorithm:
;; - I/O port A of the PSG is set to input,
;; - PPI Port A is set to output
.read_matrix
ld hl,matrix_buffer ; buffer to store matrix data
ld bc,&f40e ; write PSG register index (14) to PPI port A (databus to PSG)
out (c),c
ld b,&f6
in a,(c)
and &30
ld c,a
or &C0 ; bit 7=bit 6=1 (PSG operation: write register index)
out (c),a ; set PSG operation -> select PSG register 14
;; at this point PSG will have register 14 selected.
;; any read/write operation to the PSG will act on this register.
out (c),c ; bit 7=bit 6=0 (PSG operation: inactive)
inc b
ld a,&92
out (c),a ; write PPI control: port A: input, port B: input, port C upper: output
; port C lower: output
push bc
set 6,c ; bit 7=0, bit 6=1 (PSG operation: read register data)
.scan_key
ld b,&f6
out (c),c ;set matrix line & set PSG operation
ld b,&f4 ;PPI port A (databus to/from PSG)
in a,(c) ;get matrix line data from PSG register 14
cpl ;invert data: 1->0, 0->1
;if a key/joystick button is pressed bit will be "1"
;keys that are not pressed will be "0"
ld (hl),a ;write line data to buffer
inc hl ;update position in buffer
inc c ;update line
ld a,c
and &0f
cp &0a ;scanned all rows?
jr nz,scan_key ;no loop and get next row
;; scanned all rows
pop bc
ld a,&82 ;write PPI Control: Port A: Output, Port B: Input, Port C upper: output, Port C lower: output.
out (c),a
dec b
out (c),c ;set PSG operation: bit7=0, bit 6=0 (PSG operation: inactive)
ret
.matrix_buffer
defs 16
Example - Original
;; This example shows the correct method to read the keyboard and
;; joysticks on the CPC, CPC+ and KC Compact.
;;
;; This source is compatible with the CPC+.
;;
;; The following is assumed before executing of this algorithm:
;; - I/O port A of the PSG is set to input,
;; - PPI Port A is set to output
;;
;;--------------------------------------------------------------------------------
;; example code showing how to use read_matrix.
org &4000
nolist
.main_loop
;; wait for vsync
ld b,&f5
.v1 in a,(c)
rra
jr nc,v1
call read_matrix
;; NOTE: Consult the 'matrix' table in the
;; document 'Scanning the Keyboard & Joysticks' to find the keyboard line
;; and bit for the keys you want to check.
;; test the 'return' key has been pressed.
;; (line 2, bit 2).
ld a,(matrix_buffer+2)
bit 2,a
jr z,not_pressed
;; return key pressed
.not_pressed
jp main_loop
;;--------------------------------------------------------------------------------
.read_matrix
ld hl,matrix_buffer ; buffer to store matrix data
ld bc,&f40e ; write PSG register index (14) to PPI port A (databus to PSG)
out (c),c
ld b,&f6
in a,(C)
and &30
ld c,A
or &C0 ; bit 7=bit 6=1 (PSG operation: write register index)
out (c),a ; set PSG operation -> select PSG register 14
;; at this point PSG will have register 14 selected.
;; any read/write operation to the PSG will act on this register.
out (c),c ; bit 7=bit 6=0 (PSG operation: inactive)
inc b
ld a,&92
out (c),a ; write PPI control: port A: input, port B: input, port C upper: output
; port C lower: output
push bc
set 6,c ; bit 7=0, bit 6=1 (PSG operation: read register data)
.scan_key
ld b,&f6
out (c),c ;set matrix line & set PSG operation
ld b,&f4 ;PPI port A (databus to/from PSG)
in a,(c) ;get matrix line data from PSG register 14
cpl ;invert data: 1->0, 0->1
;if a key/joystick button is pressed bit will be "1"
;keys that are not pressed will be "0"
ld (hl),a ;write line data to buffer
inc hl ;update position in buffer
inc c ;update line
ld a,c
and &0f
cp &0a ;scanned all rows?
jr nz,scan_key ;no loop and get next row
;; scanned all rows
pop bc
ld a,&82 ;write PPI Control: Port A: Output, Port B: Input, Port C upper: output, Port C lower: output.
out (c),a
dec b
out (c),c ;set PSG operation: bit7=0, bit 6=0 (PSG operation: inactive)
ret
;; This buffer has one byte per keyboard line.
;; Each byte defines a single keyboard line, which defines
;; the state for up to 8 keys.
;;
;; A bit in a byte will be '1' if the corresponding key
;; is pressed, '0' if the key is not pressed.
.matrix_buffer
defs 10








