27

I am trying to write an Applesoft / floating point BASIC program using high resolution graphics and some assembly language code.

My BASIC program is quite large, and after running the program for a while it crashes for no apparent reason, or my machine code stops working.

If I'm using hi-res page 2, I see random garbage appearing on the screen, which disappears when the screen is cleared, but then the program crashes.

While I was working on a very long BASIC program, all was fine until I ran it, it cleared graphics page 1... and it crashed and the end of my program listing disappeared.

What's going on?

2 Answers 2

34

Managing memory on an Apple II using Applesoft BASIC can be quite complicated, especially for large programs.

A general map of Apple II memory at power-up looks like this:

$E000-FFFF - Monitor ROM / Extended RAM     8 / 8 kB
$D000-DFFF - BASIC ROM / RAM bank 1 & 2     4 / 4 / 4 kB
$C800-CFFF - Shared slot ROM memory         2 kB
$C100-C7FF - Permanent Slot ROM memory      256 per slot
$C000-C0FF - Softswitches                   256 bytes
$6000-BFFF - Free memory                    24 kB 
$4000-5FFF - Free memory / Hi-Res page 2    8 kB
$2000-3FFF - Free memory / Hi-Res page 1    8 kB
$0C00-1FFF - Free memory                    5 kB
$0800-0BFF - Free memory / Text page 2      1 kB
$0400-07FF - Text page 1                    1 kB
$03D0-03FF - Interrupt vectors              48 bytes
$0300-03CF - Free memory                    208 bytes
$0200-02FF - Input prompt character buffer  256 bytes
$0100-01FF - CPU stack                      256 bytes
$0000-00FF - Zero page                      256 bytes

When DOS 3.3 boots, it loads itself as high as possible in memory, just below $C000, and normally occupies $9600 to $BFFF, with 3 file buffers each 595 bytes long.

$9600-BFFF - DOS 3.3 and 3 file buffers     10.5 kB
$6000-95FF - Free memory                    13.5 kB

If the number of buffers is reduced to 1, using the DOS command MAXFILES 1 then DOS occupies $9AA6 to $BFFF.

Applesoft BASIC programs normally load starting at $801, overlapping text page 2. BASIC programs load linearly into memory, and they cannot "jump over" memory regions.

As a BASIC program grows past 6 kilobytes in length ($0800 to $1FFF), it starts to cross into hi-res graphics page 1 followed by hi-res page 2. If you do anything with hi-res page 1 in this situation, you will damage or delete the end of your program.

 

When a BASIC program runs, it normally uses variables to store numbers and strings. These are stored in two separate groups, with the number values being placed immediately after the end of the program and growing upward, and the strings placed high in memory below DOS and growing downward.

So, over time as a BASIC program runs, both graphics pages are at risk of being overwritten with variable data, from above and below.

Also, if you were to write an assembly language program and store it at $6000, it is also at risk of eventually being overwritten by variables as a BASIC program continues to run.

 

Normally the variable and string use will grow indefinitely until the two come together. Then BASIC will perform what is called garbage collection, scanning through variable memory and removing unused old values and recovering free space. Garbage collection can be quite slow, if left to happen on its own in one huge process.

You can force BASIC to do variable garbage collection immediately by using the command PRINT FRE(0), which both returns a value reporting free memory for variables, and also runs garbage collection.

Using this command frequently in large programs will keep variable usage from growing too large, as well as preventing garbage collection from becoming too slow when it does happen.

In ProDOS, there is a DOS command FRE which is much faster than the FRE(0) built into Applesoft BASIC. This is used with the command PRINT CHR$(4)"FRE"

 

It is possible to control where BASIC stores variables using the commands HIMEM: and LOMEM: ... these are odd commands in that the colon is part of the command name.

LOMEM: is automatically adjusted by BASIC to point to the memory just after the end of a BASIC program. To obtain the current value, use: PRINT PEEK(74) + 256 * PEEK(75)

HIMEM: is automatically adjusted by DOS to point to just before the beginning of DOS. To obtain the current value, use: PRINT PEEK(76) + 256 * PEEK(77)

To change where variables are stored, these two commands are used as soon as possible in a program before any variables are created.

To push all variables above $6000 so that the graphics pages are never overwritten accidentally, use LOMEM: 24576

To confine all variables to the graphics page 2 memory region, and protect $0800-$3FFF and $6000-$95FF, use LOMEM:16384 : HIMEM:24575

 

An advanced memory management trick is to change where BASIC stores programs. This location is configured when BASIC first loads and can be changed when running programs, and is stored at $67-$68 or 103,104 decimal.

For example if you have a very large BASIC program that overlaps hi-res page 1, but you want to use text page 2, hi-res page 1, and hi-res page 2, you can tell DOS and BASIC to load and run programs starting at $6001.

The memory value directly before the start of a BASIC program must be zero, so $6000 is set to 0 before reloading.

10 REM If BASIC program load address is $0801,
20 REM   then change to $6001 and reload this program.
30 IF PEEK(104) = 8 THEN POKE 104,96 : POKE 24576,0 :
      PRINT "RELOADING" : PRINT CHR$(4)"RUN THIS-PROGRAM"

SAVE THIS-PROGRAM

This also automatically confines variables to above your program between $6000 and the start of DOS.

 

If you need even more free memory than what DOS 3.3 provides, and you have a 64k or better Apple II, you can use the customized DOS 3.3 called DAVID-DOS, which includes a command called HIDOS.

http://mirrors.apple2.org.za/ftp.apple.asimov.net/images/masters/3rd_party_dos/

This command relocates most of DAVID-DOS into the bank-switched high memory area above $CFFF. Only a small 512 byte stub remains in the 48k memory space, at $BE00-$BFFF, so that it can access the rest of its program code that is sharing memory addresses with the BASIC and Monitor ROM.

$BE00-BFFF - DAVID-DOS bank-switcher        0.5 kB
$6000-BDFF - Free memory                    23.5 kB

 

Finally, if you have a very short assembly language program and you want to avoid all this complexity, it is very common to put short assembly programs in the area of $0300-$03CF as this area is normally not used for anything else and will not be overwritten by BASIC.

 

EDIT June 1, 2020:

Part of the reasons for these complexities appears to be the high cost of memory when the Apple II was still very new.

According to Wikipedia, the Apple II was first sold in 1977 with only 4 kB of memory installed for US$1298 or with 48 kB of memory for US$2638. This works out to $30.45 per kilobyte in 1977, or $128.83 per kilobyte in 2020, and $11,160 for the 48 kB Apple II in 2020 dollars.

Reference: https://en.wikipedia.org/wiki/Apple_II_series#Apple_II

The first 2 kilobytes enable pages $00 to $07, for the zero page, stack, input buffer, vectors, and text / lo-res page 1.

The second 2 kilobytes enable pages $08 to $0F, providing free memory for BASIC or machine language programs, and loading and saving either of these using cassette tape.

$E000-FFFF - Monitor ROM                    8 kB
$D000-DFFF - BASIC ROM                      4 kB
$C800-CFFF - Shared slot ROM                2 kB
$C100-C7FF - Permanent Slot ROM             256 per slot
$C000-C0FF - Softswitches                   256 bytes
$1000-BFFF - Not usable                     44 kB
$0C00-0FFF - Free memory                    1 kB
$0800-0BFF - Free memory / Text page 2      1 kB
$0400-07FF - Text page 1                    1 kB
$03D0-03FF - Interrupt vectors              48 bytes
$0300-03CF - Free memory                    208 bytes
$0200-02FF - Input prompt character buffer  256 bytes
$0100-01FF - CPU stack                      256 bytes
$0000-00FF - Zero page                      256 bytes

However memory was not required to be installed in contiguous regions across the entire memory address space. Unlike modern computers, there was no memory management circuitry, and the memory socket pins on the motherboard directly correspond to the address map.

It was possible to purchase 8 kB of memory (for about US$1000 in 2020 dollars) and socket it into only memory pages $2000-3FFF to enable high resolution graphics page 1, while leaving the 4 kB at pages $1000-1FFF unusable.

$E000-FFFF - Monitor ROM                    8 kB
$D000-DFFF - BASIC ROM                      4 kB
$C800-CFFF - Shared slot ROM                2 kB
$C100-C7FF - Permanent Slot ROM             256 per slot
$C000-C0FF - Softswitches                   256 bytes
$4000-BFFF - Not usable                     32 kB
$2000-3FFF - Hi-res page 1                  8 kB
$1000-1FFF - Not usable                     4 kB
$0C00-0FFF - Free memory                    1 kB
$0800-0BFF - Free memory / Text page 2      1 kB
$0400-07FF - Text page 1                    1 kB
$03D0-03FF - Interrupt vectors              48 bytes
$0300-03CF - Free memory                    208 bytes
$0200-02FF - Input prompt character buffer  256 bytes
$0100-01FF - CPU stack                      256 bytes
$0000-00FF - Zero page                      256 bytes
3
  • 3
    Another trick, especially if programs can put useful binary data or machine code in the area before $2000, is to have code at $0801 start with 10 POKE 104,64 : RUN [use a value of 96 if hires page 2 is needed] and then have all the other code start at $4001 [or $6001]. Poke 104 back to 8 before saving the code, and the saved file will include everything before the start of the main code, including the contents of hires screens (which could be pre-loaded with title-screen graphics).
    – supercat
    Dec 14, 2016 at 17:17
  • PS--Another advantage of putting BASIC programs somewhere other than $0801 is that the floppy controller's boot ROM will trash $800-$8FF. I'm not sure exactly what areas of memory are and are not clobbered by various versions of DOS, but at least in 3.3 there are large areas of memory that will survive a reboot. Recovering a program whose first 256 bytes are trashed can be tricky, but recovery if it starts later may be easier.
    – supercat
    Dec 17, 2016 at 17:30
  • 1
    PPS--I wonder why DOS is loaded at the top of memory rather than putting 6K at the bottom? Doing the latter would have reduced the number of relocation patches required to accommodate different memory sizes, made it so that rebooting need not trash a BASIC program, and made it easy for the BASIC interpreter to avoid conflicts with hi-res screens by making hgr or hgr2 relocate the start-of-program address past the appropriate high-res screen if it wasn't already there without wasting the storage $0800-$1FFF.
    – supercat
    May 16, 2018 at 19:15
1

Summary of Dale's excellent response: Your Applesoft program has grown beyond memory location $4000, which puts it into the Hi-res page-2 memory area. Using the 'hgr2' command writes zeroes in that portion of memory, effectively stomping over part of your program.

On a wider note, old 8-bit machines (and beyond) tended to have rather complicated memory maps, especially those with RAM/ROM beyond the 64k address space, and the Apple2 series was notorious by even those standards. Eg, the 128k RAM of the //c was chopped into something like 10 separate memory "banks" mapped to its 64k address space (IIRC).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .