Tom R. Halfhill, Editor
Programs Within Programs
Imagine what your life would be like if every time you had to perform a routine task-such as starting your car or switching on a TV-you had to think really hard about it, almost as if you were learning the task for the first time. Starting a car doesn't seem too difficult, but it does require you to execute a number of smaller tasks in exactly the same sequence each time. You have to find the right key, unlock the door, grasp the handle, pull open the door, climb into the seat, stick the key into the ignition, twist the key, and press the gas pedal.
Yet, unless the car is brand-new or belongs to someone else, you can probably do all of this with your eyes closed, like a blindfolded soldier reassembling his rifle. That's because you've performed the actions so many times that they're carved into your unconscious. You just think start the car, and a little "program" takes over.
When you think about it, your brain stores thousands of such tiny programs. They let you perform everyday tasks almost on autopilot. Without them, every routine action would be like a new learning experience. Life might be more interesting, like a young child's, but you'd be a lot less efficient.
Computer programs can benefit from the same sort of efficiency. After all, a program at its most basic level is just a list of instructions telling the computer how to perform some kind of job. That job might be something as simple as adding two numbers or something as complex as modeling the economy of a large nation. Still, even simple jobs can often be broken down into several smaller tasks which are executed repeatedly. So why make the computer do things the hard way? Why not equip your programs with the same kind of subprograms that your brain seems to use to automate routine tasks?
This concept of smaller programs within larger programs is so powerful that virtually every computer language offers some way to do it. By identifying these repetitive tasks and turning them into subprograms or subroutines, you can write programs that run faster, consume less memory, and are easier to understand and modify.
When To Use A Subroutine
Your brain acquires a subroutine by rote-it subconsciously memorizes a task that you perform over and over again. Today's computers aren't quite intelligent enough to learn this way, so you have to spell it out for them more literally with BASIC commands.
First you have to decide when to take a piece of a program and make it into a subroutine. This judgment comes naturally after a while, but as a general rule, any small task which is performed more than once in a program is a candidate for a subroutine.
Once you've identified this task, you write the little routine and make the program detour to those lines whenever you need to perform that task. At the end of each subroutine, you use the command RETURN to automatically go back into the main program and proceed with other things.
Let's try an example. Assume you're writing a program that frequently pauses and asks the user to press a key. With no subroutines, this is how clumsy the program would be:
90
DIM A$(1):REM This line for Atari only
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 PRINT "PRESS C AND RETURN TO
CONTINUE";
150 INPUT A$
160 IF A$<>"C" THEN GOTO 140
170 PRINT "Poor medical care accounted"
180 PRINT "for many casualties,"
190 PRINT "but outmoded military tactics"
200 PRINT "were also to blame."
210 PRINT "PRESS C AND RETURN TO
CONTINUE";
220 INPUT A$
230 IF A$<>"C" THEN GOTO 210
...
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 PRINT "PRESS C AND RETURN TO
CONTINUE";
150 INPUT A$
160 IF A$<>"C" THEN GOTO 140
170 PRINT "Poor medical care accounted"
180 PRINT "for many casualties,"
190 PRINT "but outmoded military tactics"
200 PRINT "were also to blame."
210 PRINT "PRESS C AND RETURN TO
CONTINUE";
220 INPUT A$
230 IF A$<>"C" THEN GOTO 210
...
Notice how the lines which ask the user to press a key (lines 140-160 and 210-230) are simply repetitious; only the line number references are different.
In each case these lines keep printing the prompt PRESS C AND RETURN TO CONTINUE until the user presses the C key. (Make sure to press a capital C if you try running this example. If you have a TI-99/4A, change every occurrence of THEN GOTO to THEN in this and all following examples.) A little three-line routine like this one might not seem like much, but if it's repeated throughout a long program, considerable space and programming time would be wasted. This is an ideal candidate for a subroutine.
Why Not GOTO?
At this point, you might be thinking about building a subroutine with the GOTO command. After all, a subroutine requires a detour from the main program, and GOTO is a programming detour (see last month's column). Why not just jump to the subroutine with GOTO and then exit from it the same way? The program might look like this:
90
DIM A$(1):REM This line for Atari only
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 GOTO 1000
150 PRINT "Poor medical care accounted"
160 PRINT "for many casualties,"
170 PRINT "but outmoded military tactics"
180 PRINT "were also to blame."
190 GOTO 1000
200 PRINT "For instance, many battles"
210 PRINT "were fought with mass charges"
220 PRINT "of infantry and cavalry."
230 GOTO 1000
...
1000 PRINT "PRESS C AND RETURN TO
CONTINUE";
1010 INPUT A$
1020 IF A$<>"C" THEN GOTO 1000
1030 GOTO 150
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 GOTO 1000
150 PRINT "Poor medical care accounted"
160 PRINT "for many casualties,"
170 PRINT "but outmoded military tactics"
180 PRINT "were also to blame."
190 GOTO 1000
200 PRINT "For instance, many battles"
210 PRINT "were fought with mass charges"
220 PRINT "of infantry and cavalry."
230 GOTO 1000
...
1000 PRINT "PRESS C AND RETURN TO
CONTINUE";
1010 INPUT A$
1020 IF A$<>"C" THEN GOTO 1000
1030 GOTO 150
At first this seems to fit the bill. The lines which await the user's keystroke are grouped together in a neat subroutine at the end of the program. All it takes is a simple instruction-GOTO 1000-to activate (or call) the subroutine.
If you try running the program, however, a problem soon becomes apparent. The subroutine works great the first time it's called. The first paragraph of text appears on the screen, followed by the prompt, and the program continues printing when you press C. But after the second time the subroutine is called, the program prints the second paragraph all over again! In fact, it keeps printing the same paragraph no matter how many times you press C-it never reaches the third paragraph at all.
GOTO is the culprit. GOTO 1000 works okay for calling the subroutine, because the routine is always at line 1000. But GOTO doesn't work so well when returning from the subroutine. The line number in the routine's final GOTO statement is fixed (GOTO 150), but the line number where the program should continue after calling the routine keeps changing. What's needed is a substitute for GOTO that always knows how to pick up where the program left off. That substitute is the pair of commands GOSUB and RETURN.
GOSUB: A GOTO With Brains
If you understood how the above programs work, you'll have no trouble at all grasping GOSUB and RETURN. GOSUB (which means GOto SUBroutine) is merely a smarter version of GOTO. The statement GOSUB 1000 does the same thing as GOTO 1000-it detours the program to line 1000. However, it also makes the computer remember where it detoured from. Then, when a RETURN statement is encountered, the program automatically returns from the subroutine and begins executing the statement which immediately follows the original GOSUB.
Here's how the previous example would look after GOSUB and RETURN are substituted for the GOTO statements that caused the problem:
90
DIM A$(1):REM This line for Atari only
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 GOSUB 1000
150 PRINT "Poor medical care accounted"
160 PRINT "for many casualties,"
170 PRINT "but outmoded military tactics"
180 PRINT "were also to blame."
190 GOSUB 1000
200 PRINT "For instance, many battles"
210 PRINT "were fought with mass charges"
220 PRINT "of infantry and cavalry."
230 GOSUB 1000
240 END
...
1000 PRINT "PRESS C AND RETURN TO
CONTINUE";
1010 INPUT A$
1020 IF A$<>"C" THEN GOTO 1000
1030 RETURN
100 PRINT "During the Civil War,"
110 PRINT "more American soldiers died"
120 PRINT "than in all other"
130 PRINT "American wars combined."
140 GOSUB 1000
150 PRINT "Poor medical care accounted"
160 PRINT "for many casualties,"
170 PRINT "but outmoded military tactics"
180 PRINT "were also to blame."
190 GOSUB 1000
200 PRINT "For instance, many battles"
210 PRINT "were fought with mass charges"
220 PRINT "of infantry and cavalry."
230 GOSUB 1000
240 END
...
1000 PRINT "PRESS C AND RETURN TO
CONTINUE";
1010 INPUT A$
1020 IF A$<>"C" THEN GOTO 1000
1030 RETURN
Think how much memory (and programming time) you could save by simply inserting a GOSUB 1000 statement whenever you want the user to press a key to continue, instead of redundantly entering the routine itself each time you need it. The memory savings are even more dramatic with longer subroutines.
For that reason alone, GOSUB and RETURN are worth their weight in RAM chips. Yet memory conservation is only one advantage of using subroutines in your programs. We already mentioned how they can increase execution speed and help make programs easier to understand and modify. But they can also drastically reduce the time you spend writing and debugging a program. Once you get a subroutine up and running without bugs, you can call it with confidence whenever necessary. If an error does result, you can be fairly certain that something outside the subroutine is causing the error. This narrows down your search for the elusive bug.
Subroutines can also make it less intimidating to write large, complex programs. By breaking a big job down into many smaller jobs, and then tackling them one at a time, the program seems to fall together much more easily. In fact, many programmers keep a library of frequently used subroutines and stick them into new programs wherever needed.
Questions Beginners Ask
Q In manuals, books, and articles, I keep seeing the term "default." What does default mean?
A Default means the way something starts out, its normal condition. For example, many computer games default to one-player mode. If there are two players, you have to let the game know by pressing a special key.
In computer terminology, default can refer to the standard setting of a switch, the screen colors when you first turn on the computer, the number stored in a memory location before it's altered by a program, and many other things. For example, the LOAD command on a Commodore 64 or VIC-20 defaults to tape instead of disk. If you type:
LOAD"PROGRAM
NAME"
the computer assumes you are loading from the cassette recorder and responds PRESS PLAY ON TAPE. To load a program from the disk drive, you have to add a device number to the command which overrides the default:
LOAD"PROGRAM
NAME",8
Another example is a dot-matrix printer which defaults to a standard typeface. To print in a special typeface such as bold or italics, you must send the printer a command (usually from within a program) which overrides the default setting.