Classic Computer Magazine Archive COMPUTE! ISSUE 80 / JANUARY 1987 / PAGE 92

INSIGHT: Atari

Bill Wilkinson


Controlling Keyboard Input

One of the most powerful features of the BASIC language is the INPUT statement. Consider: This single statement allows you to ask the user for numbers or strings and allows the user to do full-screen editing as he or she enters data. Yet all this power has its price. The INPUT statement is extremely vulnerable and can easily cause programs to crash.
    For example, if your program is expecting a number and the user types a string-KABLOOEY. Ad mittedly, you can (and should) TRAP this kind of error. But what about the user who delights in using the cursor-control keys to move all over the screen? Or the one who hits the CLEAR key after your program has gone to the trouble to put 20 lines of information on the screen? For a truly professional-looking program, you probably want the capability to restrict data entry to only those characters which you are expecting. And the INPUT statement just won't work for this.

BASIC Has The Answer
Fear not; good old BASIC has another answer. Most BASICS provide a way to get a single key from the keyboard, and Atari BASIC is no exception. You simply OPEN the K: device on some channel, and then GET characters (actually, bytes) one at a time from that channel. Since the characters you get this way are not even echoed on the screen, you have a chance to filter the user's keystrokes and ignore or alter those you don't want. For example, if you want only digits (for a numeric input), you could ignore all non-numeric characters.
    Sidelight: This type of problem is not unique to BASIC programmers. Any programmer using a language which accepts input from a screen editor as is built into the Atari eight-bit machines will have to decide whether to go to the trouble to use methods similar to those I am about to describe.
    The program here is an example which will help familiarize you with restricted keyboard input. Let's take a closer look at some of its inner workings.
    First, because we're using Atari BASIC, I have violated my own rules and placed the major subrou tine near the start of the program, with the mainline code following. I did this because this major subroutine is very speed-critical, and every little thing that can be done to make it run faster is a help. Anyway, line 1030 immediately sends us to the main code, which starts at line 1400. Notice the OPEN of the keyboard device in line 1430 and the allocation of some strings in line 1440.
    Then, after clearing the screen, we begin the main loop of this little example. In line 1500 we simply READ from some DATA statements to find the position, size, and type of a field on the screen which is to receive our ministrations. We have two special cases to take care of here: First, if the type code is an asterisk, we have exhausted our DATA. (In a larger, real-world program, this would probably indicate that it's time to save the contents of our various fields to disk, and so on.) Second, if the length code is zero, the string which is the last item in each DATA statement is actually information to be displayed on the screen, so this particular DATA statement does not cause any input processing (see lines 1530 and 1540).
    Assuming that we do have a field which requires formatted input processing, line 1520 causes the main subroutine at line 1100 to be called. This subroutine (in line 1100) displays a line of dots on the screen which is intended to tell the user the maximum size of the data he or she is supposed to input. Line 1110 is a bit of a trick: Since Atari BASIC does not allow us to PUT to channel 0 (the screen), we cheat and use nonexistent channel 16, which just happens to be translated by BASIC and the OS into (you guessed it) channel 0. And the two PUTS cause a cursor-right and then cursor-left movement to take place. (We do this because the POSITION statement does not actually move the cursor-the OS waits for a subsequent character output before moving it. These cursor movements get the cursor to the right location without actually changing the display.)

Special Cases
The CNT variable simply counts the characters we have passed so far. It can never be less than zero or more than the maximum length of the field we are currently working on. Then, within the loop, we get a single keystroke. Lines 1150 and 1160 combine to cause either an ESCape key or a RETURN key to force an exit from our formatted input routine. And line 1170 takes care of the special case of the backspace character (check any Atari BASIC or OS reference book to see which ATASCII codes normally perform various editing functions).
    Finally, in line 1180 (where we convert lowercase letters into uppercase ones-certainly an optional process), we begin to start our testing. This rather simplistic example program provides for only three types of fields: All alphabetic fields (designated by an A type code), all numeric fields (designated by an N type code), and "everything goes" fields (designated by the E type code). Lines 1190 and 1200 validate the A and N types, respectively.
    Note that we also restrict the number of characters to the maximum (line 1210-and PUT #16,253 simply sounds the bell on your Atari). If all is going well, we add the character the user typed to our collected field (line 1230) and go back for another character. Lines 1250-1290 are used to handle a backspace key. You should play with the PUTS a bit to figure out what they are doing. I will mention, however, that line 1260 serves to reduce the size of FIELD$ by a character.
    And that's about it. You're welcome to type in this program and try it, but don't expect it to do much. It is intended solely to get you started in using formatted input, so it doesn't demonstrate what you can do with this nice formatted data once you have gotten it. (For example, once you have gotten a numeric-only field entered into FIELD$, how do you convert it to a number? Take a look at the VAL function.)
    Also, the very simplistic nature of my three field types (A, N, and E) means that some desirable features are missing. For example, try typing in a name containing a space for that first NAME field. Or try entering a decimal point as part of the ZIP CODE. To be really flexible, this program should handle a dozen or so different data-entry formats. But now the sad truth comes out.
    Atari BASIC is just too slow to do anything really fancy in the formatted entry subroutine. I have some much more exotic versions of this program written in BASIC XL and BASIC XE, but with Atari BASIC they tend to bog down way too soon. Still, for a particular program you should be able to develop four or five different types to be handled, and still maintain reasonable speed. And that is probably adequate.
    So play with this program and these concepts; improve it and add features. I'll even give you a few hints on directions to take. For example, what happens when you add this line?

1165 IF KEY=125 THEN 1100

    Or how about "normalizing" a numeric input which includes a possible decimal point? For example, suppose you have a dollars-and-cents field. What should your program do if the user enters just dollars, with no decimal point and no cents? Or suppose the user enters three or more digits after the decimal point-what should you do?

Formatted Screen Data Entry

KF 1000 REM
NM 1010 REM PROGRAM TO SHOW
        PROTECTED INPUT
KH 1020 REM
BK 1030 GOTO 1400:REM (TO MA
        KE SUBROUTINES FASTE
        R)
KJ 1040 REM
BJ 1050 REM MAIN SUBROUTINE:
LM 1060 REM .  X,Y ARE SCREE
        N POSITION OF FIELD
JL 1070 REM .  L IS MAXIMUM
        LENGTH OF FIELD
AN 1080 REM .  TYPE$ IS ONE
        CHARACTER FIELD TYPE
         CODE
KO 1090 REM
FL 1100 POSITION X,Y:PRINT F
        ILL$(1,L);
KO 1110 POSITION X,Y:PUT #16
        ,31:PUT #16,30
FJ 1120 CNT=0:FIELDS=""
FN 1130 REM MAJOR LOOP
AP 1140 GET #1 ,KEY
BN 1150 IF KEY>127 THEN KEY=
        KEY-128
PF 1160 IF KEY=27 THEN RETUR
        N
A0 1170 IF KEY=126 THEN 1250
DA 1180 IF KEY>96 THEN KEY=K
        EY-32:REM (LOWER CAS
        E GOES TO UPPER)
LB 1190 IF TYPE$="A" THEN IF
         KEY<65 OR KEY>90 TH
        EN PUT #16,253:GOTO
        1130
LK 1200 IF TYPE$="N" THEN IF
         KEY<48 OR KEY>57 TH
        EN PUT #16,253:GOTO
        1130
KP 1210 IF CNT>=L THEN PUT #
        16,253:GOTO 1130
FN 1220 PUT #16,KEY
JJ 1230 CNT=CNT+1:FIELD$(CNT
        )=CHR$(KEY)
MF 1240 GOTO 1130
FJ 1250 IF CNT=0 THEN PUT #1
        6,253:GOTO 1130
AI 1260 FIELD$(CNT)=""
AB 1270 PUT 1116,KEY:CNT=CNT-
        1
DA 1280 PUT #16,46:PUT #16,3
        0
MK 1290 GOTO 1130
KI 1300 REM
NK 1310 REM THE MAIN CODE
KK 1320 REM
FP 1330 REM IN THIS SAMPLE P
        ROGRAM, WE DEFINE TH
        E FIELDS
KO 1340 REM VIA DATA STATEME
        NTS
KN 1350 REM
LP 1360 REM IN A MORE COMPLE
        X PROGRAM, THE INFO
        MIGHT COME
EE 1370 REM FROM A FILE
LA 1380 REM
LB 1390 REM
KJ 1400 REM
EA 1410 REM === INITIALIZATI
        ON ===
KL 1420 REM
PP 1430 OPEN #1,4,0,"K:"
CA 1440 DIM FIELD$(40),TYPE$
        (1),FILL$(40)
MG 1450 FILL$=".............
        ....................
        ......."
EM 1460 GRAPHICS 0
LA 1470 REM
IC 1480 REM NOW A SIMPLE LOO
        P TO GET DATA FOR FI
        ELDS ON SCREEN
LC 1490 REM
FA 1500 READ X,Y,L,FIELD$:TY
        PE$=FIELD$
EL 1510 IF TYPE$="*" THEN PO
        SITION 2,20:STOP
PH 1520 IF L<>0 THEN GOSUB 1
        100:6070 1500
KF 1530 POSITION X,Y:PRINT F
        IELD$;
MJ 1540 GOTO 1500
KP 1550 REM
AA 1560 REM IN A REAL PROGRA
        M, THE DATA FROM THE
GN 1570 REM FIELDS WOULD NOW
         BE MANIPULATED IN S
        OME
ND 1580 REM WAY (PERHAPS PLA
        CED IN A DISK FILE)
LD 1590 REM ================
        ==========
NN 1600 REM
KM 1610 REM
IA 1620 REM DATA TO DEFINE T
        HE FIELDS FOLLOW
KO 1630 REM
CD 1640 DATA 7,2,0,NAME
AP 1650 DATA 3,4,0,ZIP CODE
HI 1660 DATA 2,7,0,MISCELLAN
        EOUS COMMENTS:
KE 1670 DATA 12,2,20,A
IN 1680 DATA 12,4,5,N
IC 1690 DATA 4,8,30,E
KE 1700 DATA 7,15,0,=== THAT
        'S ALL FOLKS ===
C81710 DATA 0,0,0,*