[net.micro.cbm] id AA16172; Fri, 24 Feb 84 06:50:10 pst

lipman@decwrl.UUCP (02/24/84)

Message-Id: <8402241450.AA16172@decwrl.ARPA>
Date: Friday, 24 Feb 1984 06:47:37-PST
From: vogon::goodenough  (speling courtesy of clapped out VT100)
To: net.micro.cbm
Subject: C64 keyboard matrix exposed

The on-board keyboard routines for the Commodore 64 have one major drawback:
it is only possible to detect one key pressed at a time.  Also the algorithm
used is very inefficient - each time any key is held pressed, ALL 64 keys are
scanned 60 times a second at interrupt level.  If you are writing a real-time
application in machine code, this overhead can be intolerable.  Also for many
applications, you want to allow more than one key to be pressed at any one
time.  With the correct information, reading the keyboard is kids stuff.
Commodore don't give us that, but I've tracked it down.  Here goes.

First of all: how the hardware works.  The 64 keys are divided into a matrix
of 8 columns and 8 rows.  You write the column select bit to $DC00, then read
the 8 keys in that column from $DC01.  [You'll instantly recognize these
addresses as our old friends the games ports, which the logic shares.  A
joystick in games port 1 (nearest the space bar) will logically OR with 5 of
the keys (N,S,E,W and Fire) - more later].  It's important to note that it's
inverse logic: a zero bit output will select that column, and a key pressed
will return a zero.  If two keys are pressed in one column, two of the bits
will be zero.  If you write more than one zero bit, you'll OR together the
keys pressed in the selected columns.

That's all there is to the hardware - now the software.

First a quick check to see if any keys at all are pressed:

		LDA	#0
		STA	$DC00	; select ALL columns
		LDA	$DC01	; read everything
		CMP	#$FF	; any key pressed?
		BEQ	ALDONE	; br if no key pressed

Now it's up to you - select the column(s) you want, and read the keys in
that (those) columns.  If you only need to use a few keys in your application,
then it's most efficient to cluster them in one or two columns.

Very important - you have to filter out contact bounce.  This is achieved by:

	BOUNCE	LDA	$DC01	; read keys
		CMP	$DC01	; check them again
		BNE	BOUNCE	; loop till no bounce

Don't forget - a zero means TRUE, so to select column 0, you write $FE to
$DC00.  If F7 (function key) and Cursor Down are pressed, you'll read $EE
from $DC01.  Here's the table:

   \ROW		0	1	2	3	4	5	6	7
    \     -----------------------------------------------------------------
COL	0 |  INST/DEL  RET   CRSR R	F7	F1	F3	F5   CRSR D
	  |
	1 |	3	W	A	4	Z	S	E    SHFT L
	  |
	2 |	5	R	D	6	C	F	T	X
	  |
	3 |	7	Y	G	8	B	H	U	V
	  |
	4 |	9	I	J	0	M	K	O	N
	  |
	5 |	+	P	L	-	.	:	@	,
	  |
	6 |   POUND	*	;   CLR/HOME  SHFT R	=	^	/
	  |
	7 |	1    L ARROW  CTRL	Z     SPACE    CMDR	Q    RUN/STOP
	  -------------------------------------------------------------------
Joystick 1|	N	S	W	E     FIRE
	  -------------------------------------------------------------------

NOTE: The joystick is independent of which column you select.  It will appear
      in all columns.  The way the C64 scan works, it takes the last character
      in the last row it scans, which is column 7.  For more than one joystick
      bit zero (selected), it will take the rightmost character in the table
      (highest bit).  This is an expansion of what I've said in earlier
      contributions; now I've taken the trouble to find out exactly what does
      happen!  [Note - joystick in rear port has no effect here, since $DC00
      is write only for keyboard]

Jeff.

PS. To kill the C64 keyboard scan, you'll have to intercept the IRQ routine
    and call UDTIM yourself if you need that. If you don't intercept, you must
    disable interrupts between writing $DC00 and reading $DC01.  If you want
    me to post details of this, let me know.

	UUCP:		... decvax!decwrl!rhea!vogon!goodenough
			...allegra!decwrl!rhea!vogon!goodenough
			... ucbvax!decwrl!rhea!vogon!goodenough

	ARPA:		decwrl!rhea!vogon!goodenough@Berkeley
			decwrl!rhea!vogon!goodenough@SU-Shasta