Classic Computer Magazine Archive COMPUTE! ISSUE 89 / OCTOBER 1987 / PAGE 63

The Beginner's Page

C. Regena


Detecting Keypresses

Last month, this column discussed INPUT for interactive programming. INPUT receives whatever the user types before he or she presses the RETURN or ENTER key, whether it is one character, several lines of characters, function keys, cursor keys, or no other keys. The user can enter something that the computer is not expecting and cause the program to crash, act strangely, cause later errors, or even end unexpectedly. You can make your programs more user-friendly and limit the input by scanning the keyboard instead.

This method of interaction receives input by scanning the keyboard. The key pressed is not printed on the screen. This method is useful if you want your user to respond with just a one-key answer, such as in a yes or no situation; a multiple-choice or menu selection; or selecting a certain letter or number.

The command to retrieve a single keypress depends on your computer. The Commodore and Applesoft versions of BASIC both use GET. The versions of BASIC for the Amiga and IBM PC and compatibles use INKEY$. (Amiga and IBM BASICs also have a GET statement, but it has a different function in these versions and cannot be used to retrieve keypresses.) The versions of BASIC for the Atari ST and Atari eight-bit models have no built-in command explictly for reading a single keypress, but alternative techniques for these machines are discussed at the end of this article.

A sample program segment would be

200 PRINT "PRESS Y FOR YES OR N FOR NO"
210 K$ = INKEY$
220 IF K$ = "" THEN 210
230 IF K$ = "Y" THEN 500
240 IF K$< > "N" THEN 210
250 REM PROGRAM CONTINUES FOR N RESPONSE
500 REM PROGRAM CONTINUES FOR Y RESPONSE

Line 200 prints a message so the user will know he or she needs to do something. Line 210 assigns the value of whatever key is pressed to the variable K$. The line works as shown for the IBM and Amiga. If you have a Commodore or Apple, replace line 210 with

210 GET K$

Line 220 is used in case no key is pressed. It is necessary because INKEY$ and the Commodore version of GET do not wait for a keypress. If no key is being pressed, these commands return a null string, "". Line 220 detects this and keeps the program in a loop until a key is pressed. Such loops are often used in conjunction with INKEY$ or GET. Note, however, that the Applesoft version of GET does wait for a keypress. Thus, Apple users can omit line 220. The GETKEY statement in BASIC 7.0 for the Commodore 128 has the same effect—it waits until a key is pressed, eliminating the need to test for a null string.

When a key is pressed, line 230 shows that if the key pressed is a Y, the program should pass control (branch) to line 500 and continue. Line 240 checks for the N key. If the key pressed is not N, then the computer branches back to line 210 to wait for another key to be pressed. If an N is pressed, the program continues. Notice that this prevents user error by accepting only Y or N and ignoring any other keypress.

You don't have to limit your input choices to Y and N. With appropriate IF-THEN tests you could look for any key, or set up an entire menu of single-letter choices.

This method is also often used to create a pause in a program until a certain key is pressed. For example, to allow a user to read a screen of instructions at his or her own speed, you can have the program wait until the space bar is pressed:

200 PRINT "PRESS THE SPACE BAR TO CONTINUE."
210 S$ = INKEY$
220 IF S$< >" " THEN 210

(This and the following examples all use INKEY$. If you're using a Commodore or Apple, remember to replace that statement with an appropriate GET.)

Line 220 contains a space between quotation marks. If the value of S$ is not a space, then the program branches back to line 210.

Instead of specifying a particular key, you can allow the user to press any key to continue:

200 PRINT "PRESS ANY KEY TO CONTINUE"
210 C$ = INKEY$:IF C$ = "" THEN 210

The IF-THEN statement in line 210 keeps the computer in a loop until a key is pressed. Note that there is nothing between the quotation marks in that line. Since the Applesoft version of GET and the Commodore 128's GETKEY wait for a key to be pressed, the IF test can be omitted if those statements are used.

Some keys that you may want to detect cannot be printed between quotation marks in a program. Examples include the RETURN key and the function keys. You can use CHR$ to check for any valid character. For example, CHR$(13) is the same as the RETURN or ENTER key. Look in your computer manual to find out the ASCII codes for the cursor keys or function keys. Here is an example using CHR$:

200 PRINT "PRESS RETURN TO CONTINUE."
210 R$ = INKEY$:IF R$ = "" THEN 210
220 IF R$< >CHR$(13) THEN 210

Another method is to check the ASCII code of the key pressed:

300 PRINT "PRESS RETURN TO CONTINUE."310 R$ = INKEY$:IF R$ = "" THEN 310
320 IF ASC(R$)< >13 THEN 310

Note that the IF-THEN statement in line 310 is necessary (except on the Apple) because the ASC function in line 320 causes an error when asked to return the ASCII value of a null string.

INKEY$ always returns a string character. You may choose any string variable name. GET can retrieve either a string or numeric value; you select which one by the variable type you use following the GET statement. For example, a statement like GET A$ returns a single-character string, while one like GET A returns a single-digit number(0–9). If you were to type a 1, GET A$ would return the character 1, equivalent to CHR$(49), while GET A would return the numeric value 1. The string form is much preferred because the numeric form will stop the program with an error message if any key other than 0–9 is pressed.

If you really want a number rather than a string, you can use the VAL() function to convert the string to a number. Here is an example: Suppose you have printed a menu screen with four choices, numbered 1–4. The user needs to press 1, 2, 3, or 4. All other keys are to be ignored.

400 N$ = INKEY$:IF N$ = "" THEN 400
410 IF N$<"1" OR N$>"4" THEN 400
420 ON VAL(N$) GOTO 1000, 2000, 3000, 4000

When testing characters with IF-THEN statements, you should note that the case of the character is significant. The computer does not recognize Y and y as being equivalent, nor N and n. Your IF-THEN statements must be more complex to check for both uppercase and lowercase letters. For example:

400 PRINT "PRESS Y OR YES OR N OR NO."
410 A$ = INKEY$:IF A$ = "" THEN 410
420 IF A$ = "Y" OR A$ = "y" THEN 1000
430 IF A$< >"N" AND A$< >"n" THEN 410
440 REM PROGRAM CONTINUES FOR N RESPONSE

If you have an Amiga, Amiga Basic has a handy soluction—the UCASE$ function. This converts any specified string to all uppercase characters. For example, the following lines retrieve a character C$ and insure that it will be uppercase. Any following IF-THEN statements need only check for uppercase characters:

PICK:
C$ = INKEY$:IF C$ = "" THEN PICK
C$ = UCASE$(C$)

Scanning The Keyboard Buffer

Another fact that you should be aware of in scanning the keyboard is that some computers have a keyboard buffer. When the computer detects a keypress, it stores the key value in memory until it is called for (up to a maximum of 10–15 keypresses, depending on the computer). Thus, keys pressed when a program is not looking for input may be retrieved by subsequent INKEY$ or GET commands.

It is wise to clear the buffer before you use any INKEY$ statement. Some computers have a POKE command to clear the keyboard buffer. (For example, POKE 198,0 does the job on the Commodore 64). Another method—if you know how many keypresses the buffer can hold—is to use a FORNEXT loop. For example, the IBM PC keyboard buffer holds 15 key-presses:

50 FOR K = 1 TO 15: A$ = INKEY$:NEXT K
60 PRINT "PRESS ANY KEY TO CONTINUE"
70 A$ = INKEY$:IF A$ = "" THEN 70

Alternative Tactics

As mentioned earlier, the version of BASIC for Atari's eight-bit computers has no built-in statement designed specifically to retrieve a single keypress. However, you can achieve the same effect by first opening a channel to the keyboard device with the statement OPEN #1, 4, 0, "K:", then using GET#1 to retrieve character values. (Any unused file number in the range 1–7 can be substituted for the 1 in these statements). The main difference between the Atari's GET#, and the INPUT$ and GET commands described above, is that GET# returns character code values rather than string characters, so GET# must always be followed by a numeric variable. If characters are desired, the CHR$ function can be used. Also, note that GET# waits for a key to be pressed.

To modify the first example program segment in this article for eight-bit Ataris, you must first add lines to open the channel for input and set up the testing variable:

100 OPEN #!, 4, 0, "K"
110 DIM K$(1)

This needs to be done only once in the program, but it must be done before the first GET# command. Then replace line 210 with:

210 GET #1, K : K$ = CHR$(K)

You may find it easier to simply check for character code values rather than characters. Refer to your BASIC manual for a complete list of Atari ASCII codes. For example, you could test for a RETURN keypress with

300 PRINT "PRESS RETURN TO CONTINUE."
310 GET#1, R:IF R< >155 THEN 310

ST BASIC, for the Atari STs, likewise has no practical command for retrieving a single keypress. (There is an INPUT$ function, but it is rather unwieldly.) Fortunately, you can achieve the same effect using the INP function. This function retrieves a value from one of the computer's internal input/output (I/O) ports. The ST's keyboard is actually an intelligent peripheral with its own microprocessor. It returns keypress information to the computer through port 2. Thus, INP(2) returns the ASCII value of the next key pressed. (The function will wait until a key is pressed.) A statement like K = INP(2) will assign the ASCII code of the next key pressed to the variable K. If you want characters instead of ASCII values, you can duplicate the INPUT$ function with a statement like K$ = CHR$(INP(2)).

The following program segment illustrates one way to read the Atari's function keys:

400 print "Press any function key:"
410 k = inp(2)
420 if k<187 or k>196 then print "That's not a function key!" :goto 410
430 print "You pressed the F"; K—86;"key."