Beware The RAMTOP Dragon
K. W. Harms
You've just had a brilliant idea for a program which requires some protected memory. Perhaps a special display list or character set is needed, or maybe a direct access memory "file." This article explains how to set aside that memory so that nothing will fiddle with it. Further, we'll reveal the generally unknown habits of the RAMTOP Dragon and show you three ways to make sure he doesn't gobble up your data.
The Atari offers a simple way to control how memory is internally managed by the operating system. The Atari Connection (Premier Issue) discussed "moving" the top memory boundary. Jim Clark in COMPUTE! #14 showed how to move the lower boundary. Both methods protect memory from BASIC programs.
The map gives a very simple picture of Atari's memory management. Fixed memory boundaries are presented in decimal "addresses," but boundaries which vary according to the amount of memory in your machine or the program loaded at a particular time are given names such as "ramtop." The 400 and 800 both use the same system.
When you turn on the Atari with a BASIC cartridge, it takes a few seconds before "READY" to check out the machine and enter values for the boundaries into specific locations. PEEK allows you to look at those values. For instance, the value for RAMTOP is stored in address 106. The instruction "? PEEK(106)" will tell you where the Atari thinks the end of RAM is. Appendix I of the Basic Reference Manual explains that the value in 106 is in "pages" of 256 bytes. Multiplying number of pages times 256 gives the last address BASIC thinks it can use (e.g., a PEEK(106) of "32" equals an address of 8192 or "8K").
The 400/800 always places the display list and display data immediately below RAMTOP. If you alter the value in RAMTOP, the Atari will push the display list and display data "downward" in RAM. This reduces the space available for your program, but leaves free RAM above the new (fake) RAM-TOP. Since the Atari doesn't know about this space, it doesn't use it, usually. This is the space usually considered "reserved" for you.
Program 1 shows how to lower RAMTOP by 4 pages (1024 bytes). Remember to issue a GRAPH-ICS command immediately after moving RAMTOP so that the display list and data are moved below the new RAMTOP. Since line 60 will clear the screen, write down the old amount of free RAM and RAMTOP. Comparing them to the new numbers from lines 80 and 90 will show that RAM-TOP is now lower and that less space is available for programs. That extra memory is now above RAMTOP and "reserved" for your exclusive use. RAMTOP is reset only by the RESET switch (and powering down/up), so that successively RUNning Program 1 will keep lowering RAMTOP until you run out of memory.
10 ? "FREE RAM = ";FRE(0) 20 RAMTOP = PEEK(106):? RAMTOP = ";RAMTOP ;" PAGES":? "LAST ADDRESS = ";RAMTOP*256 30 FOR W = 1 TO 1000 : NEXT W 40 SMALLRAM = RAMTOP-4 50 POKE 106, SMALLRAM 60 GRAPHICS 0 70 RAMTOP = PEEK(106) 80 ? "NEW FREE RAM = ";FRE(0) 90 ? "NEW RAMTOP = ";RAMTOP:" PAGES": ? "LAST ADDRESS = ";RAMTOP*256 100 ? "RESERVED MEMORY BEGINS AT "; RAMTOP*256 + 1
Although others have described ways to use the reserved space, they have not warned you about the RAMTOP Dragon who will periodically visit your reserved RAM and gobble up memory. Extensive field observations have revealed that Dragon visits upper memory on three occasions:
- A GRAPHICS command clears the visible screen and also the first 64 bytes above RAMTOP.
- A CLEAR command (or "PRINT CHR$(125)") clears the first 64 bytes above RAMTOP.
- Scrolling the text window of a graphic mode 3 to 8 screen clears up to 800 bytes above RAMTOP.
Program 2 lets you play with the RAMTOP dragon. Lines 100 to 140 move down RAMTOP and reset the display list and data. Answer "NO" for all except the first pass or the program will lower RAMTOP each time until you are out of memory.
After an initial questioning, the next section (lines 200 to 290 first turns off the "direct memory access" for the ANTIC processor so that the program will operate faster (see COMPUTE! #11). It then fills the 900 bytes after RAMTOP with a sequence of values between 1 and 255. Note that the values will remain there as long as there's power to the CPU (and nothing clears them). Therefore, it's not necessary to repeat this step on subsequent RUNs.
The "Choose Action" section (lines 300 to 340) GOSUBs to the three major program sections.
The "Screen Play" routine (lines 1000 to 1100) exercises all three of the actions which call the Dragon. It clears the screen, changes graphic modes and scrolls text windows. To scroll a window, enter graphic mode 3 to 8 and then enter numerical responses for as long as you wish to scroll (the amount of scrolling appears to affect the amount of memory cleared).
The "Check Memory" routine (lines 2000 to 2100) prints addresses for the first and last positions of reserved memory and requests the starting and ending addresses you wish to check. This section allows you to look at different ranges of locations to see how much memory has been cleared by displaying these memory addresses and their values. Knowing that Dragon always leaves 0's in his path, and remembering that we loaded memory with values between 1 and 255, 0's will appear only in areas he visited. (Actually, I'm not sure whether Dragon is a he or a she.) When you're done checking and want to enter a different set of actions, an "0,0" entry will return you to the "Choose Action" section.
The "Neat Trick of the Week" is found in lines 2055 and 2075. The memory address at 53775 can be used to tell you whether a key on the keyboard is being pressed at the time you PEEK it. If a key (any key) is depressed, 53775 contains the value 251. When the key is released, 53775 will show a 255. Line 2055, then stops the program whenever any key is pressed and restarts it when the key is released. The POKEing 764 with a 255 (line 2075) clears the "halt" character so that future INPUTs, GETs, etc. aren't confused.
How can one avoid the Dragon anyway? There are many ways. You could never change graphic modes or clear or scroll the screen. This is difficult if you have any significant screen output. However, since the screen clear erases only 64 bytes, you could always clear the screen before the text window scrolls and never use that first 64 bytes. Or you could skip the first 800 bytes after RAMTOP and allow both scrolls and clears. Taking the other path, you could move the bottom of memory up and use memory below the new bottom (see Jim Clark's article). However, this requires using a (simple) machine language subroutine.
If you are using the reserved memory in a stable program (one with no further coding), you have another choice. Program 3 shows how to use memory below RAMTOP as your special area instead of reserving memory above RAMTOP. In your program, wait until after all strings and arrays are dimensioned. Then, go to the highest resolution graphic mode and PEEK at the top of your BASIC program (line 10000). Since the Atari saves data on GOSLJB and FOR/NEXT statements as it encounters them in a dynamic "stack" at the top of a program, you must provide some room for this storage. Figure on 4 bytes for each active GOSLJB (one which hasn't been RETURNed) plus 16 bytes for each active FOR/NEXT (while it's FORing and NEXTing). Add this allowance to the previous address (line 10010) and use the total as the bottom of your reserved area.
Next, PEEK at MEMTOP, the top of RAM available for BASIC programs (line 10100), and use that number as the top of your area.
Program 3. 10000 PROTOP = PEEK(14) + 256 * PEEK(15) 10010 MEMSTART = PROTOP + 24 + 1 : REM START OF YOUR MEMORY — ALLOWS FOR 2 GOSUBS PLUS 1 FOR/NEXT 10100 MEMTOP = PEEK(741) + 256 * PEEK(742) 10110 MEMFINISH = MEMTOP : REM END OF YOUR MEMORY AREA
This method gives you the greatest possible amount of RAM without special code, but brings three general risks. If your BASIC program grows (by encountering an unexpected DIM or FOR/ NEXT, for instance) after you have set the lower boundary, it will gnaw into the bottom of "your" memory. If the graphic mode is changed to a higher resolution mode after the upper boundary is set, the display list will push down into the reserved memory. Last, a program loaded after the boundaries are set may be larger and run into the set aside memory.
The next time you see the RAMTOP Dragon, you'll be ready!
Atari Memory Management
|57344-65535||ROM for character set, OS, etc.|
|55296-57343||ROM, Floating Point ROM|
|53248-55295||ROM, Hardware Registers ROM|
|40960-49151||CARTRIDGE ROM for BASIC, etc.|
|RAMTOP||END of RAM PEEK (106)|
|Display List and Display Data (usually 1K in Graphics 0, around 8K in Graphics 8).|
|MEMTOP||Top of RAM usable by BASIC programs.|
|PEEK(741) + 256 * PEEK(742)|
|PROTOP||Program top for the current BASIC program|
|PEEK(14) + 256 * PEEK(15)|
|Free RAM used for programs, data storage, etc.|
|BASIC LOMEM Start of BASIC program|
|Operating System, various buffers, hardware registers, etc.|
|0||Start of RAM addresses|
50 REM SET UP VARIABLES FOR CALLS 60 CHECK = 1000 : SCREEN = 2000 : QUIT = 3000 : DIM AN$(10) 100 REM MOVE RAMTOP DOWN 110 RAMTOP = PEEK(106) 120 ? "MOVE RAMTOP DOWN" ; : INPUT AN$ : IF AN$(1, 1) = "N" THEN 200 130 RAMTOP = RAMTOP - 5 : POKE 106, RAMTOP 140 GRAPHICS 0 200 REM FILL 900 BYTES ABOVE RAMTOP 210 FIRST = RAMTOP * 256 + 1 : LAST = RAMTOP * 256 + 900 220 ? "FILL MEMORY ABOVE RAMTOP" ; : INPUT AN$ : IF AN$(1, 1) = "N" THEN 300 230 POKE 559, 0 : REM TURN SCREEN REFRESHER OFF 240 FOR POSITION = FIRST TO LAST 250 IF VALUE = 255 THEN VALUE = 0 260 VALUE = VALUE + 1 270 POKE POSITION, VALUE 280 NEXT POSITION 290 POKE 559, 34 : REM TURN SCREEN ON 300 REM CHOOSE ACTION 310 ? "WHAT ACTION? 1 TO CHECK RAM, 2 TO PLAY WITH SCREEN, 3 TO QUIT" 320 INPUT ACTION 330 ON ACTION GOSUB SCREEN, CHECK, QUIT 340 GOTO 300 1000 REM SCREEN PLAY 1010 ? "CLEAR SCREEN" : INPUT AN$ 1020 IF AN$(1, 1) = "Y" THEN ? CHR$(125) 1030 ? "CHANGE GRAPHICS MODE" ; :INPUT AN$ 1040 IF AN$(1, 1) = "Y" THEN ? "WHAT MODE" ; : INPUT MODE : GRAPHICS MODE 1050 IF MODE <> THEN ? "ENTER ANSWERS TILL DONE, THEN NO" ; : INPUT AN$ 1060 IF AN$(1, 1) <> "N" THEN GOTO 1050 1070 IF MODE <> 0 THEN GRAPHICS 0 1100 RETURN 2000 REM CHECK MEMORY 2010 ? : ? "FIRST POSITION = ";FIRST : ? "LAST = " ; LAST : ? "ENTER POSITIONS TO CHECK OR 0, 0 TO RETURN" 2020 INPUT START, FINISH : IF START = 0 THEN GOTO 2100 2030 POKE 82, 7 : POKE 201, 11:? : REM MOVE MARGIN, SET TAB 2040 FOR POSITION = START TO FINISH 2050 VALUE = PEEK(POSITION) : ? POSITION; "=" ; VALUE, 2055 HALT = PEEK(53775) : IF HALT = 251 THEN GOTO 2055 2060 NEXT POSITION 2070 POKE 82, 2 : REM RESTORE MARGIN 2075 POKE 764, 255 2080 GOTO 2000 2700 RETURN 3000 REM QUIT 3010 ?"NORMAL END OF JOB" : END