10 GOSUB 610
20 COLOUR 3+128:CLS
30 LET BG=2:LET FG=1:LET T=0:LET L=3:LET LW=W-3:GOSUB 280
40 COLOUR 2+128:COLOUR 0
50 PRINT TAB(1,1);"LEVEL GENERATOR";
60 PRINT TAB(1,2);"THIS IS LEVEL:";LE;
70 PRINT TAB(1,3);"PRESS H FOR HELP"
80 LET BG=3:LET FG=2:LET T=5:LET L=15:LET LW=15:GOSUB 280
90 LET X=1:LET Y=1
100 LET I$=INKEY$(0)
110 IF I$="H" THEN GOSUB 360
120 IF I$="A" AND Y>1 THEN LET Y=Y-1
130 IF I$="Z" AND Y<15 THEN LET Y=Y+1
140 IF I$="N" AND X>1 THEN LET X=X-1
150 IF I$="M" AND X<15 THEN LET X=X+1
160 IF I$>"/" AND I$<":" THEN GOSUB 230
170 COLOUR 3+128:COLOUR 0
180 PRINT TAB(X,Y+5);CHR$(OS);
190 PRINT TAB(X,Y+5);CHR$(R(X,Y));
200 IF I$="S" AND IX>0 THEN GOSUB 450:GOTO 20
210 IF I$<>"F" THEN GOTO 100
220 STOP
230 LET I=VAL(I$)
240 IF I=9 THEN LET I=8+RND(3)
250 IF I=5 THEN LET IX=X:LET IY=Y
260 LET R(X,Y)=C0+I
270 RETURN
280 PRINT TAB(0,T);
290 COLOUR FG+128:PRINT LEFT$(B$,LW+2)
300 COLOUR BG+128:COLOUR FG
310 FOR I=1 TO L
320 PRINT TAB(0);CHR$(OS);LEFT$(B$,LW);CHR$(OS)
330 NEXT I
340 COLOUR FG+128:PRINT LEFT$(B$,LW+2);
350 RETURN
360 COLOUR 128+1:COLOUR 3
370 FOR H=1 TO 10
380 PRINT TAB(1,4);H$(H);:GOSUB 430
390 PRINT TAB(1,4);LEFT$(B$,W-2);
400 NEXT H
410 COLOUR 3
420 RETURN
430 LET G$=INKEY$(0):IF G$="" THEN GOTO 430
440 RETURN
450 PRINT TAB(1,4);"ONE MOMENT PLEASE.";
460 LET S$=""
470 FOR J=1 TO 15
480 FOR K=1 TO 15
490 LET S$=S$+CHR$(R(K,J))
500 NEXT K
510 NEXT J
520 LET S$=S$+CHR$(IX+OS):LET S$=S$+CHR$(IY+OS)
530 LET S$=S$+CHR$(LE+OS)
540 PRINT TAB(1,4);"ANY KEY TO SAVE ";:GOSUB 430
550 LET S=OPENOUT "LEVEL"
560 PRINT#S,S$
570 CLOSE#S
580 PRINT TAB(1,4);LEFT$(B$,W)
590 LET LE=LE+1:GOSUB 700
600 RETURN
610 DIM R(15,15),H$(10)
620 GOSUB 790
630 DATA "PRESS ANY KEY","TO MOVE A Z N M","1 WALL 2 VASE"
640 DATA "3 CHEST 4 * IDOL *","5 WAY IN 6 EXIT","7 TRAP","8 SAFE PLACE"
650 DATA "9 GUARD","0 TO ERASE","S TO SAVE"
660 LET LE=1
670 FOR I=1 TO 10
680 READ H$(I)
690 NEXT I:GOSUB 810
700 FOR J=1 TO 15
710 FOR K=1 TO 15
720 LET R(J,K)=C0
730 NEXT K
740 NEXT J
750 LET IX=0:LET IY=0
760 LET B$="":FOR I=1 TO W:LET B$=B$+" ":NEXT I
770 RETURN
790 OS=224:C0=OS+6:W=20
795 MODE 5:VDU 23,0,8202;0;0;0;
800 RETURN
810 REM READ THE CHARACTERS
820 VDU 23,224:FOR I=0 TO 7:READ A:VDU A:NEXT I
830 FOR I=0 TO 11:VDU 23,230+I
840 FOR J=0 TO 7:READ A:VDU A:NEXT J
850 NEXT I:RETURN
1000 DATA 255,255,255,255,255,255,255,255
1010 DATA 0,0,0,0,0,0,0,0
1020 DATA 85,170,85,170,85,170,85,170
1030 DATA 0,60,24,60,126,126,126,60
1040 DATA 0,56,100,114,95,73,41,31
1050 DATA 20,42,20,20,93,93,62,99
1060 DATA 60,126,255,255,255,253,255,255
1070 DATA 60,102,195,129,129,129,133,129
1080 DATA 129,66,36,0,0,36,66,129
1090 DATA 0,60,66,66,66,66,60,0
1100 DATA 76,158,170,190,84,30,37,88
1110 DATA 0,56,84,124,56,44,68,102
1120 DATA 0,8,28,42,127,85,65,34
Text Editor Prevents “Starvation” and Programming Hassles
By Donald Fitchhorn
MITS
Computer Notes, October 1977.
Why I wrote an editor
My main objective in writing an editor was to complement DISK EXTENDED BASIC’s built-in EDIT feature. This feature is invaluable if the location of a needed change is already known. But what about our friend Bob’s problem? He knew WHAT the problem was but not WHERE to find it. What he needs is a program that will search through the entire file until it finds what he wants. Since there isn’t such an editor in Disk Extended BASIC, I wrote my own in BASIC so that it can be easily changed.
The search command and others like it where the beginnings of my editor, which now has 14 commands. But before we get into an explanation of this editor, let’s review the definition of an editor.
What is an editor?
An editor is a program which permits the addition and deletion of lines and characters in one file to make another file. The second file is the new up-to-date file, and the first file is retained as the backup file. Some editors permit the creation of new files and/or use multiple input files. An editor can be as extensive or as minimal as is necessary for an application.
PROGRAM FILES
The EDIT program works on ASCII program files. A program file is any file that looks like a program to BASIC. (see EXAMPLE 1.) BASIC doesn’t care if the whole file is REMark statements; it’s only concerned with whether or not there is a line number at the beginning of each line. (See EXAMPLE 2.)
(NOTE: The EDIT program cannot handle files saved in binary. Save all files in ASCII, i.e. SAVE "FILENAME", 0, A)
EXAMPLE #1
10 'THIS IS A PROGRAM FILE
20 FOR I = 1 TO 100000
30 PRINT I;RND(I)*1000;I*RND(I)
40 ' THIS PRINTS SOME NUMBERS
50 NEXT
60 END
EXAMPLE #2
10 'This is a document program file
20 'here is the
30 'text of the
40 'document !
50 'this is the end.
EXAMPLE #3
LOWER CASE IS USER TYPED
run"edit
EDIT -- VERSION 1.0
INPUT FILE NAME?time
>r
EOF1
CLEARING..........
>/1
5 LPRINT"MINUTES","HUNDREDTHS",,"MINUTES","HUNDREDTHS"
10 FORI=6TO30STEP-1
20 J=INT(I/60*100)
21 K=INT((I/30)/60*100)
30 LPRINTI,J,,I-30,K
40 NEXT
>gJ
20 J
>cL2
30 L2=INT(I/60*100)
>gJ
30 LPRINTI,J
>cL2
30 LPRINTI,L2,,I-30,K
>gJ
EOB
>x
BACKUP FILE NAME?time.bak
OK
load"time
OK
list
5 LPRINT"MINUTES","HUNDREDTHS",,"MINUTES","HUNDREDTHS"
10 FOR I=60TO30STEP-1
20 J=INT(I/60*100)
21 K=INT((I-30)/60*100)
30 LPRINTI,J,,I-30,K
40 NEXT
OK
Saving documents as programs (EXAMPLE 2 format) allows them to be loaded with BASIC. This allows BASIC to be used to alter, delete, and add lines. Of course, line numbers on the finished document may not be wanted, so a short program that reads the file and PRINTs MID$(LINE$, INSTR(LINE$,"'")+1 will print everything to the right of the (‘). Be sure to use LINE INPUT instead of INPUT when reading up LINE$ to prevent truncation because of commas in the text. The (‘)’s in EXAMPLE 2 are necessary if the program file is to be loaded and saved by BASIC. Without the (‘), BASIC will modify the text line.
BASIC won’t allow lines to be moved around within the file without retyping each line. But with the EDIT program, line numbers can be changed to whatever is wanted. When the edited file is loaded into BASIC, BASIC will put the lines in numerical order.
Internal structure
The EDIT program maintains a double-linked list in memory. Each line (L1$(X)) has a pointer to the previous line [M1(X,0)] and to the next line [M1(X,1)] added to it when it is read in. The array (L1$) that the lines are kept in is divided into two parts — ACTIVE CELLS, which have data in them, and INACTIVE CELLS, available for use as data lines. Deleted lines are linked into the INACTIVE CELLS from the ACTIVE CELLS. Inserted lines are written into the first available INACTIVE CELL and then linked into the ACTIVE CELLS. Pointers are maintained for FIRST ACTIVE CELL (LN), FIRST INACTIVE CELL (IN), DOT or position within cell (H) and CELL that DOT is in (J).
Commands
My editor, like many others, uses a single letter to select a command. For example, A will advance DOT one line. Most commands may be preceded by a number or a slash (/) to indicate that hey should be executed more than once. 13A will advance DOT 13 lines. OA will position DOT at the beginning of the current line. /A will advance DOT to the end of the page. All commands that allow a prefix will default to one if none is specified. The following is an explanation of the commands. (See TABLE 1 for a list of commands in alphabetical order. See TABLE 2 for a list divided into four main groups).
TABLE #2
COMMANDS THAT AFFECT LINES
I - Inserts lines until, backslash (\) is entered.
K - Kills entire line. #K & /K are legal.
L - Lists entire line. #L & /L are legal.
V - Prints the current line up to DOT.
(Verify's the position of DOT).
COMMANDS THAT AFFECT CHARACTERS
C - Changes last character string gotten with G command to
something else. Use: Ghere
Cthere
will change here to there.
D - Delete a character. #D legal.
G - Get string. Searches for occurrence of string.
Use: 3Gstuff
Finds third occurrrence of stuff. #G legal.
I - Insert characters at DOT. Use: Iabcde
Will insert abcde in current line at DOT.
COMMANDS THAT MOVE DOT
A - Moves DOT forward or back the number of lines
specified. 0A, #A, -#A, /A(same as E) legal.
B - Moves DOT to beginning of page .
E - Moves DOT to end of page. (same as /A)
J - Moves DOT forward or back the number of characters
specified. 0J, #J, -#J legal.
COMMANDS THAT READ AND WRITE THE FILE
N - Writes out current page and reads in next page.
R - Read in a new page.
X - Writes out current page then reads and writes until
end of file. Closes and renames files.
How to use edit’s commands
The commands in EDIT are broken up into four groups, as shown in TABLE 2. The use of these commands will be explained in that order.
The commands that affect lines will work on one line at a time. Or, in the case of K & L, they may be preceded by a number or slash (/) to indicate that they are to be performed on several succeeding lines. TO begin insertion of lines, type I <return> and then the lines to insert. To tell EDIT that the last line to be inserted has been entered, type backslash ().
Of the commands that affect characters I & C cannot have a # prefix. #D deletes # characters to the right of DOT. G & C work together to allow getting a string and then changing it to something else. G moved DOT ahead of the Nth occurrence of the string. Then C can be used to change the nth occurrence to a new string. It works like C, except instead of changing one string for another, it inserts a new string ahead of DOT.
The commands that move DOT are A, B, E, and J. B & E require no other specifiers. They simply move DOT to the beginning or end of the current page. A & J move DOT forward or back a specified number of lines or characters.
The commands N, R, and X read and write the files. R reads the input file until EOF, until it has read 50 lines, or 2000 characters. It then clears and resets the INACTIVE CELLS. N writes out the current page and then executes and R (reads in the next page). X does a series of N’s until the input file is EOF. Then it closes the files and renames them by giving the input file a backup name and the output file the input file’s old name.
The best way to learn to use EDIT is to load it in and try a few commands. (See EXAMPLE 3). Once you get the hang of it, the power and versatility will be well worth the time it took t type it in.
The other side of the coin
All of the above is really wonderful isn’t it? But this program is not without its limitations.
The commands may or may not work the way the user expects them to. This is a typical problem in any new program because the commands take some getting used to. If, after trying it for a while, the user wants a command to work differently or wants to use another command, just remember that the program is written in BASIC, so modifications are easy.
The program allows working on large files by breaking the file into pages. This works out well except for one thing. No matter what the user does, string space is used. Eventually, all available space will be used. At that time, BASIC has to look through all of the string space, shuffling things around and freeing up no-longer-used bytes so that the program will have some more space. This is commonly referred to as GARBAGE COLLECTING. IT can happen at the most unlikely of times and can take as long as five minutes. Unfortunately to the unsuspecting user, it looks as though the program has bombed BASIC out because CTRL-C and RESET don’t help solve the problem. But patience is rewarded and the program does come back to life.
Modifications and improvements
I will leave these up to the reader because they are easy to make. For example, suppose a command is needed to exit the program gracefully without making any changes. Follow these steps:
Pick a command character. How about Q for quit?
Alter line 250 to reflect where the program should go if it sees a Q. Let’s make this 1000.
Put in the necessary code to perform the command. 1000 CLOSE:CLEAR 200:END
Save the new program.
PROGRAM LISTING
10 ' == WRITTEN BY D. L. FITCHHORN ==
15 ' = ***** **** ***** ***** =
20 ' = * * * * * =
25 ' = *** * * * * =
30 ' = * * * * * =
35 ' = ***** **** * * =
40 ' == PROGRAMMER - MITS, INC ==
45 '
50 DEFINT A-Z
55 CLEAR 15000
60 DIM L1$(100),L2$(13),M1(100,1):FOR I=0 TO 13:READ L2$(I):NEXT
65 DATA
A - ADVANCE,B - BEGINNING,C - CHANGE,D - DELETE,E - END,G - GET,I - INSERT,
J - JUMP, K - KILL,L -LIST,N = NEXT,R - READ,V - VERIFY,X - EXIT
70 PRINT "EDIT -- VERSION 1.0":PRINT
75 LINE INPUT "INPUT FILE NAME?"; N1$
80 OPEN "I",1,N1$
85 N2$="EDIT.TMP"
90 OPEN "O",2,N2$:PRINT #2,""
95 I=1:J=1:H=1
100 '---------------------------------------------------------INPUT COMMAND
105 K=0
110 IF A$="" THEN FOR Q9=1 TO 5:PRINT CHR$(7);:NEXT:LINE INPUT ">"; A$
115 A=0:J1=1:U=0:T=1
120 IF A$="" THEN 105 ELSE I9=INSTR(A$,"\\"):IF I9<>0 THEN S$=LEFT$(A$,I9-1):
A$=MID$(A$,I9+1) ELSE S$=A$:A$=""
125 SS=ASC(S$)
130 IF 64<SS THEN IF 96<SS THEN SS=SS-32:GOTO 170 ELSE GOTO 170
135 T=VAL(S$)
140 S=LEN(STR$(T))
145 IF T=0 THEN IF LEFT$(S$,1)="/" THEN T=400
150 S$=MID$(S$,S)
155 IF T<0 THEN S$=MID$(S$,2)
160 GOTO 125
165 ' .A .B .C .D .E <F> .G <H> .I .J .K .L <M>
170 ON SS-64 GOTO 195,235,245,265,280,180,290,180,320,360,385,425,180,
445,180,180,180,465,180,180,180,515,180,525,180,180
175 ' .N <O> <P> <Q> .R <S> <T> <U> .V <W> .X <Y> <Z>
180 FOR N=0 TO 12:PRINT L2$(N):NEXT:GOTO 105
185 '
190 '-----------------------------------------------------------A COMMAND
195 H=1:IF T=0 THEN GOTO 105 ELSE IF T<0 THEN J1=-1
200 FOR I3=0 TO T-J1 STEP J1
205 IF M1(J,0)=0 AND J1=-1 THEN GOTO 105
210 IF M1(J,1)=-1 AND J1=1 THEN J=M1(J,0):GOTO 105
215 IF J1=1 THEN J=M1(J,1) ELSE J=M1(J,0)
220 NEXT
225 GOTO 105
230 '-----------------------------------------------------------B COMMAND
235 J=LN:H=1:GOTO 105
240 '-----------------------------------------------------------C COMMAND
245 S$=MID$(S$,2):IF K=0 THEN S=LEN(S$):K=H
250 IF K=1 THEN L1$(J)=S$+MID$(L1$(J),K+S) ELSE L1$(J)=LEFT$(L1$(J),K-1)+
S$+MID$(L1$(J),K+S)
255 PRINT L1$(J):H=K+LEN(S$):GOTO 105
260 '-----------------------------------------------------------D COMMAND
265 IF H=1 THEN L1$(J)=MID$(L1$(J),H+T):
ELSE L1$(J)=LEFT$(L1$(J),H-1)+MID$(L1$(J),H+T)
270 GOTO 105
275 '-----------------------------------------------------------E COMMAND
280 IF M1(J,1)=-1 THEN H=1:GOTO 105 ELSE J=M1(J,1):GOTO 280
285 '-----------------------------------------------------------G COMMAND
290 S$=MID$(S$,2):S=LEN(S$):IF S=0 THEN GOTO 105
295 K=INSTR(H,L1$(J),S$):IF K=0 THEN IF M1(J,1)=-1
THEN PRINT "EOB":A$="":GOTO 105 ELSE J=M1(J,1):H=1:GOTO 295
300 U=U+1:IF U<T THEN H=K+S:GOTO 295
305 PRINT LEFT$(L1$(J),K+S-1):H=K
310 GOTO 110
315 '-----------------------------------------------------------I COMMAND
320 IF MID$(S$,2)<>"" THEN 345 ELSE I2=J:IF J=LN THEN LN=IN
325 LINE INPUT L1$(IN):IF L1$(IN)"\\" THEN 105
330 I3=M1(IN,1):M1(IN,1)=I2
335 M1(IN,0)=M1(I2,0):M1(I2,0)=IN:I4=M1(IN,0)
340 M1(I4,1)=IN:IN=I3:M1(I3,0)=0:GOTO 325
345 IF H1=1 THEN L1$(J)=MID$(S$,2)+L1$(J)
ELSE L1$(J)=LEFT$(L1$(J),H-1)+MID$(S$,2)+MID$(L1$(J),H)
350 H=H+LEN(S$):GOTO 105
355 '-----------------------------------------------------------J COMMAND
360 IF T=0 THEN H=1:GOTO 105
365 H=H+T:IF H<1 THEN H=1
370 IF H>LEN(L1$(J)) THEN H=LEN(L1$(J))
375 GOTO 105
380 '-----------------------------------------------------------K COMMAND
385 H=1:I2=J:I3=M1(J,0):FOR J1=1 TO T
390 IF M1(J,1)=-1 THEN GOTO 410
395 I4=M1(J,1):L1$(J)="":M1(I4,0)=I3
400 M1(IN,0)=J:M1(J,0)=0:M1(J,1)=IN:IN=J
405 J=I4:NEXT
410 IF I2=LN THEN LN=J ELSE M1(I3,1)=J
415 GOTO 105
420 '-----------------------------------------------------------L COMMAND
425 I2=J:FOR J1=1 TO T
430 PRINT L1$(I2):IF M1(I2,1)=-1 THEN GOTO 105 ELSE I2=M1(I2,1):NEXT
435 GOTO 105
440 '-----------------------------------------------------------N COMMAND
445 I2=LN
450 IF M1(I2,1)=-1 THEN GOTO 465 ELSE PRINT #2,L1$(I2):I2=M1(I2,1)
455 GOTO 450
460 '-----------------------------------------------------------R COMMAND
465 J=1:A#=0:LN=1:I=1:FE=0:GOSUB 470:GOTO 105
470 IF EOF(1) THEN PRINT "EOF1":I=I-1:FE=1:GOTO 495
475 LINE INPUT #1,L$:IF L$="" THEN GOTO 470
480 A#=A#+LEN(L$)
485 L1$(I)=L$:M1(I,0)=I-1:IF I=1 THEN 490 ELSE M1(I-1,1)=I
490 IF I=50 OR A#>2000 THEN GOTO 495 ELSE I=I+1:GOTO 470
495 M1(I,1)=I+1:I=I+1:L1$(I)="END OF BUFFER":M1(I,0)=I-1:M1(I,1)=-1:H=1:IN=I+1
500 FOR I2=IN TO 100:M1(I2,1)=I2+1:M1(I2,0)=I2-1:NEXT
505 M1(IN,0)=0:M1(I2-1,1)=-1:RETURN
510 '-----------------------------------------------------------V COMMAND
515 PRINT LEFT$(L1$(J),H):GOTO 105
520 '-----------------------------------------------------------X COMMAND
525 I2=LN
530 IF M1(I2,1)=-1 THEN GOTO 535 ELSE PRINT #2,L1$(I2):I2=M1(I2,1):GOTO 530
535 IF FE=0 THEN I=1:A#=0:GOSUB 470 GOTO 525
540 CLOSE:ON ERROR GOTO 555:LINE INPUT "BACKUP FILE NAME?";N3$:KILL N3$
545 NAME N1$ AS N3$
550 NAME N2$ AS N1$: CLEAR 200:END
555 IF ERR = 53 THEN GOTO 545: ELSE:ON ERROR GOTO 0
A lot of people have just gotten into having their own computer. Often they don’t know too much about software and particularly systems software, but would like to be able to program in something other than machine language. The TINY BASIC project is aimed at you if you are one of these people. Our goals are very limited— to provide a minimal BASIC-like language for writing simple programs. Later we may make it more complicated, but now the name of the game is keep it simple. That translates to a limited language (no floating point, no sines and cosines, no arrays, etc, I and even this is a pretty difficult undertaking.
Originally we had planned to limit ourselves to the 8080, but with a variety of new machines appearing at very low prices, we have decided to try to make a portable TINY BASIC system even at the cost of some efficiency. Most of the language processor will be written in a pseudo language which is good for writing interpreters like TINY BASIC. This pseudo language (which interprets TINY BASIC) will then itself be implemented interpretively.
To implement TINY BASIC on a new machine, one simply writes a simple interpreter for this pseudo language end not a whole interpreter for TINY BASIC.
We’d like this to be a participatory design project. This sequence of design notes follows the project which we are doing here at PCC. There may well be errors in content and concept. If you’re making a BASIC along with us, we’d appreciate your help and your corrections.
Incidentally, were we building a production interpreter or compiler, we would probably structure the whole system quite differently. We chose this scheme because it is easy for people to change without access to specialized tools like parser generator programs.
THE TINY BASIC LANGUAGE
There isn’t much to it. TINY BASIC looks like BASIC but all variables are integers. There are no functions yet (we plan to add RND, TAB, and some others later). Statement numbers must be between 1 and 255 so we can store them in a single byte. LIST only works on the whole program. There is no FOR-NEXT statement. We’ve tried to simplify the language to the point where it will fit into a very small memory so impecunious, tyros can use the system.
The language design was done in consultation with the PCC Dragon. We asked him what he had to have to write a useful program, then we took some of it away. Some other things (computed GOTO labels, for example) come free with our proposed implementation and might be useful.
The boxes shown define the language. The guide gives a quick reference to what we will include. The formal grammar defines exactly what is a legal TINY BASIC statement. The grammar is important because our interpreter design will be based upon it.
A SIMPLE TINY BASIC PROGRAM
100 PRINT "POWERS”
110 INPUT N
120 PRINT N*N, N*N*N
130 IF N<>0 THEN GOTO 110
140 END
IT’S ALL DONE WITH MIRRORS – OR HOW TINY BASIC WORKS
All the variables in TINY BASIC: the control information as to which statement is presently being executed and how the next statement is to be found, the return addresses of active GOSUBS — all this information constitutes the state of the TINY BASIC interpreter.
There are several procedures which act upon this state. One procedure knows how to execute any TINY BASIC statement. Given the starting point in memory of a TINY BASIC statement, it will execute it changing the state of the machine as required. For example,
100 LET S = A+6 (CR)
would change the value of S to the sum of the contents of the variable A and the integer 6, and sets the next line counter to whatever line follows 100, if the line exists.
A second procedure really controls the interpretation process by telling the line interpreter what to do. When TINY BASIC is loaded, this control routine performs some initialization, and then attempts to read a lint of information from the console. The characters typed in are saved in a buffer LBUF. It first checks to see if there is a leading line number. If there is, it incorporates the line into the program by first deleting the line with the same line number (if it is present) then inserting the new line if it is of nonzero length. If there is no line number present, it attempts to execute the line directly. With this strategy, all possible commands, even LIST end CLEAR and RUN are possible inside programs. Suicidal programs are also certainly possible.
IMPLEMENTATION STRATEGIES AND ONIONS
When you write a program in TINY BASIC there is an abstract machine which is necessary to execute it. If you had a compiler it would make in the machine language of your computer a program which emulates that abstract machine for your program. An interpreter implements the abstract machine for the entire language and rather than translating the program once to machine code it translates it dynamically as needed. Interpreters are programs and as such have theirs as abstract machines. One can find a better instruction set than that of any general purpose computer for writing a particular interpreter. Then one can write an interpreter to interpret the instructions of the interpreter which is interpreting the TINY BASIC program. And if your machine is microprogrammed (like PACE), the machine which is interpreting the interpreter interpreting the interpreter interpreting BASIC is in fact interpreted.
TINY BASIC GRAMMAR
The things in bold face stand for themselves. The names in lower case represent classes of things. “::=” is read “is defined as”. The asterisk denotes zero or more occurrences of the object to its immediate left. Parenthesis group objects, ∅ is the empty set. | denotes the alternative (the exclusive-or).
A BREAK from the console will interrupt execution of the program.
QUICK REFERENCE GUIDE FOR TINY BASIC
LINE FORMAT AND EDITING
Lines without numbers executed immediately
Lines with numbers appended to program
Line numbers must be 1 to 255
Line number alone (empty line) deletes line
Blanks are not significant, but key words must contain no unneeded blanks
“<-” deletes last character
XC deletes the entire line
EXECUTION CONTROL
CLEAR delete all lines end data RUN run program LIST list program
EXPRESSIONS
Operators
Arithmetic
+ - * /
Relational
< > = >= <= <>
Variables
A...Z (26 only)
All arithmetic is modulo 215 (± 32762)
INPUT / OUTPUT
PRINT X, Y, Z
PRINT "A STRING"
PRINT "THE ANSWER IS"
INPUT X
INPUT X, Y, Z
ASSIGNMENT STATEMENTS
LET X=3
LET X= -3 + 5 * Y
CONTROL STATEMENTS
GOTO X+10
GOTO 35
GOSUB X+35
GOSUB 50
RETURN
IF X>Y THEN GOTO 30
This multilayered, onion-like approach gains two things: the interpreter for the interpreter is smaller and simpler to write than an interpreter for all of TINY BASIC, so the resultant system is fairly portable. Secondly, since the major part of the TINY BASIC is programmed in a highly memory efficient, tailored instruction set, the interpreted TINY BASIC will be smaller than direct coding would allow. The cost is in execution speed, but there is not such a thing as a free lunch.
LINE STORAGE
The TINY BASIC program is stored, except for line numbers, just as it is entered from the console. In some BASIC interpreters, the program is translated into an intermediate form which speeds execution and saves space. In the TINY BASIC environment, the code necessary to provide the transformation would easily exceed the space saved.
When a line is read in from the console device, it is saved in a 72-byte array called LBUF (Line BUFfer). At the same time, a pointer, CP, is maintained to indicate the next available space in LBUF. Indexing is, of course, from zero.
Delete the leading blanks. If the string matches the BASIC line, advance the cursor over the matched string and execute the next IL instruction, If the match fails, continue at the IL instruction labeled lbl.
The TINY BASIC program is stored as an array called PGM in order of increasing line numbers. A pointer, PGP, indicates the first free place in the array. PGP=0 indicates an empty program; PGP must be less than the dimension of the array PGM, The PGM array must be reorganized when new lines are added, tines replaced, or lines ere deleted.
Insertion and deletion are carried on simultaneously. When a new line is to be entered, the PGM array searches for a line with a line number greater than or equal to that of the new line.Notice that lines begin at PGM (0) and at PGM (j+1) for every j such that PGM (j) = [carriage return]. If the line numbers are equal, then the length of the existing line is computed. A space equal to the length of the new line is created by moving all lines with line numbers greater than that of the line being inserted up or down as appropriate. The empty line is handled as a special case in that no insertion is made.
ERRORS AND ERROR RECOVERY
There are two places that errors can occur, if they occur in the TINY BASIC system, they must be captured and action taken to preserve the system. If the error occurs in the TINY BASIC program entered by the user, the system should report the error and allow the user to fix their problem. An error in TINY BASIC can result from a badly formed statement, an illegal action (attempt to divide by zero, for example), or the exhaustion of some resource such as memory space. In any case, the desired response is some kind of error message. We plan to provide a message of the form:
! mmm AT nnn
where mmm is the error number and nnn is the line number at which it occurs. For direct statements, the form will be:
! mmm
since there is no line number.
Some error indications we know we will need are:
Syntax error
Missing line
Line number too large
Too many GOSUBs
RETURN without GOSUB
Expression too complex
Too many lines
Division by zero
THE BASIC LINE EXECUTOR
The execution routine is written in the interpretive language, IL. It consists of a sequence of instructions which may call subroutines written in IL, or invoke special instructions which are really subroutines written in machine language. Two different things are going on at the same time. The routines must determine if the TINY BASIC line is a legal one and determine its form according to the grammar; secondly, it must call appropriate action routines to execute the line. Consider the TINY BASIC statement:
GOTO 100
At the start of the line, the interpreter looks for BASIC key words {LET, GO. IF, RETURN, etc.) In this case, it finds GO, and then finds TO. By this time it knows that it has found a GOTO statement. It then calls the routine EXPR to obtain the destination line number of the GOTO. The expression routine calls a whole bunch of other routines, eventually leaving the number 100 (the value of the expression) in a special place, the top of the arithmetic expression stack. Since everything is legal, the XFER operator is invoked to arrange for the execution of line 100 (if it exists) as the next line to be executed.
Each TINY BASIC statement is handled similarly. Some procedural section of an IL program corresponds to tests for the statement structure and acts to execute the statement.
ENCODING
There are a number of different considerations in the TINY BASIC design which fall in this general category. The problem is to make efficient use of the bits available to store information without loosing out by requiring a too complex decoding scheme.
In a number of places we have to indicate the end of a string of characters (or else we have to provide for its length somewhere). Commonly, one uses a special character (NUL = 00H for example) to indicate the end. This costs one byte per string but is easy to check, A better way depends upon the fact that ASCII code does not use the high order bit; normally It is used for parity on transmission. We can use it to indicate the end (that is, last character) of a string. When we process the characters we must AND the character with 07FH to scrub off the flag bit.
The interpreter opcodes can be encoded into a single byte. Operations fall into two distinct classes–those which call machine language subroutines, and those which either call or transfer within the IL language itself, The diagram indicates one encoding scheme. The CALL operations have been subsumed into the IL instruction set. Addressing is shown to be relative to PC for IL operations. Given the current IL program size, this seems adequate. If it is not, the address could be used to index an array with the ML class instructions.
TINY BASIC INTERPRETIVE OPERATIONS
TST lbl,'string'
Delete leading blanks.
If string matches the BASIC line, advance cursor over the matched string and execute the next IL instruction. If a match fails, execute the It instruction at the labeled lbl.
CALL lbl
Execute the IL subroutine starting at lbl. Save the IL address following the CALL on the control stack.
RTN
Return to the IL location specified by the top of the control stack.
DONE
Report a syntax error if after deletion leading blanks the cursor is not positioned to read a carriage return.
JMP lbl
Continue execution of IL at the label specified.
PRS
Print characters from the BASIC text up to but not including the closing quote mark. If a (CR) is found in the program text, report an error. Move the cursor to the point following the closing quote.
PRN
Print number obtained by popping the top of the expression stack.
SPC
Insert spaces to move the print head to next zone,
NLINE
Output CRLF to Printer.
NXT
If the present mode is direct (line number zero), then return to line collection. Otherwise, select the next sequential line and begin interpretation.
XFER
Test value at the top of the AE stack to be within range. If not, report an error. If so, attempt to position cursor at that line. If it exists, begin interpretation there; if not report an error.
SAV
Place present line number on SBRSTK. Report overflow as error.
RSTR
Replace current line number with value on SBRSTK, If stack is empty, report error,
CMPR
Compare AESTK(SP), the top of the stack, with AESTK (SP – 2) as per the relation indicated by AESTK(SP – 1). Delete all from stack. If condition specified did not match, then perform NXT action.
INNUM
Read a number from the terminal and push its value onto the AESTK.
FIN
Return to the line collect routine.
ERR
Report syntax error and return to line collect routine.
ADD
Replace top two elements of AESTK by their sum.
SUB
Replace top two elements of AESTK by their difference.
NEG
Replace top of AESTK with its negative.
MUL
Replace top two elements of AESTK by their product
DIV
Replace top two elements of AESTK by their quotient
STORE
Place the value at the top of the AESTK into the variable designated by the index specified by the value immediately below it. Delete both from the stack.
TSTV lbl
Test for variable (i.e. letter) if present. Place its index value onto the AESTK and continue execution at next suggested location. Otherwise, continue at lbl.
TSTN lbl
Test for number. If present, place Its value onto the AESTK and continue execution at next suggested location. Otherwise, continue at lbl.
IND
Replace top of stack by variable value if indexes.
LST
List the contents of the program area.
INIT
Performs global initialization
Clears program area, empties GOSUB stack, etc.
GETLINE
Input a line to LBUF.
TSTL lbl
After editing leading blanks, look for a line number. Report error if invalid; transfer to lbl if not present.
INSRT
Insert line after deleting any line with same line number.
XINIT
Perform initialization for each stated execution.
Empties AEXP stack.
A STATEMENT EXECUTOR WRITTEN IN IL
This program in IL will execute a TINY BASIC statement. The operators TST, TSTV, TSTN, and PRS all use a cursor to find characteristics of the TINY BASIC line. Other operations (NXT, XPER) move the cursor so it points to another TINY BASIC line.
THE IL CONTROL SECTION
START: INIT ; INITIALIZE
NLINE ; WRITE CR/LF
CO: GETLINE ; WRITE PROMPT & GET A LINE
TSTL XEC ; TEST FOR LINE NUMBER
INSRT ; INSERT IT (MAY BE DELETE)
JMP CO
STMT: XINIT ; INITIALIZE FOR EXECUTION
STATEMENT EXECUTOR
STMT: TST S1,'LET' ; IS STATEMENT A LET?
TSTV S16 ; YES. PLACE VAR ADDRESS ON AESTK.
CALL EXPR ; PLACE EXPR VALUE ON AESTK.
DONE ; REPORT ERROR IF (CR) NOT NEXT.
STORE ; STORE RESULT.
NXT ; AND SEQUENCE TO NEXT.
S1: TST S3,'GO' ; GOTO OR GOSUB?
TST S2,'TO' ; YES ...TO OR ...SUB.
CALL EXPR ; GET LABEL.
DONE ; ERROR IF (CR) NOT NEXT.
XPER ; SET UP AND JUMP.
S2: TST S14,'SUB' ; ERROR IF NO MATCH.
CALL EXPR ; GET DESTINATION.
DONE ; ERROR IF (CR) NOT NEXT.
SAV ; SAVE RETURN LINE.
XFER ; AND JUMP.
S3: TST S8,'PRINT' ; PRINT.
S4: TST S7,'"' ; TEST FOR QUOTE.
PRS ; PRINT STRING.
S5: TST S6,',' ; IS THERE MORE?
SPC ; SPACE TO NEXT ZONE.
JMP S4 ; YES. JUMP BACK.
S6: DONE ; NO, ERROR IF NO (CR).
NLINE
NXT
S7: CALL EXPR ; GET EXPR VALUE.
PRN ; PRINT IT.
JMP S5 ; IS THERE MORE?
S8: TST S9,'IF' ; IF STATEMENT.
CALL EXPR ; GET EXPRESSION.
CALL RELOP ; DETERMINE OPR AND PUT ON STK.
CALL EXPR ; GET EXPRESSION.
CMPR ; PERFORM COMPARISON-PERFORMS NEXT IF FALSE.
JMP STMT ; GET NEXT STATEMENT.
S9: TST S12,'INPUT' ; INPUT STATEMENT.
S10: CALL VAR ; GET VAR ADDRESS.
INNUM ; MOVE NUMBER FROM TTY TO AESTK.
STORE ; STORE IT.
TST S11,',' ; IS THERE MORE?
JMP S10 ; YES.
S11: DONE ; MUST BE (CR).
NXT ; SEQUENCE TO NEXT.
S12: TST S13,'RETURN' ; RETURN STATEMENT.
DONE ; MUST BE (CR).
RSTR ; RESTORE LINE NUMBER OF CALL.
NXT ; SEQUENCE TO NEXT STATEMENT.
S13: TST S14,'END'
FIN
S14: TST S15,'LIST' ; LIST COMMAND.
DONE
LST
NXT
S15: TST S16,'RUN' ; RUN COMMAND.
DONE
NXT
S16: TST S17,'CLEAR' ; CLEAR COMMAND.
DONE
JMP START
S17: ERR ; SYNTAX ERROR.
EXPR: TST E0,'-' ; TEST FOR UNARY -.
CALL TERM ; GET VALUE.
NEG ; NEGATE IT.
JMP E1 ; LOOK FOR MORE.
E0: TST E1,'+' ; TEST FOR UNARY +.
CALL TERM ; LEADING TERM.
E1: TST E2,'+'
CALL TERM ; SUM TERM.
ADD
JMP E1
E2: TST E3,'-' ; ANY MORE?
CALL TERM ; DIFFERENCE TERM.
SUB
JMP E3
E3: T2: RTN ; ANY MORE?
TERM: CALL FACT
T0: TST T1,'*'
CALL FACT ; PRODUCT FACTOR
MPY
JMP T0
T1: TST T2,'/' ; ANY MORE?
CALL FACT ; QUOTIENT FACTOR.
DIV
JMP T0
FACT: TSTV F0 ; VARIABLE.
IND ; YES, GET THE VALUE.
RTN
F0: TSTN F1 ; NUMBER, GET ITS VALUE.
RTN
F1: TST F2,'(' ; PARENTHESIZED EXPR.
CALL EXPR
TST F2,')' ; MATCHING PARENTHESIS.
RTN
F2: ERR ; ERROR.
RELOP: TST R0,'='
LIT 0 ; =
RTN
R0: TST R4,'<'
TST R1,'='
LIT 2 ; <=
RTN
R1: TST R3,'>'
LIT 3 ; <>
RTN
R3: LIT 1 ; <
RTN
R4: TST S17,'>'
TST R5,'='
LIT 5 ; >=
RTN
R5: TST R6,'<'
LIT 3 ; <>
R6: LIT 4 ; >
RTN
WE’RE NOT DONE YET
This isn’t everything one needs to make a TINY BASIC. It’s just a beginning and nothing is tested yet. We’ll have more next issue. In the meantime, we welcome your comments, ideas, letters, and corrections.
The original purpose of this project was to design and then construct computers that would be able to survive a societal collapse.
After working on the project, it became apparent that the point of the project should not be to imagine a hypothetical future, but to engage practically with the problems of the present.
The entire computing stack of the modern era is large, confusing, and unsafe. People would rather excuse themselves from having to learn about it. Who can blame them?
The first aim of the project is to be exceedingly cost effective: “if the oppressed cannot access some technology, then it is not revolutionary”.
The second aim of the project is the promotion of digital literacy. Digital literacy should promote a joyful user experience that is non-exploitative. It should also foster a sense of community–“no-one is an island”.
To this end, the scope of the people’s permacomputer project was deliberately limited to programs around or just over 100 lines. Definitely not more than 200.
The project therefore decided to investigate BASIC as the paradigmatic human-computer interface for the permacomputer.
For the application towards which it was targeted, the investigation the project has done so far into BASIC has been fruitful, and even quite surprising–the 70s/80s hobby computing scene was far richer and more creative than the project previously assumed: not less than five (5) different text editors in BASIC were unearthed!