Writing Transportable BASIC
Edward T. Ordman
Department of Mathematical Sciences
Memphis State University
If you think your programs might ever be used on another computer with a different dialect of BASIC – the suggestions in this two-part article can go a long way towards easing the transition. This month the author covers documentation, vocabulary, and readability. The article concludes next month with an overview of highly machine-sensitive issues such as input-output and graphics.
So you finally got your own computer. Unfortunately, it is not the same model you had at school. Or you've arrived at high school or college and the computer there is not the same one that your junior high school or high school had. What are you going to do with all the programs you have accumulated? My own school has just bought several of the new IBM Personal Computers – but most of the programs we have on hand were written for a mainframe or for our OSI microcomputers. Come to think of it, we are changing mainframes next semester, too!
Of course, all of these machines have a version of BASIC. (Some of them, in fact, have several versions of BASIC.) But, as is clear to anyone who has read a program written in Apple BASIC and wished he could run it on his Atari (or PET or TRS-80 or ...), all BASIC interpreters are not the same.
What is the solution? There is no ideal solution, for all cases. Some published programs are difficult to convert from one dialect to another. We can, however, in writing programs for ourselves, for friends, and perhaps even for publication, try to make our programs transportable. That is, we can write the programs so that they can be adapted to another machine with a minimum of difficulty.
A program is easily transportable from one machine to another if it can be entered and run in the second machine with no substantial rewriting – certainly no changes in the underlying logic or algorithms – and a minimum of minor changes. The program should be self-explanatory so that it can be rewritten without knowledge of the original machine – a knowledge of the machine we are rewriting it for should be enough.
I have one fairly complex simulation program that was first written about 12 years ago for a PDP-8. It has since been rewritten, by me or by others, for S-100 bus machines in CBASIC, Apple, TRS-80, IBM Personal Computers and IBM 370's, Xerox Sigma 9, PDP-11, and enough other machines that I have lost count. I suspect that it would have been forgotten after the second or third transportation to a new machine, if it had not been written so that it was usually just a matter of typing it in again.
I should warn you at the outset that all this article considers is how to write the BASIC program. It does not address the problems of getting a program from one machine to another without having to key it in again. Increasingly, it is possible to connect the two computers over a phone line, directly or via one of the dial-up timesharing services, and move the program as a text file to avoid retyping. Nevertheless, the focus of this article is transportable programming techniques.
What can you do, when writing a program, to make it easily transportable? We will divide the strategy into five main parts: 1) minimal vocabulary; 2) in-program readability; 3) formal structuring; 4) careful attention to input-output; and 5) limited graphics.
First, let's consider the question of vocabulary – what features of BASIC we should use. Apparently, whenever a company produces a new computer or a new version of BASIC, it feels compelled to add features not found in anyone else's BASIC. Often these features are convenient and may make programming for that machine easier. However, they make transporting a program much harder. If at all possible, such features should be avoided when writing with transportability in mind.
If we must use special features, they should be isolated in a subroutine near the end of the program and clearly labelled. The main program should stick to features found in virtually all versions of BASIC. This does not mean that string handling must be restricted to the limitations of Radio Shack Level 1 BASIC, which is an extreme example; nor are there universal rules as to what constructions are allowed. Some textbooks define "minimal BASIC" or restrict themselves in a similar way.
Educational institutions often belong to groups (consortia) which promote standards for exchanging programs; CONDUIT is one such educational group with a nice pamphlet on standard BASIC. If you have worked with several versions of BASIC, sticking to common features is a good guide for what will be transportable between them. For informal use, however, or for the individual who has just worked on one machine, here are the standards I have found useful in working with perhaps a dozen different machines, large and small.
Variables And Commands
Figure 1 suggests some guidelines for variable names, numbers, line numbers, DIM statements. Clearly, the list could be made much longer. For instance, how big can a real number be and not overflow? How small can a positive number be and still be distinguished from zero? Most BASIC programs do not depend critically on these figures, which may differ dramatically from one system to another.
Figure 1: Variables and Numbers
Line numbers: 1 to 9999
Variable names: One letter or one letter and one digit. Strings, one letter and $. Examples:
A B2 C9 F$ Z$
Dimensions: Always declared if needed; execute the DIM statement once, before using the variables. If possible, stick to one subscript for strings, two for numbers. Do not use variable sizes or reuse letters. DIM C$(50),D(20,10) is good; DIM B(N), A(50), A$(20) is bad.
If your program does depend on them, you should probably make this explicit (and include a REMark giving the limits on your system). For instance, if your program has a variable X that gets closer and closer to zero as you go around a loop, and you exit the loop by testing IF X = 0 THEN ... , the program may behave very differently or even fail on another computer. Changing this to
500 IF ABS(X)<1E-50 THEN ...: REM USE A SMALL NON-ZERO NUMBER
will make the program transportable: the person converting it can check to see if the new computer will accept 1E-50. If it will not, he can substitute an acceptable number, e.g., 1E-30.
The most common statements
Figure 2 shows a limited list of BASIC commands — a very limited list. While almost every BASIC accepts more commands than these, they differ on which statements those are. For each command not on this list, there is some computer around that will not accept it. To make matters worse, computers differ substantially in how they interpret some of these commands. Some, for instance, do strange things on a STOP but allow END only as the last line of a program. The cure: place 9999 END as the last line of the program, and terminate anywhere else by GOTO 9999.
GOTO and GOSUB should be followed just by a line number. GOTO 500 is fine; avoid GOTO A even if your computer likes it. In the statement FOR X = A TO B STEP C, it is best to restrict A, B, and C to integers (or expressions evaluating to integers) and to avoid changing them inside the loop. NEXT must name just one variable for the corresponding FOR, e.g., NEXT X.
IF...THEN statements require special attention, since so many computers have so many different extensions. A few computers accept only statements such as IF Y> = Z THEN 830, prohibiting calculations, logical operations, and not allowing anything but a line number after THEN. I am not seriously suggesting that you keep things this simple: the extensions are extremely helpful. However, it is a good idea to keep things simple enough so that your statements can be translated into this form. This will be discussed further in the section on structure, next month.
Numeric And String Functions
Figure 3 shows the most commonly implemented numeric functions. Either most BASICs have these functions, or the programmer using the machine will be prepared to fake them somehow. Two deserve special mention: RND and TAB.
RND is implemented differently on almost every computer. Some use X = RND, some use X = RND(0), some use RND(1), some use RANDOMIZE to start (seed) the random number generator and some do not. You should assume that every line containing RND will have to be rewritten. You should make this as easy as possible, by minimizing the number of lines involved and making your intention clear. If you need a random number in 20 different places in your program, do not have RND appear in 20 places; place it in a subroutine. That is, incorporate in your program
Figure 3: The most common numeric functions
ABS COS INT RND SIN TAB ATN EXP LOG SGN SQR TAN
9000 REM *** GET RANDOM NUMBER, CHANGE FOR OTHER COMPUTERS *** 9010 X = RND (1) : REM RANDOM, 0<X<1, NEW SEQUENCE EACH RUN 9020 RETURN
and then place GOSUB 9000 wherever needed in your program. Here is a more typical use, near the start of a game program:
150 N = INT(100*RND) + 1 : REM RANDOM INTEGER 1 TO 100***************
Here the string of asterisks warns you, when transporting the program, that the line is likely to change. The remark tells what is wanted and will save a lot of time if the new computer achieves this by N = RND(100).
Turning briefly to TAB : there are computers that like TAB(N) (go to column N), those that like SPC(N) (print N spaces), those that like both, and those that like neither. Most people know how to juggle spacing on their own machine, so making your intention clear (by remarks or a sample printout) is probably more important than the exact way you write your PRINT statements. There will be more on this in the discussion of input-output, next month.
Figure 4: The most common string functions
ASC(X$) CHR$(N) VAL(X$) STR$(X) LEFT$(A$,N) RIGHT$(A$,N) MID$(A$,I,J)
The functions given in Figure 4 are now remarkably widespread in microcomputers. It is probably safe to use all of them freely in that context. That is, if the person rewriting the program does not have LEFT$, he probably has a reasonbly direct substitute. You cannot count on the format produced by STR$ being the same from one machine to another — some pad with blanks on the left, some on the right, some not at all. Functions that match a substring are present on many machines, but absent on many others. Many systems will crash if you call LEFT$(A$,N) and A$ has less than N characters, so you should always test for this before you call LEFT$ even if your system does not insist on it.
Large computers differ substantially in how they handle strings, and are often more restrictive than small computers. ASC and CHR$ are frequently absent; many large computers do not even use the ASCII character set. Avoid extensive string manipulations, or at least place them in a subroutine, if your program may have to run on a large mainframe next year.
Next, if our program is to be readily transportable to another version of BASIC, it must be readable. First, can the reader understand our individual lines, and translate them for the new system? Second, can the reader understand our general strategy or procedure (our algorithm) well enough to debug the program if errors creep in, or if his BASIC interprets some command very differently than expected?
The most important consideration, for the second of these, is to make the program sufficiently modular and to provide appropriate REMarks for each module; this is addressed more in the discussion of structure, later. There are a number of "tricks of the trade" that make individual lines easier to read, however. Here are a few principles:
1. Leave plenty of space between line numbers. Even if you have only one command per line, some one-line commands on your system may become multiple commands on another. If you use several commands per line, the situation gets far worse. This is not to condemn all multiple-command-per-line statements, since they can add to the clarity of the program. Just remember that while your computer may allow:
500 INPUT "WHAT IS YOUR NAME?"; N$
someone else's may require
500 PRINT "WHAT IS YOUR NAME"; 501 INPUT N$
This is an easy change if you left a line number available. It is quite possible for a complex one-line statement on one system to require six or eight lines on another.
2. Leave plenty of blanks in your commands, where appropriate. You may have no trouble understanding 250PRINTT5 or 300FORI5 = PTOM but a reader will find 250 PRINT T5 and 300 FOR 15 = P TO M much easier to copy or edit. Many BASICs do insist on the spaces; the new IBM Personal Computer is one that does. Your computer may allow a larger program or run faster if you delete spaces and remarks, but you make the program much harder to transport when you delete them. It may be worth keeping two programs, a transportable copy and a condensed, quick-run copy.
3. Avoid unprintable characters. Where a few are necessary, find a way to make their presence visible. For instance, a disk read in Applesoft requires that you PRINT a CONTROL-D followed by a string. You can make this readable by
200 D$ = CHR$(4) :REM CONTROL-D ... 540 PRINT D$;"OPEN FILENAME" :REM DOS COMMAND STARTS CTRL-D
It is a good idea to indicate what other CHR$ characters are when they are created, too — for instance when CHR$ is used to put a quote mark into a string, or manipulate carriage returns or line feeds.
4. Identify specific features you depend on. This happens most often in connection with PRINT and INPUT statements. Most of us can guess what someone else's PRINT statements are supposed to do, but the INPUTs are another matter.
Some systems input a sentence like "TODAY IT RAINS" by INPUT A$ and the response ?TODAY IT RAINS; others by INPUT A$ and response ?"TODAY IT RAINS"; others by INPUT LINE A$ or by LINPUT A$ or even by INPUT (FIELD 40) A$. You can make this clear to the reader — so that he can try to do the appropriate thing on his system — by remarks, but clear user instructions within the program are probably even better. For example,
110 PRINT "TYPE IN A SENTENCE SURROUNDED BY QUOTE MARKS" 120 INPUT A$ :REM SAMPLE "HELLO, JOE, WADDAYA KNOW."
5. Make cues to the user extremely clear. Remember that you won't be around to show people how to use it; in fact, no expert on the program will be around. Give sample answers whenever possible, and protect against invalid answers.
130 PRINT"DO YOU WANT TO PLAY AGAIN (Y/N)"; 140 INPUT A$ 150 IF A$ = "N" THEN 9999 160 IF A$<>"Y" THEN 130
Note that invalid answers will cause the question to be asked again.
Next month, examples of portable program structure, input-output, and graphics programming.