Classic Computer Magazine Archive COMPUTE! ISSUE 17 / OCTOBER 1981 / PAGE 76

A Tape "EXEC" For Applesoft:

Loading Machine Language Programs

Part II

Sherm Ostrowsky
Goleta, CA

Loading ML With BASIC

This has been an example of the simplest kind of EXEC file; it merely loads and runs a single ML program. Let's extend the procedure a bit more, now, and load both a ML program and an Applesoft program together. One of the most effective advanced programming tools has always been to combine higher level language (e.g., Applesoft) and lower level language (i.e., machine language) routines so that each does the jobs for which it is best suited. But it has long been a problem, much debated and written about in computing magazines, to get them both into the computer from external storage (i.e., cassette tape in this case).

Short ML routines can be included in DATA statements in your Applesoft program and POKEd into memory by a READ - POKE loop. The Lam technique, using strings rather than DATA statements, has also been used to accomplish this. But for ML subroutines of any significant length, this process takes an excruciatingly long time. Some articles have suggested "hiding" long ML routines in front of or behind Applesoft programs by changing certain "pointers" in memory before saving and after loading, to fool Applesoft into believing that they are all parts of one long BASIC program. For one reason or another, none of these techniques has proven very satisfactory.

With the advent of disk systems, this problem was at least solved for disk owners. They just write an EXEC program that loads an Applesoft program and ML subroutines individually stored on the disk. The EXEC program and DOS know where to find them (on the disk) and where to put them (in memory). It is all done automatically. Very simple — if you have a disk system. If not... well, until now you were just sort of out of luck.

Now we cassette users can do the same kind of thing. It takes longer to load, named files are not available, and a little more work is required to arrange everything on the tape and set up the EXEC file (i.e., the loader) to read them and put them where they need to go, but this only has to be done once for any given set of cooperating programs. Thereafter, loading the whole group is just as simple as typing LOAD.

Let me give an example based on a previous article of mine "Clearing the Apple II Low-Resolution Graphics Screen," COMPUTE! #10. For those who may not have access to this issue, I'll give all the essential details as they are needed.

Consider the following short Applesoft program that simply calls a subroutine to fill the Low-Resolution screen with a random pattern of colors, asks you to select one of the sixteen low-res color numbers (0–15), then calls a ML subroutine which clears the screen to the selected color in an instantaneous flash. Here is the program:

10 REM FLASH-CLEAR DEMO
20 GOSUB 1000
30 VTAB23
40 INPUT "SELECT COLOR (0-15):" ;C
50 COLOR = C
60 CALL 800
70 FOR PAUSE = 0 TO 2000: NEXT: GOTO 20
80 END
1000 GR
1010 FOR I = 0 TO 39
1020 FOR J = 0 TO 39
1030 COLOR = 1 + INT (15 * RND (1))
1040 PLOT J, I
1050 NEXT J, I
1060 RETURN

Briefly, line 20 calls subroutine 1000 which fills the screen with random color squares. Line 30 positions the cursor in the text area below the graphics screen. Line 40 asks for your selection and line 50 sets it up to be used in the clearing operation which is performed when line 60 calls the ML subroutine which begins at location (decimal) 800. Line 80 pauses briefly and then loops around to try it all again.

In the original version, the flash-clear subroutine at 800 was stored as DATA within the main program and POKEd into memory using a READ - POKE loop; it's short enough so that this procedure is really quite adequate. But, for the purpose of this demonstration, we will have the Exec-Loader read the ML program in from tape and put it into memory where it is supposed to be. To see how to create the total tape package, you'll have to get the ML program into memory first somehow. The easiest way is to use the Monitor. Do this first, before you enter either the Applesoft loader or the Applesoft demo program. Type CALL - 151, and when you see the asterisk prompt ("*") type:

320:A5 30 A0 78 20 2D 03 A0 50 20 3D 03
60 88 99 00 4 99 80 4 99 00 5 99 80 5 D0
F1 60 88 99 00 6 99 80 6 99 00 7 99 80
7 DO F1 60 (return)

Do it exactly as written, with a space between each pair of digits. Just keep right on typing, past the ends of lines, without stopping until you reach the end. The ML program is now in its place in memory. But where we want it to be is on tape.

The order in which things have to go on the tape is this: first the Applesoft loader, then the ML subroutine, and finally the Applesoft demo program. The demo program has to come last because, after the Exec-Loader program (which I will present below) has executed a LOAD for an Applesoft program (here, the demo), it will automatically be deleted out of memory. It is a standard part of an Applesoft LOAD to clear out any pre-existing Applesoft programs first and, although this can be circumvented, there is no reason to go to the trouble of doing so in this case. So the demo must be loaded last because the loader will cease to exist in memory at that time.

This loader is an extension of the one shown before, but with most of the "bells-and-whistles" left off to make it shorter. Aside from changing the memory locations in string Y$ to correspond to the present ML subroutine, the only other change made from the previous loader is the addition of a LOAD command for the demo program.

10 REM ML + AS LOADER
20 Y$ = "320.34CR D823G"
30 FOR I = 1 TO LEN (Y$): POKE 511 + I, ASC (MID$ (Y$,I,1)) + 128: NEXT
40 POKE 72,0: CALL - 144
50 POKE 214,85
60 LOAD'
70 END

This version has no unnecessary features. If there's a loading error, you'll find out about it when the system prints "ERR" on the screen. Since it takes only a short time to load these little programs, there is no need to give you a message so you'll know what is going on during the loading process.

Now here's the procedure, written down in the form of an algorithm: a series of listed steps to be followed one by one.

  1. Make sure that the ML subroutine is in memory locations 320 through 34C, as indeed it will be if you typed it in using the Monitor as described above.
  2. Go into Applesoft (from the Monitor, type CONTROL-C (return)), and type in the Loader program, as given above.
  3. Type: POKE 82,128
  4. Start your cassette recorder in RECORD mode and type SAVE (ret). When the loader has been saved, stop the tape, but do not rewind.
  5. Back to the Monitor (CALL-151). Type: 320.34CW restart recorder in RECORD mode, then press (return). Stop recorder when finished, but again do not rewind.
  6. 6. Back to Applesoft. Type NEW. Type in the demo program. SAVE it in the regular way.

That's it. To check it out, rewind the tape, turn your Apple off and back on (to make sure memory is cleared out), and LOAD the tape in the usual Applesoft manner. You'll hear five beeps before it finishes loading, but, in the end, it stops just the same as any regular Applesoft tape. You run the program by typing RUN.

Does this seem like a lot of trouble to go through just to load a short machine language subroutine along with an Applesoft program? Of course it is. But suppose the ML program were a lot longer. I have a "Print Using" subroutine that is over 1000 bytes long. Have you any idea how many DATA statements it would need to POKE all that into memory from within my calling program, and how long it would take to do it? The fact is that, before the method I have been demonstrating came along, there was no good way at all for a tape user to put long ML subroutines into his Applesoft programs. Only disk users could have that convenience, with their "BLOAD" and "EXEC" commands. Well, we have just written the tape equivalent of an EXEC file which performs the equivalent of a BLOAD. And that's far from all. The possibilities seem endless. I don't want this article to seem the same, so I'll mention just one more idea and then leave you to enjoy cooking up others on your own.

Protecting Programs In Memory

COMPUTE! #11 had an article (page 76) on resolving the memory conflicts between Applesoft programs and the locations of the two hi-res graphics screen memories. The problem is that a really long Applesoft program, starting at its usual location of $0801 (which is decimal 2049) can easily grow to overflow into the first hi-res screen starting at $2000 (decimal 8192), and some can even intrude into the second hi-res screen starting at $4000. An equivalent problem can occur if you want to make use of the second page of lo-res graphics (did you know that there is a second page?) which unfortunately starts at $0800 and thus always conflicts with an Applesoft program.

Among the solutions mentioned in the article, the one which seems to me to be the most flexible is to move the whole Applesoft program, variables and all, to a starting location above the memory region you want to protect from interference. For example, if you just want to use page two of lo-res graphics, it would suffice to move the program up so that it begins at $0C01 (decimal 3073). If you want to use both hi-res graphics pages, on the other hand, you'd have to move the program all the way up to $6001 (decimal 24566), assuming you even have that much memory to play around with.

As a matter of fact, you don't actually "move" an Applesoft program around in memory. Although this can be done, it would require doing some repair work on pointers and relinking the program lines which is too technical for most casual users to bother with. It isn't necessary. All you have to do is arrange to have the program go directly into the desired memory locations at the time it is being read in from tape. That is a much simpler procedure, requiring only a couple of POKEs from the keyboard before typing LOAD. Even so, that puts us right back into the situation we were in with ML programs: you can't just type LOAD — you have to refer to a set of written directions in order to get the program to load right. At least you do if your memory is as poor as mine is at recalling such details without notes. And there's another point: if the program is to be usable by a non-computerist, how would you instruct such a person in the loading procedure?

So, here's another job for the Tape-EXEC, alias a loader program. Let the loader do the POKEs for you. That way, once you have gone to a little extra trouble to set the tape up right to begin with, you (or anybody) can forever after just type LOAD and let the computer handle the details. If you think it likely that you might want to use the program more than once or twice, then the extra preparations are worth the trouble in the long run.

A loader for a single Applesoft program, to be entered into memory starting at (say) $0C01, would look something like this:

10 REM HIGH MEMORY LOADER
20 POKE 104,12: POKE 3072,0
30 POKE 214,85
40 LOAD
50 END

The algorithm for preparing the loader-leader and tape is similar to the one given for the Applesoft/ML conjunction; just omit steps two and five.

If you want the program to load above hi-res page 1, just change line 20 to:

20 POKE 104,64: POKE 16384,0

If you want it to load above hi-res page 2, change line 20 to:

20 POKE 104,96: POKE 24576,0