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:

  • A PRG (PRoGram-format) file is brought into memory. Program is just the name for a certain type of file. The material contained in the file doesn't need to be a program. It could be a block of data, a screen, a character set, or anything.
  • If the LOAD command doesn't specify nonrelocation, the information is normally relocated. That is, no matter what memory location it was saved from, it's loaded into memory beginning at the start of BASIC program space. PET/CBM computers are an exception to this rule: They never relocate programs. There's also a special cassette format available on VIC-20 and Commodore 64 computers which forces nonrelocation. (Unfortunately, this format is not easy for the beginner to create.)
  • If the LOAD command specifies nonrelocation, the information is placed in memory at the same addresses from which it was saved. This is generally done by adding ,1 at the end of the LOAD command. For example, LOAD "PROG",8 loads the file PROG from disk and relocates it in memory, but LOAD-"PROG",8,1 loads without relocating.
  • If there were no errors during the load, the reserved variable ST is set to 0 (for tape) or 64 (for disk). It's often a good idea to check ST after loading. You can't rely on BASIC to catch every conceivable load error, particularly with tape. If you're using a disk drive, it's a good idea to check the drive status or at least see if the red error light is flashing.
  • After the load is complete, the program is relinked (more about relinking in a moment).
  • If the LOAD command was issued in direct mode (from the keyboard), all variables are cleared and the BASIC start-of-variables pointer is set to the first byte past the last byte that was loaded.
  • If the LOAD command was issued by a program, variables are not cleared and the program automatically reruns from the beginning. This creates powerful opportunities, but requires some special handling to work correctly.

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