Self-Modifying Programs In BASIC
David Williams Toronto, Canada
The notion of a program which alters itself as it runs raises feelings of doubt and mistrust in many novice computer users. It seems that such a program would be doomed to failure through some kind of logical paradox. In fact this is not the case. Providing that the part of the program which guides the modification process is separate from that which is being changed, and that no attempt is made to execute program lines which are in the process of being modified, no problems need arise.
As a demonstration, try keying in the following program. As you do so, be careful not to include any spaces in lines 10 or 20, or between the quote marks in line 120. Line 20 should consist of a string of exactly twenty π's.
10 GOTO 100 20 ^^^^^^^^^^^^^^^^^^^^ 30 RETURN 100 FORI=826TO838:POKEI,32:NEXT 110 INPUTS$ 120 S$="GOTO200:"+S$+CHR$(13) 130 FORI=1TOLEN(S$):POKE838+I,ASC(MID$(S&neg;$,I)):NEXT 140 POKE175,2:POKE212,2:POKE59408, &neg;PEEK (59408) ANDNOT32:POKE188,0: &neg;POKE176,2 150 END 200 POKE175,0:POKE176,3 210 1=0 220 PK=PEEK(517+I) 230 IFPK=0THEN300 240 POKE1038+I,PK 250 I=I+1 260 GOTO220 300 FORI=ITO19:POKE1038+I,32:NEXT 400 GOSUB20 READY.
When you have finished entering the program, SAVE it before you first run it. If you have made any typing mistakes it is possible that the program may destroy itself or crash the PET when it is run. Having a copy on tape could save you a lot of re-typing!
When the program is run, a question mark and flashing cursor should appear on the screen. This is the input line 110. Respond to this by typing in some simple instruction in BASIC, such as PRINT 2 + 3*5, and hit the return key. Within the next couple of seconds the number 17 (the correct response to our input instruction) should be printed, followed by the word READY and the flashing cursor.
The output from this program is less interesting than another result, which can be seen by LISTing the program after it has run. Line 20 will be found to have changed from a meaningless string of π's to:
20 PRINT 2+ 3*5
the very same instruction that was entered while the program was running. In fact the π s were there only to reserve a set of twenty addresses into which the new line was POKEd. There are still twenty characters in line 20, but most of them are now blanks, which are not visible in the listing and do not cause any problems when the line is executed. Since the number of characters in the line is unchanged, the program can be run repeatedly, altering the contents of this line each time.
Maybe you now think that the program is far more complicated than it needs to be to achieve the result of poking the desired instruction into line 20. Surely all that needs to be done is to poke the ASCII numbers corresponding to P,R,I,N,T,etc. into the 20 addresses of the line. Write your own program to do this, if you want, but you're in for a disappointment. When your program is working properly, the new line will LIST exactly as it should, but when you try to execute it you will get a SYNTAX ERROR. The problem is that BASIC instruction words are stored in PET's memory as single token characters (the LISTing routine translates them back into English words) and the machine cannot understand them except in token form.
The demonstration program not only enters the new line in correct token form, it also does so without invoking the line editor, which would cause the erasure of any pre-existing variables, strings, etc. in memory. To provide this, enter "X = 5" in direct mode, then start the program without erasing memory by entering "GOTO 10". Put in any simple BASIC instruction, such as PRINT "DONE", when line 110 asks for it. When the program has finished, enter PRINT X in direct mode. The value 5 will be returned, showing that it is still in memory.
Let's now look at the program to see how it works. The first few lines are arranged so that the changeable line is as near the start of the program as possible. This makes its addresses easy to find (e.g. by using the machine-language monitor), and also protects them from being messed around with by any editing of the rest of the program. Lines 100 to 130 take the input instruction, in string form, prefix it with "GOTO 200", and then POKE it, letter by letter, into the second cassette buffer in the PET starting several characters from the start of the buffer. This buffer is used by the program for one of its originally intended purposes, as an input/output device. Line 140 contains a set ol POKEs which "persuade" the PET that a second cassette unit is present, that its "Play" key is pressed, and that this is the device from which it should take its next input and to which it should make its next output, starting at the beginning of the buffer.
At line 150, an END instruction is encountered. This makes the PET print READY into the start of the second cassette buffer and then to take the instructions which are waiting for it in the later locations in the buffer. These are first translated into token form (just what we wanted!) and entered into another buffer, from which they are later read by the routines which execute BASIC instructions. However, the first instruction to be executed is GOTO 200, which re-starts the program and leaves the instructions which we want to put into line 20, in token form, in the basic input buffer.
Line 200 restores the keyboard as the PET's input device and the screen as its output device. Lines 210 to 260 copy the desired text from the basic input buffer into the addresses occupied by line 20, then line 300 fills the remainder of these addresses with blanks. Finally, line 400 demonstrates that the new line actually works, and the machine prints the word READY on the screen as the program ends.
There is an obvious criticism which can be made of this program as it stands. Why go to the trouble of copying the instructions into line 20 when they could have been executed directly from the basic input buffer? This is a valid criticism, provided the instructions are to be executed only once, and that they can legally be performed in direct mode. In practical applications of this technique, however, one or the other of these conditions is often not true.
So much for the mechanics of simple selfmodifying programs. Their potential usefulness is great. They represent a class of interactive programs which allow the user not only to supply the values of variables and to make simple choices, but also to give precise logical instructions to the program as it operates.
Probably the simplest applications are in general mathematical programs. These can easily be written to draw the graph of any function, to use an iterative method to solve any equation, or any similar task. The program asks the user to enter the equation he is interested in, and then writes this into one of its own lines. This line can later be executed as many times as necessary for the program to complete its job.
I have recently written a self-modifying program with a very different purpose: to teach students how to set up computer programs in the form of flow-charts. The program allows a student to draw a flow-chart on the PET screen, with BASIC instructions placed on the diagram in the appropriate places. When the diagram is complete, its instructions can be executed without the student having to write a conventional program. The PET simply follows the logic lines on the diagram. When an instruction is encountered, it is written into one of several modifiable lines in the main program and executed appropriately.
I am sure there are thousands of other applications, but I'll leave them for you to discover…