Wednesday, March 7, 2012

LOMEM, HIMEM, and saving BASIC Programs


I spent some time today making sense of how to save BASIC programs as memory dumps, and the use of the LOMEM and HIMEM commands. Much of this has been covered in forums, web sites, and in documentation but I wanted to combine all the information here in one place, hopefully in a form that makes sense.

Apple 1 BASIC, as on the Replica 1, doesn't use all available memory by default. Most later BASICs did this, possibly after prompting the user for a manual entry of memory size on startup if they desired. The lower and upper range of memory addresses can be set by using the commands LOMEM and HIMEM as documented in the Apple BASIC User's Manual. Here is an example:

LOMEM=2048
HIMEM=4096

This must be typed as a immediate mode command, not from within a BASIC program. It also destroys any program that might currently be loaded (It doesn't seem to actually clear any program from memory but it is likely to crash BASIC if you don't clear it). It doesn't do any checking that the addresses are valid.

The defaults are the values listed in the previous example, which make sense for the original Apple 1 with 4K of memory.

If you have more memory and you want to make use of it, you can use the commands to extend the range of memory used. Typically you would only set HIMEM. On a Replica 1 with 32K of RAM the highest address available is hex $7FFF or decimal 32767, so to make use of all the memory you could do:

HIMEM=32767

Before loading or entering a program.  How do you know if you need to do this? The symptom of running out of memory is getting the "*** MEM FULL ERR" error message.

Incidentally, you can't directly read the values of LOMEM and HIMEM from BASIC, although it may appear that you can. Try doing:

PRINT HIMEM
PRINT LOMEM

and you'll probably find what I did. HIMEM seems to give a random value and LOMEM crashes BASIC. But the information is stored in known memory locations. LOMEM is stored in zero page addresses $4A and $4B and HIMEM in $4C and $4D in the usual 6502 low byte/high byte format.

The following two lines of BASIC code will print the current values (note that hex address $4A is 74 in decimal):

PRINT "LOMEM=";PEEK(74)+256*PEEK(75)
PRINT "HIMEM=";PEEK(76)+256*PEEK(77)

By default we get when we run the above:

LOMEM=2048
HIMEM=4096

After setting HIMEM=32767 we see:

LOMEM=2048
HIMEM=32767

Why would you want to set LOMEM? It's dangerous to set it lower than the default as lower addresses contain page zero, the stack, and other low addresses used by BASIC or the Woz monitor. But you might want to move it higher to leave room for some machine language routines that you want to store there and call from BASIC using CALL.

Where this all becomes relevant is in saving a BASIC program as a memory dump that can be loaded back using the serial port on the Replica 1.

While you can save a program as the source code using LIST, a dump of memory is typically smaller and loads faster because BASIC does not have to parse every line as it comes in (f you have done the high speed serial mod, you can load files as fast as the computer can accept them and the difference between the speed of BASIC and the Woz monitor is significant).

You can save a program using the Woz monitor and later load it back the same way. The question is, what range of memory needs to be saved?

The answer is we need to save two regions of memory. The BASIC program itself will be stored in the range of memory from LOMEM to HIMEM so we need to save that. We also need to save some low memory that stores additional information, such as the values of LOMEM and HIMEM. It turns out saving zero pages addresses $4A through $FF will do the trick. There may be some addressess in there that don't need to be saved but it is less than 200 bytes so it's not worth trying to avoid a few bytes within that range by using multiple memory dump commands.

As an example, to save with the default values of LOMEM ($0800) and HIMEM ($1000) we could dump memory from the Woz monitor with these commands:

4A.FF
800.1000

For the case where HIMEM is $7FFF, we could use

4A.FF
800.7FFF

That's quite a bit of memory. Our BASIC program may not be using all that memory, but I don't know of any way to know what the highest and lowest addresses are that it actually uses, so we need to save the entire range to be sure. If you are working on a small program you should stick to the default value of HIMEM so you don't need to save as much memory.

To actually save to a file you'll want to use your terminal program to capture the output and save it to a file.  It's also good practice to start up BASIC from the file by adding a command to run from the BASIC warm start location, $E2B3 and then run the program, so the last two lines of the file would typically be:

E2B3R
RUN

If you want to save your program as a listing that can be loaded into BASIC rather than a Woz monitor memory dump, you should add any LOMEM or HIMEM commands to the top of the file so that they will be set automatically when loading the file. Do a SCR first to clear any existing program and add a RUN at the end to make it start. In case the user is in the Woz monitor, calling BASIC with an E000R command at the start of the file is good practice. Here is an example:

E000R
SCR
HIMEM=32768
100 REM MY BASIC PROGRAM
...
900 END
RUN

That's all I have to say about HIMEM and LOMEM and it's probably more than enough.

No comments: