Classic Computer Magazine Archive COMPUTE! ISSUE 73 / JUNE 1986 / PAGE 74

Loading And Linking
Commodore Programs

Part 4: Overlaying

JIM Butterfield, Associate Editor

This installment of Jim Butterfield's series on loading and linking Commodore programs talks about overlays - a technique that allows a program to call in additional subroutines and other data. The principles apply to most Commodore computers, including the 64,128, VIC-20, PET, Plus/4, and 16.

There are three major ways of connecting Commodore programs together. Chaining allows several programs to perform a job, each program continuing the work that a previous program began. Linking enables one program to call up another, with the new program starting fresh on a new task. Overlaying allows a main program to call in supplementary material such as machine language subroutines, data tables, or display information. This article discusses overlay programming techniques. (Though the example programs are designed for a disk drive, you should be able to change most of them to work with tape by replacing ,8,1 with ,1,1.)
    In some situations a computer program may need extra pieces of information to perform its task. The extra material may be one or more programs (often machine language), or it could be pure data. Data can be of several types: information, display screens, character sets, sprite shapes, or whatever. The difference between overlaying and chaining or load linking is that the main program stays in memory at all times, calling up the modules it needs.

Why Overlay?
The classic reason to overlay programs is so that a main program can call up a machine language module to do a specific job. This permits you to keep a library of special programs on disk and call in each program as it is needed. For example, you might bring in one machine language program to scan through a file, searching for information; another might be used to display data neatly on the screen; yet another module could be called to handle printer output, and so on. In the simplest case, only one program module is brought in at a time, and a certain section of the computer's memory is set aside to hold the current program. This lets you run programs which are, in effect, much larger than the amount of memory in your computer.
    One obvious use for this technique is to bring in a series of attractive high-resolution graphics screens. Since each hi-res screen requires 8,000 bytes of memory (with more needed for color information), it's not practical to keep more than one or two in memory at a time. But a disk can hold the data for many hi-res screens. By calling in each screen only when it's needed, you can display dozens of hi-res. pictures in the course of a program run.
    The same factors apply to other sorts of data, too. For instance, a program could use many different sprite shapes as it runs. Sprite-animated figures could change from bicycles to cars, and later to horses, elephants, or boats as a schedule of race events progresses. All that's required is to replace one set of sprite shapes with a new set by means of overlaying.
    Alternate character sets also require extensive amounts of data, usually thousands of bytes for each different set. If you want to switch from Roman (the characters you're reading right now) to Greek, Arabic, Hebrew, Russian, or whatever, simply haul in each new character set as you need it.

Breaking The Chain
Before you overlay information, you must set aside space to hold it. This isn't a new requirement: Regardless of where the data comes from, it's always necessary to allocate room for sprite shapes, hi-res screens, machine language programs, and so on. So we won't repeat the familiar methods of setting aside memory for such purposes.
    Let's work through the sequence of events that occur when you bring in an overlay module. Keep in mind that the BASIC program itself is not replaced-the program is still present and running.
    The first step is for the BASIC program to load the desired module with a command like LOAD "MOD ULE",8,1. (The ,1 at the end of the LOAD command is needed on most Commodore computers to specify a nonrelocating load-one that loads the file back into the exact part of memory from which it was saved.)
    Here comes the tricky part. When the load is complete, the computer thinks that it has performed a chain. It concludes (wrongly in this case) that the old BASIC program has been replaced by a new one. None of the program's existing variables are erased or changed, but the computer reruns the BASIC program from its first line.
    This phenomenon isn't a bug; it's simply what the designers intended to happen whenever you LOAD from within a BASIC program. However, it raises a puzzling problem for beginners. If you write a program that begins with the line 10 LOAD "MODULE",8,1 and run it, here's what happens. The MODULE file is loaded. Then the program reruns, beginning at line 10. So MODULE is loaded again. Then the program reruns again, loading MODULE again, which causes another restart, and so on. Until you press RUN/STOP, the program continues forever.
    Fortunately, there's an easy solution. Because LOAD from within a program doesn't destroy existing variables, we can change a variable when the load occurs and use it to branch around the LOAD command when the program restarts. It's like building a bypass around the LOAD after the overlay is complete. Take a look at this program fragment:

10 IF A=1 GOTO 40
20 A=1
30 LOAD "MODULE",8,1

    Let's trace what happens when this program runs. The first time it's run, the variable A is equal to 0 (it hasn't been defined yet). So the IF test in line 10 (which tests for the condition A=1) fails, and we don't branch to line 40. Instead, the program proceeds to the next line. Line 20 then makes A equal to 1. Line 30 loads the MODULE file to wherever it's going in memory. At this point (the end of line 30), the program goes back to the first statement. This time the IF test is true (A is equal to 1), so we branch to line 40. The program continues without getting caught in an endless series of loads. You could also condense the whole operation into one program line:

10 IF A=0 THEN A=1: LOAD "MODULE",8,1

    This example combines the IF test, the setting of A to 1, and the LOAD command all in one line. Another option is to replace line 10 of the original example with 10 ON A GOTO 40. In a moment, we'll use a variation of this technique to allow for several overlays.

Setting Up Files
Let's write an example geared to the Commodore 64. We'll overlay three items: a graphics screen and two small machine language programs. The screen will load into the usual screen memory area, locations 1024-2023. The machine language programs will come into the cassette buffer, which starts at location 828 on the 64. (Because this example uses the cassette buffer, it works only with disk.) Only one machine language module will be in memory at a time.
    Enter NEW, then type in this program. It creates a screen that will be loaded later.

100 DATA 8,1,16,16,25,32,2,
110 OPEN 1,8,2,"0:SCREEN,P,
120 PRINT#1,CHR$(0);CHR$(4)
130 FOR J=1 TO 986
140 PRINT#1,CHR$(32);
150 IF J<>494 GOTO 200
160 FOR K=1 TO 14
170 READ X
180 PRINT#1,CHR$(X);
190 NEXT K
200 NEXT J
210 CLOSE 1

    Make sure that lines 120, 140, and 180 each end with a semicolon. When you run this program, it creates a file called SCREEN which is four disk blocks in length. When that's done, enter NEW again and type in the next program. This one creates a machine language program called MLA. When the ML program loads into memory, it will do three small jobs: It will clear the screen, change the screen background color to white, and set the screen's POKE color to red.

100 DATA 60,3
110 DATA 169,147,32,210,255
120 DATA 169,31,32,210,255
130 DATA 169,1,141,33,208
140 DATA 169,0,133,252,169,
150 DATA 162,4,169,2,160,0
160 DATA 145,252,200,208,25
170 DATA 230,253,202,208,24
200 A=42
210 FOR J=1 TO A
220 READ X
230 T=T+X
240 NEXT J
250 IF T<>6238 THEN STOP
270 OPEN 8,8,8,"0:MLA,P,W"
280 FOR J=1 TO A
290 READ X
300 PRINT#8,CHR$(X);
310 NEXT J
320 CLOSE 8

    Be sure that line 300 ends with a semicolon. Run the program; if it stops at line 250, you have an error in one of the DATA statements.
    Once that's done, enter NEW again. The next generator program creates a machine language routine to blink the screen. This ML module, which we'll call MLB, will occupy the same part of memory as MLA. The memory conflict isn't important since we'll load the programs one at a time. Type in and run this program:

100 DATA 60,3
110 DATA 169,0,133,252,173,
120 DATA 133,253,162,4,160,
130 DATA 177,252,201,32,240
140 DATA 73,1?28,145,252,200
150 DATA 230,253,202,208,23
200 A=34
210 FOR J=1 TO A
220 READ X
230 T=T+X
240 NEXT J
250 IF T<>5022 THEN STOP
270 OPEN 8,8,8,"0:MLB,P,W"
280 FOR J=1 TO A
290 READ X
300 PRINT#B,CHR$(X);
310 NEXT J
320 CLOSE 8

    Be sure to put a semicolon at the end of line 300. If you've typed the program correctly, it writes the ML program MLB to disk. At this point, all of the modules are complete. Let's write the main program to tie it all together.

The Main Program
Enter NEW and type in the following program lines. We'll start with a line that dispatches the program to the correct line after each load:

100 ON X GOTO 130,160,180

    The first load brings in the machine language program MLA.

110 X=1
120 LOAD "0:MLA",8,1

    After the first load is complete, line 100 sends us to line 130, where we activate the ML program with SYS:

130 SYS 828

    The next two lines bring in the graphics screen.

140 X=2
150 LOAD "0:SCREEN",8,1

    When the screen has loaded, you'll see the message it contains. After the second load is done, line 100 sends us to line 160, where we bring in the second machine language program:

160 X=3
170 LOAD "0:MLB",8,1

    We resume at line 180 (courtesy of line 100) with a screen in place, the colors set as desired, and
a blink program waiting to be called with another SYS command. Let's finish off with a loop to flash the message.

180 FOR J=1 TO 20
190 SYS 828
200 FOR K=1 TO 100
210 NEXT K
220 NEXT J

    That's all it takes. It's a simple example, but the program shows the potential of the overlay technique.

Earlier in this series, we mentioned self-chaining, a method of restarting a program that has snarled itself inside several levels of subroutines. Again, keep in mind that prevention is the best way to avoid this problem. Good program structure should ensure that you never get tangled up in your own code. But occasionally you may program yourself into a corner and need a simple way to get out.
    Assuming that you've gotten into this deplorable situation somehow, you can escape by making the dubious program chain to itself. The chaining activity cancels all FOR-NEXT loops and subroutine RETURNS, and also RESTORES the DATA pointer to the very first DATA statement in the program. However, all existing variables are preserved, and all open files (if any) remain open.
    Don't misunderstand what a self-chain does. The program text itself doesn't change-all you've done is reload the same program lines into memory. But the act of doing so untangled the snarled subroutines and FOR-NEXT loops and restarted the program from its first line. Other than that, everything remains as it was before the selfchain.
    Since it's the chaining (not the loading) that does the trick, we can skip loading the program itself. In stead, we can overlay a single byte somewhere in memory to trigger the chaining process. To illustrate, let's write to disk a simple one-byte program file that will load the useless byte to some unimportant memory location. The chaining action that accompanies the load will do the job we want.
    To write this file, type NEW and enter the following program:

100 DATA 255,0,0
270 OPEN 8,8,8,"0:DUMMY,P,W
280 FOR J=1 TO 3
290 READ X
300 PRINT#8,CHR$(X);
310 NEXT J
320 CLOSE 8

    Again, be sure that there is a semicolon at the end of line 300. When you run this program, it creates a tiny file named DUMMY. Now let's repeat the dreadful program that we used before. Again, please don't write programs this way; it's here just to illustrate the point. Type NEW and enter this program:

100 IF N>0 GOTO 130
120 DIM N$(50)
170 PRINT "3. QUIT"
190 ON C GOSUB 210,310,350
200 GOTO 130
240 GOSUB 260
250 GOTO 240
260 INPUT N$
270 IF N$="*" OR N=50 THEN
    LOAD "DUMMY",8
280 N=N+1
290 N$(N)=N$
310 FOR J=1 TO N
320 PRINT N$(J)
330 NEXT J
350 END

    Try to write programs in such a way that you don't get into the problem shown above. By the time the program reaches line 210, it's in a subroutine. At line 260, it's nested within a second subroutine. When line 270 discovers that an exit is wanted, we're almost stuck and don't dare GOTO 130, which would leave unRETURNed subroutine addresses on the computer's internal stack.
    Here's how to escape. At line 270, LOAD the one-byte DUMMY file. The load does nothing, but the act of chaining untangles the rest of the mess. How does this compare to our first solution of the same problem, where the entire program chained to itself? You get the same results, but gain in speed because you're loading a much smaller file.
    Overlaying, like the other methods examined in this series, becomes especially useful in big program situations, and generally eases the burden of bringing large amounts of data into memory when it's needed. The computer still thinks that it's performing a chain, but overlaying uses the same general technique for a different purpose. Once you understand the difference between chaining and overlaying, you can write even more powerful, flexible programs.