1
Programming Hints
Reading the Keyboard Codes
Orson Scott Card
By Reading the Atari keyboard directly, you can get almost any key to perform like a function key—without changing any of the regular uses of the keyboard.

Whenever you press a key on your Atari keyboard, a number is stored at location 53769 in memory and, in most cases, in a shadow register at location 764. That number is the keyboard code for the key, or combination of keys, you pressed./

Unfortunately, that number has no relation at all either to ATASCII character code or to the Atari's internal character code. So most programmers ignore the keyboard code (KEYCODE) and let the operating system translate the keyboard code into ATASCII form.

You can use the KEYCODE, however, to get some interesting results:

Speed. Picking up the keyboard code at 53769 or 764 can save you time, especially when you're working in machine language. For one thing, you completely short-circuit the "debounce" routine that makes the computer wait for a while before repeating a key that is being held down continuously. If the key is down, it's down, and you can read the value at once. That can be a disadvantage if you have a touch-typing program, but it can be a great help if you want instant repetition of a key.

Customization. You can set up your computer, with software, to read the keys any way you like. This article, for instance, includes a program to make your computer read the keyboard according to Dvorak pattern instead of the standard Qwerty layout. Also, you can set up your own system for shift-locking the keyboard. You don't have to follow the standard computer system of locking and unlocking only the alphabetic characters when you press SHIFT-CAPS/LOWR, CONTROL-CAPS/LOWR, or CAPS/LOWR alone. You can make the entire keyboard lock and unlock, or have the nonalphabetic characters lock independently of the alphabetic characters, by pressing SHIFT-ESCAPE or CONTROL-ESCAPE, for instance.

Range. Perhaps the most exciting advantages of working with the keyboard code is the great range of values it offers you. Every key on the keyboard except SHIFT, CONTROL, BREAK, START, SELECT, OPTION, and RESET produces its own unique KEYCODE number. Holding down SHIFT while depressing another key produces that same number plus 64. Holding down CONTROL produces that numer plus 128. And, except for 11 keys (16 for XL users), holding down both SHIFT and CONTROL produces that number plus 192.

This means almost every key has four possible values—even RETURN and ESC and SPACE, which the computer usually treats the same regardless of whether SHIFT or CONTROL is pressed. There are 52 keys on old Ataris and 57 keys on XL models that put numbers in location 53769. That gives you 197 unique signals from your keyboard (212 if you use an XL model).

Yet there are only 128 valid ATASCII codes (values 128-255 are merely inverse characters). You are left with 68 (or 84) possible key combinations that ATASCII doesn't need to use. If you were creating a word-processing program, you could print every single character, including graphics characters, and still have 68 commands left over—without ever reaching for the console keys.

The Three Atari Character Codes

The Atari Operating System (OS) uses three different codes for character values: ATASCII, Internal Code (ICODE), and Keyboard Code (KEYCODE). Each has a specific use, and most of the time, the OS handles all the conversions from one to another so quickly that you don't even notice it's going on.

In order to use KEYCODEs effectively, you need to have a clear idea of the differences among the three codes and their relationship to each other. So let's review the function of each of the character codes.

ATASCII. This is the code used by BASIC. All the alphanumeric characters (letters and numbers) and symbols follow the standard ASCII code recognized by most computers. The rest of the ASTASCII codes are used for graphics characters. For instance, in ATASCII, the letter A has the value 65.

The following commands and functions use the ATASCII number:

CHR$(ATASCII)
OPEN #1,4,0,"K:":GET #1,ATASCII
ATASCII=PEEK(763)
ATASCII=ASC("A")
GRAPHICS 1: COLOR ATASCII:PLOT I,I

(Special ATASCII code conversions are used in GRAPHIS 1 and 2, but for values 32-95, the regular ATASCII values will PLOT in Color 1—color register 0.)

Internal code. This is the code used by the operating system to put characters on the screen. The ICODE (internal code) number represents the character's position within the ROM character set. The first character in the ROM character set is the blank (space) character. It has the ICODE number 0. The character A is in position 33 in the character set, so its ICODE number is 33.

The ICODE number is used twice. First, when you type or PRINT a character on the screen, the OS converts the ATASCII value into the ICODE value and stores the ICODE value in screen memory. Second, the ANTIC chip, which scans screen memory 60 times a second, reads the ICODE value stored there and uses it to count a certain number of steps into the ROM character set. Since it takes eight bytes to contain each character pattern in the ROM set, ANTIC counts 8*ICODE bytes into the character set to find the beginning of the pattern.

So when you type the letter A, the OS stores the number 33 in screen memory. ANTIC finds that 33 and multiplies it by 8, which results in the number 264. ANTIC then goes to the character set and counts in until it finds byte 264. This is the first byte of the pattern for the character A. ANTIC uses that byte, along with the next seven bytes, to tell the TV screen what to display.

You will use ICODE values for the same purpose the OS uses them—to POKE characters directly into screen memory and to find a character's pattern within the character set.

Keyboard code. This is the number generated by the circuits in your keyboard when you press a key (see Table 1). The combination of open and closed circuits from the keyboard causes a KEYCODE (keyboard code) number to be stored in location 53769. This number is then read by the OS and stored at 764, where it is picked up and converted into an ATASCII value which is stored in location 763.

The keyboard code is never used anywhere else, but there are still several things you can do with it. By POKEing character codes into location 764, you can fool the OS into thinking that a particular key has been pressed. Then, when your program GETs the latest key pressed or executes an INPUT statement, it will think the key you specified was pressed.

You can also change the way the computer thinks the keys are laid out. For instance, you might want to try the Dvorak keyboard. The Qwerty keyboard (the one your computer comes with) was deliberatly designed to be inconvenient and slow. Back when mechanial typewriterswere first used, quick typists kept jamming the keys. So the Qwerty keyboard puts the most commonly used letters off the home keys or on the left side, where most typists will have a harder time getting to them. Computerized keybaords are faster now, and the Dvorak keyboard is designed to take advantage or that. The most commonly used characters are on the home keys. And you can learn the Dvorak system by making your Atari read the keyboard in the Dvorak pattern, just by reconfiguring the relationship between KEYCODE and ATASCII.

You can also use the keyboard codes to get input from the keyboard directly, bypassing the OS's formulas for conversion. That's the use we'll persue in the rest of this article.

Table 1. Keyboard Codes
Unshifted Keyboard Values
ESC
28
1
31
2
30
3
26
4
24
5
29
6
27
7
51
8
53
9
48
0
50
<
54
>
55
DEL
52
  TAB
44
Q
47
W
46
E
42
R
40
T
45
Y
43
U
11
I
13
O
8
P
10
-
14
=
15
RETURN
12
    A
63
S
62
D
58
F
56
G
61
H
57
J
1
K
5
L
0
;
2
+
6
*
7
CAPS/LOWR
60
      Z
23
X
22
C
18
V
16
B
21
N
35
M
37
,
32
.
34
/
38
Atari logo
39
SPACE BAR
33
F1
3
F2
4
F3
19
F4
20
HELP
17

Keyboard Values with SHIFT*
ESC
92
1
95
2
94
3
90
4
88
5
93
6
91
7
115
8
117
9
112
0
114
<
118
>
119
DEL
116
  TAB
108
Q
111
W
110
E
106
R
104
T
109
Y
107
U
75
I
77
O
72
P
74
-
78
=
79
RETURN
76
    A
127
S
126
D
122
F
120
G
125
H
121
J
65
K
69
L
64
;
66
+
70
*
71
CAPS/LOWR
124
      Z
87
X
86
C
82
V
80
B
85
N
99
M
101
,
96
.
98
/
102
Atari logo
103
SPACE BAR
97
F1
67
F2
68
F3
83
F4
84
HELP
81

Keyboard Values with CONTROL*
ESC
156
1
159
2
158
3
154
4
152
5
157
6
155
7
179
8
181
9
176
0
178
<
182
>
183
DEL
180
  TAB
172
Q
175
W
174
E
170
R
168
T
173
Y
171
U
139
I
141
O
136
P
138
-
142
=
143
RETURN
140
    A
191
S
190
D
186
F
184
G
189
H
185
J
129
K
133
L
128
;
130
+
134
*
135
CAPS/LOWR
188
      Z
151
X
150
C
146
V
144
B
149
N
163
M
165
,
160
.
162
/
166
Atari logo
167
SPACE BAR
161
F1
131
F2
132
F3
147
F4
148
HELP
145

Keyboard Values with SHIFT and CONTROL
ESC
220
1
223
2
222
3
218
4
216
5
221
6
219
7
243
8
245
9
240
0
242
<
246
>
247
DEL
244
  TAB
236
Q
239
W
238
E
234
R
232
T
237
Y
235
U
203
I
205
O
200
P
202
-
206
=
207
RETURN
204
    A
255
S
254
D
250
F
248
G
253
H
249
J
 
K
 
L
 
;
 
+
 
*
 
CAPS/LOWR
252
      Z
 
X
 
C
 
V
 
B
 
N
227
M
229
,
224
.
226
/
230
Atari logo
231
SPACE BAR
225
F1
 
F2
 
F3
 
F4
 
HELP
 

*Eleven keys cannot be used with SHIFT and CONTROL pressed: J, K, L, ;, +, *, Z, X, C, V, and B (and F1, F2, F3, F4, and HELP on XML models).

Exceptions to the Rules

SHIFT-lock. It is important to remember that the number stored at 53769 and shadowed at 764 is the value of the key combination actually pressed. It is not affected at all by whether the keyboard is SHIFT-locked or CONTROL-locked.

When the Atari powers up, the keyboard is locked into alphabetic shift mode—when you press any letter key, with or without pressing SHIFT at the same time, the shifted value appears on the screen. But as far as locations 53769 and 764 are concerned, if you don't press SHIFT, the unshifted value is all it gets.

The way the operating system handles SHIFT-lock and CONTROL-lock is simple—you can imitate this in your own programs. When the CAPS/LOWR key is pressed, the operating system changes the SHIFT-lock flag at location 702. If the CAPS/LOWR key is pressed by itself, 0 is stored at 702; if SHIFT and CAPS/LOWR are pressed together, 64 is stored there; and if CONTROL and CAPS/LOWR are pressed together; 128 is stored there. From then on, if the key pressed calls for an alphabetic (letter, rather than number or symbol) character, the operating system checks location 702 and adds the number stored there to the offset into the Key Definition Table. Programs 1 and 2 both perform a customized version of this function, by-passing the operating system entirely.

XL models. XL models (Atari 600XL, 800XL, 1200XL, 1400XL, and 1450XL) allow you to lay out your own Keycode Definitions array AC(n)), and inform the operating system by POKEing the address of the table, low byte first, into locations 121 and 122 ($79 and $7A). The table is set up exactly like the ATASCII array—you could use the DATA statements, converting them from ICODE to ATASCII order, to set up the table for the XL redefinition.

The XL models also allow you to redefine the Fn and SHIFT-Fn keys separately, without redefining the entire keyboard, by setting up an eight-byte table and POKEing its address, low byte first, into locations 96 and 97 ($60 and $61).

However, this system of keyboard redefinition still leaves you with the OS's system or interpretation, which ignores all SHIFT-CONTROL and all CONTROL-number key combinations. To really take advantage of the power of the keyboard code, you need to set up your own interpretation system as well.

Missing SHIFT-CONTROL combinations. Eleven keys—16 on XL models—return no value to location 57369 if both SHIFT and CONTROL are pressed at the same time: J, K, L, ;, +, *, Z, X, C, V, and B on all Ataris, and F1, F2, F3, F4 and HELP on XL models. It is as if those key combinations did not exist.

Interrupts. Most of the time, whatever number is stored in location 53769 is also stored at 764. There are exceptions when the key combination is acted on during an interrupt. The CONTROL-1 combination, for instance, is read during an interrupt and can't be read from 764—but the value still occurs at location 53769 and can be read there. On XL models, CONTROL-F1, CONTROL-F2, CONTROL-F4, HELP, SHIFT-HELP, and CONTROL-HELP also generate codes that are not transferred from 53769 to 764.

What difference does this make? If you want to be able to read that code in spite of the interrupt, you can—by reading 53679 (actually, 53769) instead of 764. The interrupt will still take place, but your program will also "know" that the key combination was pressed. Or if you want your program to ignore keys used by the interrupts, read the values at 764 instead of 53769.

Here is a short program that reads the hardware register and POKEs the raw KEYCODE number into screen memory. First, you will see that the KEYCODE number has no relation to the ICODE number that normally is POKEd into screen memory.

Second, since the PRINT command isn't being used, the CONTROL-1 key has no effect at all—and its KEYCODE number is POKEd into screen memory, where it appears as an inverse question mark. If you have an XL model, you will see that pressing CONTROL-F4 still toggles between the standard and international character sets—but it also causes an inverse 4 to appear on the screen.

10 POKE PEEK(88)+256*PEEK(89)+N,PEEK(53769)
20 N=N+1-960*(N>958):GOTO 10

Built-in delay. There is a slight but measurable time lag between the keypresses causing a number to be stored at 53769, and the echo getting stored in 764. Here is a very short example program that will show you these codes:

10 PRINT PEEK(764);" ";PEEK(53769)
20 FOR I=0 TO 40:NEXT I: GOTO 10

This program PRINTs the value at 764 on the left and the value at 53769 on the right. If you RUN this program and then type very quickly, you will sometimes see a number appear on the right that has not yet appeared on the left—you have caught the OS between receiving the KEYCODE at 53769 and echoing it at 764. If your program needs speed (particularly if it is a machine language routine), you'll definitely want to read the keyboard code at 53769.

ATASCII-ICODE Conversions

Actually, since ATASCII and ICODE have a regular relationship, conversions back and forth are quite simple. Subroutine 1 converts the ATASCII number AC(N) to ICODE and assigns the value to IC(N):

Subroutine 1. ATASCII to ICODE

800 VERS=0:IF AC>127 THEN VERS=1:AC=AC-128
810 IF AC<32 THEN IC=AC+64+128*VERS:RETURN
820 IF AC<96 THEN IC=AC-32+128*VERS:RETURN
830 IC=AC+128*IV:RETURN

The routine should actually look like this because of the last paragraph in this section:
100 IV=0:IF AC>127 THEN IV=1:AC=AC-128
110 IF AC<32 THEN IC=AC+64+128*IV:RETURN
120 IF AC<96 THEN IC=AC-32+128*IV:RETURN
130 IC=AC+128*IV:RETURN

When you jump to this routine, the variable AC must contain the ATASCII value of the character you want converted to ICODE. When you return from the subroutine, the variable IC will contain the ICODE value. You can POKE it to screen memory: POKE PEEK(88)+256*PEEK(89)+OFFSET,IC.

Subroutine 2 converts from ICODE to ATASCII:

Subroutine 2. ICODE to ATASCII
200 IV=0:IF IC>127 THEN IV=1:IC=IC-128
210 IF IC<64 THEN AC=IC+32+128*IV:RETURN
220 IF IC<96 THEN AC=IC-64+128*IV:RETURN
230 AC=IC+128*IV:RETURN

When you GOSUB to this routine, the variable IC contains the ICODE value of the character you want converted. When you return from the subroutine, the variable AC will contain the value that, when PRINTed, will cause the character to be displayed.

In both subroutines, the variable IV is used to keep track of whether the character was inverse or not. Note that you cannot change the order of these lines. If 220 is executed before 210, or 120 before 110, the results will be wrong.

The Keyboard Code Array

Since KEYCODE doesn't have a systematic relationship with the other codes, a simple3 program wouldn't convert to and from KEYCODE. A much better solution is to set up a table of ATASCII or ICODE values in KEYCODE order, and then use the KEYCODE number as a pointer into the table to find the right ATASCII or ICODE value. In BASIC, the simplest way of doing this is to use the KEYCODE number as the subscript in an array containing either ICODE or ATASCII values. (For a complete listing of keyboard codes and their relationship to internal code and ATASCII, see Appendix A, "A Complete Guide to the Atari Character Set.")

During your program's setup phase, you need to DIMension one or both of these arrays:

DIM IC(255),AC(255)

The elements of this array will be assigned either ATASCII or ICODE values, arranged in KEYCODE order. For Instance, KEYCODE 0 is produced by pressing l (lowercase L). Therefore the value of C(0) will be 108.

Once the array has been set up, the keyboard can be read almost instantly. For instance, to PRINT the last key pressed, regardless of what it was or how long ago it was pressed, this statement would do:

PRINT AC(PEEK(764))

We'll go into much mo0re detail about effective use of the keyboard codes later on.

Assigning values. The way you assign values to this array depends on how you want to use the keyboard data.

KEYCODE 94 is produced by pressing SHIFT-2 (the quotation mark). If the array has been set up in ATASCII order, the value of C(94) will be 34. This is the method you will use if you want to print CHR$(C(KEYCODE)) or create strings.

If the array has been set up in ICODE order, the value of C(94) will be 2. This is the method you will use if you want to POKE keyboard input directly into screen memory, to create displays without using PRINT or strings.

The KEYCODE DATA Statements

Program 1 is the heart of this system. It consists of DATA statements that contain ICODE values in KEYCODE order.

Extra key combinations. Zero is used for every KEYCODE value that has not been assigned an ATASCII or ICODE value. There are many zeros in the DATA statements, even though only the space bar should produce a blank, because there are many KEYCODE values that have no corresponding ICODE or ATASCII values. For instance, SHIFT-RETURN has no special ICODE or ATASCII value. If you wanted SHIFT-RETURN to have the same value as RETURN, you would assign it the same value as RETURN.

Inverse ATASCII characters. Some ATASCII values are really inverse characters. ATASCII 156-159 (usually produced by pressing SHIFT-DELETE, SHIFT-INSERT, CONTROL-TAB, and SHIFT-TAB) PRINT as nothing omre than the inverse of ATASCII 28-31. ATASCII 253, 254, and 255 (CONTROL-2, CONTROL-DELETE, and CONTROL-INSERT) are inverses of ATASCII 125-127 (SHIFT-CLEAR, DELETE, and TAB). ATASCII 155 (RETURN)_, if it could be PRINTed as a character, would be the inverse of ATASCII 27 (ESC). Since all of these characters can be obtained by PRINTing an inverse of another key combination, they have been left as zeros in the DATA statements. (If you want a keyboard code to clear the screen or ring the CONTROL-2 buzzer, that can be done independently, as will be shown below.)

Impossible codes. Many of the zeros in the DATA statements are there because certain KEYCODE values cannot exist—no combination of keys will result in that particular number. The impossible codes on non-XL Ataris are 3, 4, 9, 17, 19, 20, 25, 36, 41, 49, and 59—and those numbers plus 64, 129, and 192. Since the DATA statements are arranged in KEYCODE order, the impossible codes are represented by zeros just to keep the array in order. If your computer is an XL model, 3, 4, 17, 19, and 20 represent F1, F2, HELP, F3, and F4, and can be read in any combination except SHIFT-CONTROL.

Assigning Values to the Array

Program 1 includes all the DATA statements needed to set up arrays AC(n) and IC(n). By removing the word REM in front of the subroutine calls, you can create a disk file containing the array, or load the data from the disk file into the array. Or you can simply add these DATA statements to a program.

Using the Keyboard Code in a Program

Once the array is set up, reading the keyboard code is very simple. You can use it directly, of course, by putting it in a function:

PRINT CHR$(PEEK(53769))

However, this does not begin to use the freedom the keyboard code gives you.

Is a key pressed? First, if your keyboard read routine is complicated at all, you will want to avoid going through it when there is nothing to read. In the main loop of your program, the test can be as simple as this:

ON PEEK(753)<>3 GOSUB 500

Location 753 is set to 3 every time a key is pressed. If a key is not pressed, it decrements (decreases in value by 1) every 1/60 second until it reaches zero. If a key is pressed and held down, 753 will continue to equal 3. So your program will GOSUB to your keyboard read routine only when a key is pressed.

Locking character sets. The CAPS-LOWR key usually affects only the alphabetic character keys. To get the % character, you have to press SHIFT-5, regardless of whether the alphabetic keys are locked in SHIFT or CONTROL mode. The subroutine below, however, will automatically lock all the keys in one mode or another.

2500 N=PEEK(53769):S=INT(N/64):KEY=N-S*64
2510 IF KEY=60 THEN SHIFT=S*64:RETURN
2530 IC=IC(KEY+SHIFT):AC=AC(KEY+SHIFT)
2540 POKE W,IC:W=W+1-960*(W=959):RETURN

Line 2500 sets up three useful variables. N holds whatever value was in 53769. S tells us, in effect, whether SHIFT, CONTROL, or both were also pressed. If S=0 then neither was depressed; if S-1, then SHIFT; if S=2, then CONTROL; if S=3, then both. KEY tells us which actual key was depressed, regardless of whether SHIFT or CONTROL was depressed.

In line 2510 the program determines whether KEY was the CAPS-LOWR key, whose code is 60. If it was, then the variable SHIFT is set at 0, 64, 128, or 192, depending on whether SHIFT or CONTROL was depressed. Since CAPS-LOWR is not a printing character, the subroutine returns at this point.

In line 2530, IC and AC are set at the ICODE and ATASCII equivalent, not of KEY, but of KEY + SHIFT. Whatever value SHIFT was last given by line 2510 is automatically added to the absolute value of whatevr key was depressed. Now if the program should print AC or POKE IC onto the screen, it would give either its shifted, control, or unshifted value, depending on the value of SHIFT, regardless of whether SHIFT or CONTROL was pressed when the key was entered.

In line 2540, IC is POKEd into location SC + W, which represents a position in screen memory. (SC = lowest address of screen memory; W = current location above SC.) Then W is incremented (increased by 1). If W is at 959, so that incrementing it would take us off the bottom of the screen, the program subtracts 960 and starts us at the upper left-hand corner again.

Inverse mode. Right now there's no way to print inverse characters. So let's add a line to take care of that.

2505 IF KEY=39 AND S>0 AND S<3 THEN IV=128*(S=2):RETURN

We also need to change line 2530:

2530 IC=IC(KEY+SHIFT+IV):AC=AC(KEY+SHIFT+IV)

Now when you press the Atari logo key at the same time as you press CONTROL, the entire keyboard shifts into inverse mode. Press SHIFT and the Atari logo key and the keyboard shifts back into regular mode. But when you press the Atari logo key by itself or with both CONTROL and SHIFT, there is no effect on inverse mode at all.

2530 IC=IC(KEY+SHIFT):AC=AC(KEY+SHIFT)

Multiple meanings. When you press the arrow keys when the keyboard is locked into the control mode, you'll notice that the arrows appear on the screen, and the cursor does not move. This is because the program is POKEing the ICODE values into screen memory. If the program were PRINTing the ATASCII values, the cursor would have moved.

But you can still use the cursor keys, just as you always have, along with the SHIFT-CLEAR key, by adding these lines:

2520 ON S GOTO 2500,2850,2900
2800 IF N=125 THEN PRINT CHR$(AC(N))
2810 RETURN
2850 H=(KEY=7)-(KEY=8):V=40*((KEY=14)-(KEY=15):W=W+H+V
2860 IF W<0 THEN W=W+960:RETURN
2870 IF W>959 THEN W=W-960
2880 RETURN
2900 REM This command  line  is  executed  if  SHIFT-CONTROL are pressed
2910 RETURN

Notice that in line 2520 the program uses a GOTO instead of a GOSUB. This means that the RETURN at the end of each of these subroutines will take us back, not to the statement immediately following the branch in line 25820, but to the main loop of the program. If we did not do this, every command would also result in a blank being displayed on the screen.

More Commands Than You Can Use

Remember when I said that we would have 68 command characters? Now you can see that we could just as easily have 140 command characters. That is because, by using the CAPS-LOWR key the way we do, all the printable values of each key can be displayed on the screen without pressing SHIFT or CONTROL each time. Then if the user does press SHIFT or CONTROL or both with a character, we can interpret that separately as a command.

Naturally, few programs would ever need 140 command characters, and a word processing program would do much better to interpret keys pressed with SHIFT as characters rather than commands—typists would hate having to use the CAPS-LOWR every time they wanted a capital letter or a shifted symbol.

But using the keyboard codes, you have the freedom to design your own keyboard system, to respond to the exact needs of your own program. You could design a word processor that used a keyboard layout different from the standard Qwerty, or you could simply speed up the key repeat. You could also use a section of the keyboard as a game controlle with continuous commands—as long as a key was held down, it would continue to repeat its function. You could read the keyboard as an organ, shifting back and forth between different banks of keys with different stops set.

The Dvorak Keyboard

One thing you might want to try is the Dvorak keyboard (Program 2). In this program, the DATA sets up the arrays so the keyboard is interpreted according to the Dvorak keyboard instead of the Qwerty keyboard (see Table 2). By using this table with yoru own keyboard reading program, you could train yourself to type with the much faster Dvorak keyboard arrangement.

Table 2. Dvorak Keyboard Codes
Unshifted Keyboard Values
ESC
28
1
31
2
30
3
26
4
24
5
29
6
27
7
51
8
53
9
48
0
50
<
54
>
55
DEL
52
  TAB
44
/
47
,
46
.
42
P
40
Y
45
F
43
G
11
C
13
R
8
L
10
-
14
=
15
RETURN
12
    A
63
O
62
E
58
U
56
I
61
D
57
H
1
T
5
N
0
S
2
+
6
*
7
CAPS/LOWR
60
      ;
23
Q
22
J
18
K
16
X
21
B
35
M
37
W
32
V
34
Z
38
Atari logo
39
SPACE BAR
33
F1
3
F2
4
F3
19
F4
20
HELP
17

Keyboard Values with SHIFT*
ESC
28
1
31
2
30
3
26
4
24
5
29
6
27
7
51
8
53
9
48
0
50
<
54
>
55
DEL
52
  TAB
44
/
47
,
46
.
42
P
40
Y
45
F
43
G
11
C
13
R
8
L
10
-
14
=
15
RETURN
12
    A
63
O
62
E
58
U
56
I
61
D
57
H
1
T
5
N
0
S
2
+
6
*
7
CAPS/LOWR
60
      ;
23
Q
22
J
18
K
16
X
21
B
35
M
37
W
32
V
34
Z
38
Atari logo
39
SPACE BAR
33
F1
3
F2
4
F3
19
F4
20
HELP
17

Keyboard Values with CONTROL*
See Table 1.

Keyboard Values with SHIFT and CONTROL
See Table 1.

Note: The Dvorak keyboard calls for the single and double quotation marks to be just to the right of the L key and the hyphen and underline characters to be just to the right of the S key. The preceeding table does not show this because those keys are used for arithmetic functions on the Atari keyboard, and most users would probably prefer to leave those keys as they are.

Debounce routine. Line 100 contains a homemade debounce routine. When you type, your finger remains on the key for a fraction of a ssecond. If the program reads the keyboard again before you lift your finger, the key will repeat—even though you might not want it to. The debounce routine checks to see if the value it just got from the keyboard is the same as the last one it got. If not, a new key has been pressed and the program goes on. but if the keys are the same, the counter X is incremented by one. If X is less than 4, the key will be ignored; if it is greater than 4, it is assumed that the typist meant the key to repeat.

By changing the 4 to some other number, you can change the time lag between holding down a key and getting it to repeat on the screen. Or you could write a routine that would cause the cursor control keys to repeat without a much shorter debounce delay than the other keys.

because this routine is written in BASIC, it has another problem—it's possible for you to type so quickly that you press one key and then go on and press another key before the program ever reads the first key's value. You can solve the problem by writing in machine language. Or you could write just your keyboard reading routine in machine language and run it in an interrupt, have that routine store the characters typed into a buffer, and let your BASIC program read the keyboard input from the buffer at its own speed. Or you could compile your BASIC program so it ran faster than people could type. But the more commands you have to check for with each letter typed, the slower your BASIC program will run, and the more keystrokes you'll lose because of slow program execution.

SHIFTing. This program improves on the way Program 1 handles the SHIFT key. Instead of simply ignoring the SHIFT and CONTROL keys except when CAPS/LOWR is pressed, Program 2 pays attention to SHIFT. If the keyboard is locked into SHIFT or CONTROL, pressing the SHIFT key has no effect. If the keyboard is locked into lowercase (that is, if you pressed CAPS/LOWR by itself), then pressing the SHIFT key with another key will cause that letter, and only that letter, to be shifted—just like the standard typewriter keyboard.

This is handled in line 105, when S is sett to equal INT(K/64). In effect, this makes S equal 1 if SHIFT is pressed, 2 if CONTROL is pressed, 3 if both are pressed, and 0 if neither is pressed. Then, in line 120, N is set back to the value of K, the original keystroke combination, if SHIFT was pressed. This by-passes the locked value of the variable SHIFT for one keystroke only.

It would be a simple matter to adapt this program so that if the keyboard is locked into SHIFTed condition, pressing the SHIFT key and another key would cause the program to display the lowercase, unshifted value of that key. Or you could write a routine that would allow you to lock and unlock the number keys into shifted and unshifted condition separately from the rest of the keyboard.

POKEing to the screen. This program pretends to be a typing program, since lines 200 and 205 POKE the letters directly into screen memory. Each time a character is POKEd into memory, the pointer variable E is incrememented by one so that the next character will be placed just to the right of the character before.

An alternative would be to replace ICODE screen POKEing with ATASCII PRINT statements. Delete lines 200 and 205 and replace them with

200 PRINT CHR$(AC(N))+VERS

Now the editing functions will work and the screen will scroll when you reach the bottom.

These programs, while not especially useful in themselves, should give you a pretty good idea of some of the possibilities that are opened up to you if your programs read the keyboard directly. Whenever you write a program that relies heavily on keybard input, you should give serious consideration to having your program read the keyboard independently—it might allow you to add refinements to your program that make it more powerful or useful to the user.


Program 1. Standard Array
5 DIM IC(255),AC(255):SC=PEEK(88)+256*PEEK(89):SHIFT=64:VERS=0
10 GOSUB 500:REM THIS WILL CREATE ARRAYS FROM DATA STATEMENTS
15 REM GOSUB 600:REM USE THIS TO CREATE "D:KEYCODE.DAT"
20 REM GOSUB 700:REM USE THIS TO CREATE ARRAYS FROM DISKFILE "D:KEYCODE.DAT"
100 POKE 694,0:ON PEEK(753)<>3 GOTO 100:K=PEEK(764)
105 N=K:IF N>63 THEN N=N-64:IF N>63 THEN N=N-64:IF N>63 THEN N=N-64
110 IF N=60 THEN SHIFT=4+K-64
115 IF N=39 THEN VERS=128*(VERS<>128)
200 PRINT N,CHR$(AC(N+SHIFT)+VERS)
205 POKE SC+959,IC(N+SHIFT)+VERS
210 GOTO 100
500 RESTORE 1000:FOR I=0 TO 191:READ N:IC(I)=N:NEXT I:FOR I=192 TO 255:IC(I)=0:NEXT I
510 FOR I=0 TO 255:N=IC(I)
520 IF N<64 THEN AC(I)=N+32
530 IF N>63 AND N<96 THEN AC(I)=N-64
540 IF N>95 THEN AC(I)=N
550 NEXT I
560 RETURN 
600 OPEN #4,8,0,"D:KEYCODE.DAT"
605 RESTORE 1000:FOR I=0 TO 191:READ N:PUT #4,N:NEXT I
610 FOR I=192 TO 255:PUT #4,0:NEXT I
615 CLOSE #4:RETURN 
700 OPEN #4,4,0,"D:KEYCODE.DAT"
705 FOR I=0 TO 255:GET #4,N:IC(I)=N
710 IF N<64 THEN AC(I)=N+32
715 IF N>63 AND N<96 THEN AC(I)=N-64
720 IF N>95 THEN AC(I)=N
725 NEXT I:RETURN 
1000 DATA 108,106,27,0,0,107,11,10,111,0,112,117,0,105,13,29
1016 DATA 118,0,99,0,0,98,120,122,20,0,19,22,91,21,18,17
1032 DATA 12,0,14,110,0,109,15,0,114,0,101,121,127,116,119,113
1048 DATA 25,0,16,23,126,24,28,30,102,104,100,0,0,103,115,97
1064 DATA 44,42,26,0,0,43,60,62,47,0,48,53,0,41,63,124
1080 DATA 54,0,35,0,0,34,56,58,4,0,3,6,0,5,2,1
1096 DATA 59,0,61,46,0,45,31,0,50,0,37,57,0,52,55,49
1112 DATA 8,0,9,7,0,32,125,0,38,40,36,0,0,39,51,33
1128 DATA 76,74,123,0,0,75,94,95,79,0,80,85,0,73,92,93
1144 DATA 86,0,67,0,0,66,88,90,0,0,0,0,0,0,0,0
1160 DATA 64,0,96,78,0,77,0,0,82,0,69,89,0,84,87,81
1176 DATA 0,0,0,0,0,0,0,0,70,72,68,0,0,71,83,65

Listing. Standard Array.
Download (Saved BASIC) / Download (Listed BASIC) / Download (KEYCODE.DAT)


Program 2. Dvorak Array
5 DIM IC(255),AC(255):SHIFT=64:VERS=0:SC=PEEK(88)+256*PEEK(89):E=0
10 GOSUB 500:REM THIS WILL CREATE ARRAYS FROM DATA STATEMENTS
15 REM GOSUB 600:REM USE THIS TO CREATE "D:DVORAK.DAT"
20 REM GOSUB 700:REM USE THIS TO CREATE ARRAYS FROM DISKFILE "D:DVORAK.DAT"
100 POKE 694,0:ON PEEK(753)<>3 GOTO 100:P=PEEK(53769):IF P=K THEN X=X+1:IF X<4 THEN 100
105 X=0:K=P:S=INT(K/64):N=K-64*S
110 IF N=60 THEN SHIFT=64*S
115 IF N=39 THEN VERS=128*(VERS<>128)
120 N=N+SHIFT:IF S=1 THEN N=K
200 POKE SC+E,IC(N)+VERS
205 E=E+1-960*(E>958)
210 GOTO 100
500 RESTORE 1000:FOR I=0 TO 191:READ N:IC(I)=N:NEXT I:FOR I=192 TO 255:IC(I)=0:NEXT I
510 FOR I=0 TO 255:N=IC(I)
520 IF N<64 THEN AC(I)=N+32
530 IF N>63 AND N<96 THEN AC(I)=N-64
540 IF N>95 THEN AC(I)=N
550 NEXT I
560 RETURN 
600 OPEN #4,8,0,"D:DVORAK.DAT"
605 RESTORE 1000:FOR I=0 TO 191:READ N:PUT #4,N:NEXT I
610 FOR I=192 TO 255:PUT #4,0:NEXT I
615 CLOSE #4:RETURN 
700 OPEN #4,4,0,"D:DVORAK.DAT"
705 FOR I=0 TO 255:GET #4,N:IC(I)=N
710 IF N<64 THEN AC(I)=N+32
715 IF N>63 AND N<96 THEN AC(I)=N-64
720 IF N>95 THEN AC(I)=N
725 NEXT I:RETURN 
1000 DATA 110,104,115,0,0,116,11,10
1008 DATA 114,0,108,103,0,99,13,29
1016 DATA 107,0,106,0,0,120,113,27
1024 DATA 20,0,19,22,91,21,18,17
1032 DATA 119,0,118,98,0,109,122,0
1040 DATA 112,0,14,102,127,121,12,15
1048 DATA 25,0,16,23,126,24,28,30
1056 DATA 117,100,101,0,0,105,111,97
1064 DATA 46,40,51,0,0,52,60,62
1072 DATA 50,0,44,39,0,35,63,124
1080 DATA 43,0,42,0,0,56,49,26
1088 DATA 4,0,3,6,0,5,2,1
1096 DATA 55,0,54,34,0,45,58,0
1104 DATA 48,0,61,38,0,57,59,31
1112 DATA 8,0,9,7,0,32,125,0
1120 DATA 53,36,37,0,0,41,47,33
1128 DATA 76,74,123,0,0,75,94,95
1136 DATA 79,0,80,85,0,73,92,93
1144 DATA 86,0,67,0,0,66,88,90
1152 DATA 0,0,0,0,0,0,0,0
1160 DATA 64,0,96,78,0,77,0,0
1168 DATA 82,0,69,89,0,84,87,81
1176 DATA 0,0,0,0,0,0,0,0
1184 DATA 70,72,68,0,0,71,83,65

Listing. Dvorak Array.
Download (Saved BASIC) / Download (Listed BASIC) / Download (DVORAK.DAT)


Return to Table of Contents | Previous Section | Next Section