Classic Computer Magazine Archive COMPUTE! ISSUE 67 / DECEMBER 1985 / PAGE 116

Commodore Dynamic Keyboard

Part 3

Jim Butterfield, Associate Editor

Parts 1 and 2 of this series showed how the dynamic keyboard technique—which allows the computer to seemingly type on its own keyboard—lets you do things that would otherwise be difficult or impossible from within a program. Now we'll look at the trickiest application of this technique—writing a program that changes itself as it runs.

Let's quickly review how the dynamic keyboard technique works. First, the program prints the desired command at a specific screen location. Then a RETURN character is placed in the keyboard buffer. Finally, the program stops with the cursor flashing over the screen command. The RETURN in the keyboard buffer causes the operating system to read the command on the screen and carry it out, just as if you pressed the RETURN key. Using the same principle, we can put several commands on the screen and make the program execute them all.

The following table shows the location of the keyboard buffer counter and the start of the keyboard buffer on most Commodore computers:

CounterBuffer
VIC-20, Commodore 64198631
Commodore 16, Plus/42391319
PET/CBM (Upgrade and 4.0 BASIC)158623
Original ROM PETs525527
B128(Model 700)209929

For a single-line command, POKE a value of 1 into the counter and a value of 13 (RETURN) into the buffer. To execute more than one screen line of commands, use a higher count and more RETURN characters. On the B128 computer, it's wise to execute a BANK 15 command before the POKEs.

Self-Editing Programs

The usual way to change a program is to type in a new line and press RETURN. The line is either added to the program or it replaces an existing line with the same line number. A program can do this, too, using the dynamic keyboard technique. But there's a hitch. Whenever you enter a program line, the computer performs a CLR command, which closes all open files and clears the contents of all variables and arrays. This can be annoying, since it's hard for a program to continue running after its variables are gone. But with some careful programming, you can still make things work.

The solution is to identify your key variables, make the program change itself, then reinstate the variables with the dynamic keyboard technique. In effect, the variables are temporarily stored on the screen and put back in the program by the equivalent of a direct command. Tricky? Crude? Whatever your opinion of this method, the point is that it works. There are other ways to do the job, but you usually want to get it done in the most direct way possible.

You might be wondering why you'd ever need to design a program that modifies itself, anyway. Here's an example. Suppose you have something in a special part of memory—a machine language program, a screen picture, or a data table. Whatever it is, you want to take the information and build it into a series of DATA statements so it can be reconstituted by a BASIC program when needed. Perhaps you'd like to publish a small machine language program in a news­letter or magazine, and want readers to be able to type it in as DATA statements rather than the more complex hexadecimal code. How to do it?

First, let's write some data into memory so that you'll have something to convert to DATA statements. Here's a quick program to put a series of prime numbers into memory locations 828 to 881:

100 POKE 828,2 : rem 192
110 POKE 829,3 : rem 195
120 N = 3 : rem 81
130 FOR A = 830 TO 881 : rem 216
140 N = N + 2 : rem 203
150 FOR M = 3 TO SQR (N) + .l STEP 2 : rem 106
160 T = N / M : rem 242
170 IF T = INT (T) GOTO 140 : rem 22
180 NEXT M : rem 37
190 PRINT N : rem 176
200 POKE A,N : rem 124
210 NEXT A : rem 19

That's not the most efficient prime number generator, but it does put the numbers into memory. The last number should be 251. Now, suppose you want these values in DATA statements so that a different program will be able to POKE them back at the start of the run.

Frenzied Activity

Type NEW to make space for the new program. The following program is written for the Commodore 64. If you're using another computer, refer to the table above to find the right POKE values for lines 75 and 80.

10 L = 100 : A = 830 : rem 206
15 PRINT CHR$ (147) : rem 225
20 PRINT : rem 239
25 PRINT : rem 244
30 PRINT L;"DATA"; : rem 16
35 DS = STRS(PEEK(A)) : rem 50
40 IF N>0 THEN DS=","+MID$(DS, 2) : rem 51
45 PRINT D$ : rem 153
50 A=A+1 : N=N+1 : IF N<10 AND A<8 82 GOTO 3 : rem 54
55 PRINT : rem 247
60 PRINT "L=";L+10;" : A=";A; : rem 193
65 PRINT " : GOTO 15" : rem 21
70 PRINT CHRS(19) : rem 176
75 POKE 198,1 : IF A<882 THEN POKE 198,2 : rem 224
80 POKE 631,13 : POKE 632,13 : rem 85
85 END : rem 68

Be sure to type the semicolon at the ends of lines 30, 45, and 60, and note that some of the strings printed in lines 60 and 65 start with a colon character. When you RUN the program, you'll see a frenzy of activity on the screen for a few moments. Then the action stops with the cursor over a line which says L = 160 : A = 882 : GOTO 15. Don't execute this line. Instead, move the cursor down, type LIST, and press RETURN. You'll find that the program contains six new lines of DATA statements.

Start the new DATA lines at line number 100 (variable L). Since the data maker program ends at a lower line number, there's no danger of replacing existing lines with new ones. Never increase the line number directly. Instead, print a higher value onto the screen. When the dynamic keyboard reinstates the variable, it's ten higher than before. There's no need to set variable N back to zero. The CLR caused by changing the program effectively clears all variables to zero.

After the DATA lines have been created—you've generated only a few—you might want to get rid of the program that made them. You could do this manually by clearing the screen and giving the direct command:

FOR J = 10 TO 85 STEP 5 : PRINT J : NEXT J

This prints the line numbers on a blank screen. You could then move the cursor back and strike RETURN 16 times, eliminating the lines. It would take a little ingenuity, but you could even cause the program to wipe itself out using the dynamic keyboard. (Hint: Crunch the program into less than ten lines—then stuff the keyboard buffer with the same number of RETURN characters.)

Convert ASCII To BASIC

Occasionally, you might have a sequential file on disk or tape that contains a program. You'd like to run this program, but LOAD can't handle a sequential file. Dynamic keyboard lets you bring the file into memory and convert it to a regular program file. How could this need arise? There are several possibilities. First, the program might have been transmitted over a communications link. It's possible to download a program in a form that's ready to run, but it's common to transmit a program in ASCII form—as ASCII characters only, rather than the usual mixture of ASCII and BASIC tokens. Now, however, you must change the listing back into a working program.

Here's another way it might happen. You want to transfer a program between two slightly incompatible computers. Perhaps you have a PET program that you'd like to use on a Plus/4, or vice versa. You may be surprised to find that the SCRATCH command used in a PET program doesn't load correctly on a Plus/4. Knowing the technical reason for this (the computers use different tokens for some commands) doesn't help solve the problem. Since ASCII listings contain no tokens, you'll find that they transport more easily than ordinary programs.

Another possibility is that you want to merge two programs into one. The dynamic keyboard offers one way to do this. Note that we're breaking down the distinction between data and programs—as personified by sequential and program files, respectively. This opens the door to such things as program-analyzing programs and program-writing programs.

Change A Program To A File

Let's start by writing a simple pro­gram. Anything will do, but let's use the following:

100 FOR J = l TO 10
110 PRINT J,SQR(J)
120 NEXT J

Remember to type NEW before entering this program. Now store it as a sequential file:

OPEN 1,8,6,"0 : PROGFILE,S,W" : CMD 1 : LIST

After you press RETURN, the disk drive operates briefly, then the cursor returns. Now type:

PRINT#1 : CLOSE 1

It is very important to close the file in exactly this manner.

The program is now stored as an ASCII listing in a sequential file named PROGFILE. If displayed on the screen, it would look almost like the original program. But it consists of nothing but ASCII characters. Thus, the first line contains the characters 1-0-0 (the line number), then a space, then the letters F-O-R, and so on. This is quite different from the tokenized form in which programs are usually stored.

This file has a few oddities caused by the way in which it was stored. Unlike most data files, it begins with two RETURN characters. And it ends with the word READY (after all, when you LIST a program to the screen, it always ends with the word READY). None of this is critical, but if you plan to do advanced work with such a file, keep these things in mind.

Keep The File Open

However, there's another problem to consider. Every time you enter a new line with the dynamic keyboard, CLR closes the file you're using. You've learned how to recreate variables, but how do you reinstate the file? You can use the following fact: The file isn't really closed, it's just "disconnected." CLR signals that no files are open simply by putting the value zero in the computer's number-of-open-files counter. If this is the only file you're handling, you can reconnect it by POKEing a 1 into the counter. The counter is found at one of the following locations, depending on your machine:

File Counter
VIC-20, Commodore 64152
Commodore 16, Plus/4 151
PET/CBM (Upgrade and 4.0 BASIC) 174
Original ROM PETs610
B128 (Model 700)864

Let's write a program to bring in this file. You know in advance that the line numbers start at 100, so you can safely put the loader program at lower numbers. If that isn't true for other situations, you might try renumbering the program with very high line numbers (above 60000, for instance).

Again, the following example runs on the VIC-20 and Commodore 64. Change the POKEs in lines 50, 65, and 70 to suit your machine. Be sure to include the semicolon at the end of line 40.

10 OPEN1, 8, 8, "PROGFILE"			    : rem 84
15 PRINT CHR$(147)					    : rem 225
20 PRINT						    : rem 239
25 PRINT						    : rem 244
30 GET#1, X$ : X = ASC(X$)			    : rem 178
35 IFX>47 AND X<58 THEN F = l			    : rem 175
40 IF F = l THEN PRINT X$;			    : rem 26
45 IF X<>13 GOTO 30			                 : rem 202
50 IF ST = 0 THEN PRINT "POKE 152, 1 : GOTO 15"  : rem 5
55 IF ST<>0 THEN PRINT "CLOSE{SPACE}1"		    : rem 241
60 PRINT CHR$(19)					    : rem 175
65 POKE 198, 2					    : rem 154
70 POKE 631, 13 : POKE 632, 13			    : rem 84
75 END							    : rem 67

When you run this program, it loads and merges the sequential ASCII listing. Typing 14 lines in order to add three more may seem inefficient. But the principle works on programs of any size.

It's been a long voyage. If you've stayed with it, you can probably see how the dynamic keyboard technique expands what you can do with the computer. Though it requires care, it also creates new possibilities. "Dynamic keyboard" is not just a buzzword, although you may add it proudly to your vocabulary. It's a new resource.