Chapter Three
Memory Usage
Many of BASIC's functions are controlled by a set of tables
built in RAM not already occupied by BASIC or the Operating
System (OS). Figure 3.1 is a diagram of memory use by both
programs. Every time a BASIC programmer enters a statement,
memory requirements for the RAM tables change. Memory use
by the OS also varies. Different graphics modes, for example,
require different amounts of memory.
These changing memory requirements are monitored, and
this series of pointers keeps BASIC and the OS from overlaying
each other in memory:
- High memory address (HMADR) at location $02E5
- Application high memory (APHM) at location $000E
- Low memory address (LMADR) at location $02E7
When a graphics mode requires larger screen space,
the OS checks the application high memory address (APHM) that has been set by BASIC.
If there is enough room for the new screen, the OS uses the upper portion of space
and sets the pointer HMADR to the bottom of the screen to tell the application how
much space the OS is now using. BASIC builds its table toward high memory from low
memory. The pointer to the lowest memory available to an application, called LMADR
in the BASIC listing, is set by the OS to tell BASIC the lowest memory address that
BASIC can use. When BASIC needs more room for one of its tables, BASIC checks HMADR.
If there is enough room, BASIC uses the space and puts the highest address it has
used into APHM for OS. BASIC's operation consists primarily of building, reading,
and modifying tables. Pointers to the RAM tables are kept in consecutive locations
in zero page starting at $80. These tables are, in order,
- Multipurpose Buffer
- Variable Name Table
- Variable Value Table
- String/Array Table
13
Chapter
Three
- Statement Table
- Runtime Stack
BASIC reserves space for a buffer at LMADR. It then
builds the tables contiguously (without gaps), starting at the top of the buffer
and extending as far as necessary towards APHM. When a new entry needs to be added
to a table, all data in the tables above is moved upward the exact amount needed
to fit the new entry into the right place. Figure 3-1. Memory usage ----------------------
FFFF | Operating System | | ROM | E000 ---------------------- | Floating Point |
| ROM | D800 ---------------------- | Hardware Registers | D000 ----------------------
| Unused | BFFF ---------------------- | BASIC ROM | A000 ----------------------
| Screen | ----------------------<----HMADR Free RAM ----------------------<----
APHM | BASIC | | RAM | | Tables | ----------------------<---- LMADR | Operating
System | | RAM | 0000 ---------------------- 14
Chapter
Three
Variable Name Table The Variable Name Table (VNT) is built during the pre-compile
process. It is read, but not modified, during execution but only by the LIST statement.
The VNT contains the names of the variables used in the program in the order in which
they were entered. The length of entries in the Variable Name Table depends on the
length of the variable name. The high order bit of the last character of the name
is on. For example, the ATASCII code for the variable name ABC is 414243 (expressed
in hexadecimal). In the Variable Name Table it looks like this: 41 42 C3 The $ character
of a string name and the ( character of an array element name are stored as part
of the variable name. The table entries for variables C, AA$, and X(3) would look
like this: C C3 AA$ 41 41 A4 X(3) 58 A8 It takes only two bytes to store X(3) because
this table stores only X(. A variable is represented in BASIC by a token. The value
of this token is the position (relative to zero) of the variable name in the Variable
Name Table, plus $80. BASIC references an entry in the table by using the token,
minus $80, as an index. The Variable Name Table is not changed during execution time.
The zero page pointer to the Variable Name Table is called VNTP in the BASIC listing.
Variable
Value Table
The Variable Value Table (VVT) is also built during the pre– compile process. It
is both read and modified during execution. There is a one-to-one correspondence
in the order of entries between the Variable Name Table and the Variable Value Table.
If XXX is the fifth variable in the Variable Name Table, then XXX's value is the
fifth entry in the Variable Value Table. BASIC references a table entry by using
the variable token, minus $80, as an index Each entry in the Variable Value Table
consists of eight bytes. The first two bytes have the following meaning: 15
Chapter
Three
1 2 --------------- | | | --------------- type vnum type
= one byte, which indicates the type of variable $00 for floating point variable
$40 for array variable $80 for string variable vnum = one byte, which indicates
the relative position of the variable in the tables The meaning of the next six bytes
varies, depending on the type of variable (floating point, string, or array). In
all three cases, these bytes are initialized to zero during syntaxing and during
the execution of the RUN or CLR. When the variable is a floating point number, the
six bytes represent its value. When the variable is an array, the remaining six bytes
have the following format 1 2 3 4 5 6 7 8 ------------------------- | | | | | | |
| | ------------------------- |disp | dim1| dim2 disp = the two-byte displacement
into string/array space of this array variable dim1 = two bytes indicating
the first dimension value dim2 = two bytes indicating the second dimension
value All three of these values are set appropriately when the array is DIMensioned
during execution. When the variable is a string, the remaining six bytes have the
following meaning: 1 2 3 4 5 6 7 8 ------------------------- | | | | | | | | | -------------------------
|disp | curl| maxl 16
Chapter
Three
disp = the two-byte displacement into string/array space
of this string variable. This value is set when the string is DIMensioned during
execution. curl = the two-byte current length of the string. This value changes
as the length of the string changes during execution. maxl = the two-byte
maximum possible length of this string. This value is set to the DIM value during
execution. When either a string or an array is DIMensioned during execution, the
low-order bit in the type byte is turned on, so that the array type is set to $41
and the string type to $81. The zero page pointer to the Variable Value Table is
called VVTP in the BASIC listing. Statement Table The Statement Table, built as each statement is entered
during editing, contains tokenized forms of the statements that were entered. This
table determines what happens during execution. The format of a Statement Table entry
is shown in Figure 3-2. There can be several tokens per statement and several statements
per line. Figure 3-2. Format of a Statement Table Entry ---+-----------------------
----------------------- --------------- | | | | | .... | | | | .... | | | ---+-----------------------
----------------------- --------------- |lnum | llen| slen| snt | toks | eos |slen
| snt | toks | eos | eol | lnum = the two-byte line number (low-order, high-order)
llen = the one-byte line length (the displacement to the next line in the
table) slen = the one-byte statement length (the displacement to the next
statement in the line) snt = the one-byte Statement Name Token toks = the
other tokens that make up the statement (this is variable in length) eos =
the one-byte end of statement token eol = the one-byte end of line token The
zero page pointer to the Statement Table is called STMTAB in the BASIC listing. 17
Chapter
Three
String/Array Table The String/Array Table (also called String/Array Space)
is created and modified during execution. Strings and arrays can be intermixed in
the table, but they have different formats. Each array or string is pointed to by
an entry in the Variable Value Table. The entry in the String/Array Table is created
when the string or array is DIMensioned during execution. The data in the entry changes
during execution as the value of the string or an element of the array changes. An
entry in the String/Array Table is not initialized to any particular value when it
is created. The elements of arrays and the characters in a string cannot be counted
upon to have any particular value. They can be zero, but they can also be garbage
- data previously stored at those locations. Array Entry For an array, the
String/Array Table contains one six-byte entry for each array element. Each element
is a floating point number, stored in raveled order. For example, the entry in the
String/Array Table for an array that was dimensioned as A(1,2) contains six elements,
in this order: A(0,0) A(0,1) A(0,2) A(1,0) A(1,1) A(1,2) String Entry A string
entry in the String/Array Table is created during execution, when the string is DiMensioned.
The size of the entry is determined by the DIM value. The "value" of the
string to BASIC at any time is determined by the data in the String/Array Table and
the current length of the string as set in the Variable Value Table. The zero page
pointer to the String/Array Table is called STARP in the BASIC listing. The Runtime
Stack is created during execution. BASIC uses this LIFO stack to control processing
of FOR/NEXT loops and GOSUBs. When either a FOR or a GOSUB statement is encountered
during execution, an entry is put on the Runtime Stack. When a NEXT, RETURN, or a
POP statement is encountered, entries are pulled off the stack. Both the FOR entry
and the GOSUB entry have a four-byte header: 18
Chapter
Three
----------------- | | + | | ----------------- type lnum disp type = one byte
indicating the type of element GOSUB type = 0 FOR type = non-zero lnum = the
two-byte number of the line which contains the statement (low-order, high-order)
disp = one byte indicating the displacement into the line in the Statement
Table of the token which caused this stack entry. The FOR-type byte is actually the
token representing the loop control variable from the FOR statement. (In the statement
FOR I = I to 10, I is the loop control variable.) So the FOR-type byte will have
a value of $80 through $FF - the possible values of a variable token. The FOR entry
contains 12 additional bytes, formatted like this: 1 2 3 4 5 6 7 8 9 10 11 12 -----+----+----+----+----+---------+----+----+----+----+-----
| | | -----+----+----+----+----+---------+----+----+----+----+----- sval step sval
= the six-byte (floating point) limit value at which to stop the loop step
= the six-byte (floating point) STEP value to increment by The GOSUB entry consists
entirely of the four-byte header. The LIST and READ statements also put a GOSUB type
entry on the Runtime Stack, so that the line containing the LIST or READ can be found
again when the statement has finished executing. The zero page pointer to the Runtime
Stack is called RUNSTK in the BASIC listing. 19
Chapter
Three
Zero Page Table Pointers The starting addresses of the tables change dynamically
during both program construction and program execution. BASIC keeps the current start
addresses of the tables and other pointers required to manage memory space in contiguous
zero- page cells. Each pointer is a two-byte address, low byte first. Since these
zero page cell addresses remain constant, BASIC is always able to find the tables.
Here are the zero page pointers used in memory management, their names in the BASIC
listing, and their addresses: Multipurpose Buffer $80,$81 Variable Name Table VNTP
$82,$83 VNT dummy end VNTD $84,$85 Variable Value Table VVTP $86,$87 Statement Table
STMTAB $88,$89 Current Statement Pointer STMCUR $8A,$8B StringlArray Table STARP
$8C,$8D Runtime Stack RUNSTK $8C,$8E Top of used memory MEMTOP $90,$91 Memory
Management Routines Memory Management routines allocate space to the BASIC tables as
needed. There are two routines: expand, to add space, and contract, to delete space.
Each routine has one entry point for cases in which the number of bytes to be added
or deleted is less than 256, and another when it is greater than or equal to 256.
The EXPAND and CONTRACT routines often move many thousands of bytes each time they
are called. The 6502 microprocessor is designed to move fewer than 256 bytes of data
very quickly. When larger blocks of data are moved, the additional 6502 instructions
required can make the process very slow. The EXPAND and CONTRACT routines circumvent
this by using the less-than-256-byte fast-move capabilities in the movement of thousands
of bytes. The end result is a set of very fast and very complex data movement routines.
All of this complexity does have a drawback. The infamous Atari BASIC lock-up problem
lives in these two routines. If an EXPAND or CONTRACT requires that an exact multiple
of 256 bytes be moved, then the routines move things from the wrong 20
Chapter
Three
place in memory to the wrong place in memory, whereupon the computer
locks up and won't respond. The only way to avoid losing hours of work this way is
to SAVE to disk or cassette frequently. EXPAND ($A881) Parameters at entry: register X = the zero page address
containing the pointer to the location after which space is to be added Y = the low-order
part of the number of bytes to expand A = the high-order part of the number of bytes
to expand The routine creates a hole in the table memory, starting at a requested
location and continuing the requested number of bytes. The routine first checks to
see that there is enough free memory space to satisfy the request. It adds the requested
expand size to each of the zero-page table pointers between the one pointed to by
the X register and MEMTOP. Then each pointer will point to the correct address when
EXPAND is done. EXPAND then creates space at the address indicated by the X register.
The number of bytes required is contained in the Y and A registers. (Y contains the
least significant byte, while A contains the most significant.) All data from the
requested address to the address pointed to by MEMTOP is moved toward high memory
by the requested number of bytes. This creates a hole of the proper size. The routine
then sets Application High Memory (APHM) to the value in MEMTOP. This tells the OS
the highest memory address that BASIC is currently using. EXPLOW ($A87F) Parameters at entry: register X = zero page address
containing the pointer to the location after which space is to be added Y = number
of bytes to expand (low-order byte only) 21
Chapter
Three
This is an additional entry point for the EXPAND routine. It is
used when the number of bytes to be added to the table is less than 256. This routine
first loads the 6502 accumulator with zero to indicate the most significant byte
of the expand length. It then functions exactly like EXPAND. CONTRACT ($A8FD) Parameters at
entry: register X = zero page address containing the pointer to the starting location
where space is to be removed Y = the low-order part of the number of bytes to contract
A = the high-order part of the number of bytes to contract This routine removes a
requested number of bytes at a requested location by moving all the data from higher
in the tables downward the exact amount needed to replace the unwanted bytes. It
subtracts the requested contract size from each of the zero page table pointers between
the one pointed to by the X register and MEMTOP. Then each pointer will point to
the correct address when CONTRACT is done. The routine sets application high memory
(APHM) to the value in MEMTOP to indicate to the OS the highest memory address that
BASIC is currently using. The block of data to be moved downward is defined by starting
at the address pointed to by the zero-page address pointed to in X, plus the offset
number stored in Y and A, and then continuing to the address specified at MEMTOP.
Each byte of data in that block is moved downward in memory by the number of bytes
specified in Y and A, effectively erasing all the data between the specified address
and that address plus the requested offset. CONTLOW ($A8FB) Parameters at entry: register X = the zero page address
containing the pointer to the location at which space is to be removed 22
Chapter
Three
Y = the number of bytes to contract (low-order byte only) This routine
is used to remove fewer than 256 bytes from the tables at a requested location by
moving all the data from higher in the tables downward the exact amount needed to
replace the unwanted bytes. This routine first loads the 6502 accumulator with zero
to serve as the most significant byte of the contract length. It then functions exactly
like CONThACT. Miscellaneous Memory Allocations Besides the tables, which change dynamically, BASIC also
uses buffers and stacks at fixed locations. The Argument/Operator Stack is allocated
at BASIC's low memory address and occupies 256 bytes. During pre-compiling it is
used as the output buffer for the tokens. During execution, it is used while evaluating
an expression. This buffer/stack is referenced by a pointer at location $80. This
pointer has several names in the BASIC listing: LOMEM, ARGOPS, ARGSTK, and OUTBUFF.
The Syntax Stack is used during the process of syntaxing a statement. It is referenced
directly that is, not through a pointer. It is located at $480 and is 256 bytes long.
The Line Buffer is the storage area where the statement is placed when it is EN'TERed.
It is the input buffer for the edit and pre-compile processes. It is 128 bytes long
and is referenced directly as LBUFF. Often the address of LBUFF is also put into
INBUFF so that the buffer can be referenced through a pointer, though INBUFF can
point to other locations during various phases of BASIC's execution.
<-Chapter
02Chapter 04->