Classic Computer Magazine Archive COMPUTE! ISSUE 70 / MARCH 1986 / PAGE 82

Loading And Linking Commodore Programs

Part 1

Jim Butterfield, Associate Editor

This series covers the ins and outs of loading, chaining, and overlaying programs on Commodore computers.

The LOAD command seems easy enough to understand: It brings a program into memory from disk or tape. But it has special features and pitfalls. And when you cause one program to load another program, you enter the special field of chaining, overlaying, and bootstrapping. Let's take a close look at all of these operations, beginning with the LOAD command.

LOAD performs some subtle tasks, including relocating a program if necessary and a delicate job called relinking. There are special rules that apply when you load a program that was saved on a different type of Commodore computer. In fact, it sometimes works better to forget about LOAD and use a different technique. And the LOAD command behaves differently when executed by a program than it does when you enter it directly from the keyboard.

Most of the principles we'll cover in this series apply to programs stored on both disk and tape. It's easier to give examples that apply to disk systems, mostly because of the simplicity of setting up demonstration files. But you can still learn a lot about LOAD even if you don't have a disk drive.

What LOAD Does

When a LOAD command is executed, the following things happen:

After a load is finished, what ends up in the computer's memory isn't quite the same as what you originally typed on the keyboard. For one thing, keywords are tokenized—compressed into single-byte values. When you type in the letters P-R-I-N-T, the BASIC word PRINT is "crunched" together into a single byte which the computer recognizes as PRINT. And the picture is further complicated because different Commodore computers use slightly different tokenizing schemes (we'll return to this point a bit later).

Relinking

When a BASIC program is in memory, each program line is linked to the next line by a chain of pointers (often called line links). At the start of each line there's a two-byte pointer that shows where the next line starts. The end of the program is marked by a pointer that consists of two zeros.

What's the purpose of the chain? When the computer runs a program, there are many times when it needs to find a line number quickly—to execute a GOTO or GOSUB, for example. Rather than wade through every character of every line, it can skip from one link to the next until it finds the line it needs.

Each two-byte pointer shows the actual memory address where the next line begins, and these links are saved with the program. After a relocating load, which may bring the program into a different memory area, the pointers may point to the wrong locations. To fix everything up, the computer automatically relinks every line in the program after loading it into memory.

Relinking is a simple job. The computer scans through each program line, looking for the zero byte that marks the end of that line. As soon as it finds this address, the computer knows where the next line begins—at the next byte past the first line's end. It then rewrites the link for the first line and leaps ahead to repeat the process.

Relinking Problems

Two things can go wrong during the relinking process. If you load a chunk of data that doesn't contain any zero bytes, the computer can't find any end-of-line markers and won't be able to relink at all. The second difficulty is fortunately quite obscure: There's a model of computer called the B system in North America and the 700 series in Europe. The most well-known model in the USA is the Bl28 (not to be confused with the Commodore 128), but the comments here apply to all B and 700 models. When you save a BASIC program from these computers, the program is stored in an unusually low memory address. Because the saved program contains zeros in unexpected places, other Commodore computers can't make any sense of the chain.

Let's create an unlinkable file. If you have a disk drive, enter the following statements in direct mode (without line numbers), pressing RETURN after each line:

OPEN 8, 8, 8, "0 : CRASHER, P, W"
PRINT#8, CHR$(1); CHR$(4);
FOR J = 1 TO
300 : PRINT#8, CHR$(4); : NEXT
CLOSE 8

Don't forget to put a semicolon at the end of each of the two PRINT# statements.

IF you LOAD "CRASHER", 8 the computer prints LOADING and READY, but the cursor doesn't return. It's stuck in an endless loop, looking for the nonexistent zero that marks the end of the first BASIC line. This kind of crash is fairly common on cassette systems, when a bad load fills memory with garbage. It can also occur with disk if you forget to add ,1 to a LOAD command that requires relocation, loading something odd like a screen image into the BASIC program area. You can usually recover control by pressing RUN/STOP-RESTORE and entering NEW.

Before we look at a solution to the second problem, let's talk about another area where incompatibility arises between machines.

LOAD Address Incompatibility

PET/CBM computers can't relocate programs at all. On a PET/CBM, BASIC program space starts at location 1025. Most other Commodore computers use a different address, meaning that the PET/CBM can't load programs saved on those computers. The program following this article is a converter to solve both of these problems. You'll need a disk drive to use it.

The new file created by the program is loadable by any eight-bit Commodore computer. Since it sets the load address to 1025, PET/CBM computers can load it properly (all other models relocate it automatically). To make B128 program files usable by other computers, it puts dummy link bytes (both containing a 1) at the beginning of each line. Remember, all these computers relink programs automatically, so this problem can be solved by putting any nonzero values in the links.

This program works only with BASIC programs, since it stops at the two zero bytes that mark the end of the program. If there's something more "pasted" onto the end of the BASIC text—a machine language routine, for example—it will not be copied.

Alternative Method

If you have access to the type of computer which generated the original program, there's an easier way to do the same thing. You can make a Commodore 64 emulate the memory configuration of another machine so BASIC programs are kept in a more compatible part of memory. If you move the start of BASIC to location 1025, the links will be more conventional and the program will be PET-compatible.

To make the area at 1025 available for BASIC, we'll also need to relocate the screen to a new area. To fit the following commands onto two screen lines, you can abbreviate PRINT as ? and POKE as P SHIFT-O:

POKE56576, 5 : POKE 53272, 4 : POKE648,128
 : POKE1024,0 : POKE44, 4 : POKE56, 128
 : PRINTCHR$(147):NEW

The first three POKEs move the screen. The next three move the start and end of the BASIC area, and the last two commands clear the new screen and set up the new BASIC work area.

Once you've entered the above commands, your Commodore 64 emulates a PET/CBM's BASIC configuration. You may now convert a BASIC program into compatible format by loading it into the reconfigured machine and saving it again. By using this load/save sequence, you're making several things happen. When you load the program, it's relocated into a new area. The chain links are then rebuilt to be compatible with the new memory space. When you save, the newly relocated program—complete with new links—is placed on disk or tape.

The modified program seems the same, but it now has a "universal" style. Whether you created it with the Converter program below or with the POKEs and LOAD/SAVE sequence, it can now be loaded into any eight-bit Commodore machine. Its addresses are directly compatible with the PET/CBM, and other machines will relocate the program as it loads. And we've eliminated the possibility of the peculiar B128 chain that confuses other Commodore computers.

Incompatible Tokens

Some programs won't transfer from one machine to another because of differences in the BASIC tokens. You'll have little trouble with commonly used commands such as PRINT and IF. The difference comes with more advanced commands that aren't part of every version of Commodore BASIC. If you write a program in CBM BASIC 4.0 and use commands like DLOAD and SCRATCH, don't be surprised to find that it doesn't run properly on a Commodore 64. Those commands don't exist in the 64's BASIC.

You might also be surprised to find that a Plus/4 or Commodore 128 (in 128 mode) can't run the PET/CBM program either, even though both of those machines have DLOAD and SCRATCH commands. Why not? The commands are there, but they're represented by different token values.

In such cases, you can't use LOAD and SAVE at all. What you'll have to do is detokenize the program by LISTing it to a disk file, then bring it back into memory with a "merge" method like the one described in "Commodore Dynamic Keyboard, Part 3" (COMPUTE!, December 1985).

We've only scratched the surface of the many uses of the LOAD command. When we start to look into chaining, overlaying, and reloading techniques, we'll discover that the LOAD command has amazing potential.

Commodore Program Converter

For instructions on entering this listing, please refer to "The New Automatic Proofreader for Commodore" published in this Issue of COMPUTE!.

CA 110 OPEN15, 8, 15
QA 120 INPUT "NAME OF PROGRAM" ;N$
JA 130 OPEN 2, 8, 2, "0 : " + N$ + ", P, R"
KH 140 INPUT#15, E, E$, E1, E2:IF I SPACE}E THEN PRINT E$: STOP
JC 150 INPUT"NAME OF CONVERTED PROGRAM"; C$
BG 160 OPEN3, 8, 3, "0:" + C$ + ", P, W"
RF 170 INPUT#15, E, E$, E1, E2 : IF I SPACE}E THEN PRINT E$ : STOP
JQ 180 Z$ = CHR$(0)
FA 190 REM : READ LOAD ADDRESS
FX 200 GET#2, A$, B$
DC 210 PRINT#3, CHR$(1) ;CHR$(4);
SR 220 REM : READ CHAIN
CB 230 GET#2, A$, $
SS 240 IF LEN(A$) + LEN(B$) = 0 GO TO 370
GA 250 PRINT#3, CHR$(1);CHR$(1);
CH 260 REM : READ LINE NUMBER
QG 270 FOR J = 1 TO 2
HK 280 GET#2, A$ :IF A$ = "" THEN {SPACEjA$ = Z$
HQ 290 PRINT#3, A$;
HJ 300 NEXT J
FE 310 REM : READ LINE
JP 320 GET#2, A$ : IF A$ = "" THEN {SPACE}A$ = Z$
ES 330 PRINT#3, A$;
SD 340 IF A = Z$ GOTO 230
DJ 350 GOTO 320
QB 360 REM : WIND UP FILES
EJ 370 INPUT#15, E, E$, E1, E2: IF E THEN PRINT E$ : STOP
AX 380 PRINT#3, Z$;Z$;
JF 390 CLOSE 3
JE 400 CLOSE 2
FF 410 CLOSE 15