The current BASIC program starts at PAGE and ends at the byte immediately below TOP (i.e. the length of the program is TOP-PAGE bytes). The 'dynamic data structures' (variables, arrays etc.) start at LOMEM which by default is set equal to TOP (i.e. they follow immediately after your program). This area of memory, which is called the 'heap', grows upwards as more variables are created. The stack grows downwards from HIMEM which by default is set a little less than two Megabytes above PAGE, but may be raised above this value by the user (memory permitting) either using a program statement or (in BBC BASIC for Windows only) the Customize menu command.
As your program runs, the heap expands upwards towards the stack and the stack expands downwards towards the heap. If the two should meet, you get a 'No room' error. Fortunately, there is a limit to the amount by which the stack and the heap expand.
HIMEM END LOMEM/TOP PAGE
LibrariesStack
(unused)
HeapUser's Program
Top of stack Current limit of stack Current limit of heap Heap base/end of program Start of program
In general, the heap only expands whilst new variables, arrays or structures are being declared. However, altering the length of string variables can result in 'dead' string space which also causes the heap to expand.
In addition to storing the 'return addresses' and other information about the nested structures in your program (loops, procedures, functions etc.), the stack is also used 'internally' by the BBC BASIC interpreter. Its size fluctuates but, in general, it expands every time you increase the depth of nesting of your program structure and every time you increase the number of local variables in use.
However, there may still be situations where the amount of memory used by your program needs to be kept to a minimum. With careful design of your program, the size of both the stack and the heap can be reduced. Growth of the stack can be reduced by avoiding deeply nested structures and re-entrant routines. The number of LOCAL variables should be kept to a minimum (especially local arrays, which can use up a lot of stack space). Growth of the heap can be controlled by limiting the number of variables you use and by good string variable management. Again, arrays are particularly thirsty of memory.
Each time the function 'calls itself' the size of the stack increases, so the larger the number whose factorial is required the greater the amount of stack usage. By restructuring the problem in a non-recursive way the amount of stack used can be reduced, but at the expense of the size and readability of the program:DEF FN_Factorial(N) IF (N = 1) THEN = 1 ELSE = N * FN_Factorial(N-1)
It must be stressed that this example is illustrative only. It is very unlikely that the amount of stack used in this case would be significant.DEF FN_Factorial(N) LOCAL I,F F = 1 FOR I = N TO 1 STEP -1 F = F * I NEXT = F
|
The first variable created for each starting character is accessed via the index and subsequently created variables are accessed via the index and the chain. Consequently, there is some speed advantage to be gained by arranging for all your variables to start with a different character. If you compile your program to a standalone EXE, variable names are (by default) automatically distributed across the alphabet, so you gain the benefits without needing to modify your program.
|
The format of the memory occupied by a 64-bit integer variable called 'NUMBER%%' is shown below.
|
The smallest amount of space is taken up by a variable with a single letter name. The static integer variables, which are not included in the variable chains, use the names A% to Z%. Thus, the only single character names available for dynamic integer variables are a% to z% plus _% and `% (CHR$(96)). As shown below, 32-bit integer variables with these names will occupy 10 bytes:
|
|
To make up the complete variable, the address word, the name and a separator (zero) byte are added to the number. The format of the memory occupied by an 80-bit real variable called 'NUMBR' is shown below.
|
This is also the format used when numeric values are stored in memory using floating-point indirection when *FLOAT 80 mode is active.
To make up the complete variable, the address word, the name and a separator (zero) byte are added to the number. The format of the memory occupied by a 64-bit real variable called 'NMBR' is shown below.
|
This is also the format used when numeric values are stored in memory using floating-point indirection when *FLOAT 64 mode is active.
|
This is also the format used when numeric values are stored in memory using floating-point indirection when *FLOAT 40 mode is active (the default).
As with integer variables, variables with single character names occupy the least memory (however, the names A to Z are available for variant numeric variables). Whilst a real variable requires an extra byte to store the number, the '%' character is not needed in the name. Thus, integer and real variables with the same name occupy the same amount of memory. However, this does not hold for arrays, since the name is only stored once.
In the following examples, the bytes are shown in the more human-readable manner with the MSB on the left.
The value 5.5 would be stored as shown below.
Because the MSB is assumed to be 1, this would become:
Mantissa Exponent .0011 0000 0000 0000 0000 0000 0000 0000 1000 0010 Sign Bit &30 00 00 00 &82
The equivalent in decimal is:
Mantissa Exponent .1011 0000 0000 0000 0000 0000 0000 0000 1000 0010 &B0 00 00 00 &82
BBC BASIC for Windows and BBC BASIC for SDL 2.0 use variant numeric variables which can hold either integers or floating-point values, allowing the faster integer arithmetic routines to be used if appropriate. The presence of an integer value in a variant numeric variable is indicated by the stored exponent being zero. Thus, if the stored exponent is zero, the 4 byte mantissa holds the number in normal integer format.
(0.5+0.125+0.0625) * 2^(130-127) = 0.6875 * 2^3 = 0.6875 * 8 = 5.5
Depending on how it is put there, an integer value can be stored in a variant numeric variable in one of two ways. For example,
will set the exponent to zero and store the integer &00 00 00 05 in the mantissa. On the other hand,number=5
will set the exponent to &82 and the mantissa to &20 00 00 00.number=5.0
The two ways of storing an integer value are illustrated in the following four examples.
Example 1 | ||||||
number=5 | & 00 | 00 | 00 | 00 | 05 | Integer 5 |
Example 2 | ||||||
number=5.0 | & 82 | 20 | 00 | 00 | 00 | Real 5.0 |
This is treated as | ||||||
& 82 | A0 | 00 | 00 | 00 | ||
= = = |
(0.5+0.125)*2^(130-127) 0.625*8 5 | |||||
because the sign bit is assumed to be 1. | ||||||
Example 3 | ||||||
number=-5 | & 00 | FF | FF | FF | FB | |
The 2's complement gives | ||||||
& 00 | 00 | 00 | 00 | 05 | Integer -5 | |
Example 4 | ||||||
number=-5.0 | & 82 | A0 | 00 | 00 | 00 | Real -5.0 |
(The sign bit is already 1) | ||||||
= = Magnitude = |
(0.5+0.125)*2^(130-127) 0.625*8 5 |
If all this seems a little complicated, try using the program below to accept a number from the keyboard and display the way it is stored in memory. The program displays the 4 bytes of the mantissa in 'human readable order' followed by the exponent byte. Look at what happens when you input first 5 and then 5.0 and you will see how this corresponds to the explanation given above. Then try -5 and -5.0 and then some other numbers. The program is an example of the use of the byte indirection operator. See the Indirection section for details.
The layout of the variable 'NMBR' in memory is shown below.
|
REPEAT INPUT "Enter a number: " NMBR PRINT "& "; : A% = ^NMBR REM Step through mantissa from MSB to LSB FOR I% = 3 TO 0 STEP -1 REM Look at value at address A%+I% num$ = STR$~(A%?I%) IF LEN(num$)=1 num$="0"+num$ PRINT num$;" "; NEXT : REM Look at exponent at address A%+4 num$ = STR$~(A%?4) IF LEN(num$)=1 num$="0"+num$ PRINT " & "+num$'' UNTIL NMBR=0
BBC BASIC for Windows version 5.95a or earlier:
|
BBC BASIC for Windows version 6.00a or later and BBC BASIC for SDL 2.0:
|
In 64-bit editions of BBC BASIC the string 'address' is actually a 32-bit offset from the base of the allocated heap memory.
The amount of memory allocated for the string depends on the current length of the string, according to the following formula:
So long as the length of the string is compatible with the allocated length, the string will be stored at the same address. If the variable is set to a string longer than this maximum length there will be insufficient room in the original position for the characters of the string. When this happens, the new string will be placed in a block of the correct length taken from the string free list or, failing that, on the top of the heap; its new start address will be loaded into the address bytes. The previous space occupied by the string is added to the free list. The same thing happens when the length of the string is reduced below the minimum value corresponding to the allocated length, otherwise wasted memory ('garbage') would result.
allocated_length = 2^INT(LOG2(current_length)+1)-1
For example if the allocated length is 2047 bytes (2^11-1) the string will be stored at the same address so long as its current length remains between 1024 and 2047 characters. If its length is reduced below 1024 characters or increased above 2047 characters the string will be moved to a new address and the 2047-byte block of memory added to the string free list.
The format of the structure heap entry is very similar to that of an ordinary variable, except that instead of a data value it includes two 32-bit pointers (links) to the format and data blocks. The heap entry of a structure called STRU{} would be stored as follows:
|
In 64-bit editions of BBC BASIC the Format block address and the Data block address are 64-bits and the structure heap entry is 8 bytes longer.
Note that the stored name includes the left brace and it is this which identifies the heap entry as a structure.
The format block is of variable length, and consists of a 32-bit value containing the total data size (in bytes) followed by a linked-list of structure members. The linked list has the same format as the main variable lists (chains) except that instead of actual data values it contains 32-bit offsets into the data block. The format block for the structure STRU{a,b} would be stored in memory as follows:
|
|
would place &54 (T) at address S%, &68 (h) at address S%+1 etc. Because the string is placed at a predetermined location in memory it is called a 'fixed' string. Fixed strings are not included in the variable chains and they do not have the overheads associated with a string variable. However, since the length of the string is not stored, an explicit terminator (&0D) is used. Consequently, in the above example, byte S%+16 would be set to &0D. Fixed strings are restricted in length to 65535 bytes (65536 bytes including the terminating &0D).DIM S% 256 $S% = "This is a string"
If you use $$ rather than $ the string in memory is NUL-terminated (&00) rather than CR-terminated (&0D). So for example:
would set byte S%+16 to &00.DIM S% 256 $$S% = "This is a string"
The format of the array heap entry is very similar to that of an integer variable, except that instead of a 32-bit data value it includes a 32-bit pointer (link) to the array descriptor. The heap entry of an array called ARRAY%() would be stored as follows:
|
In 64-bit editions of BBC BASIC the array descriptor address is 64-bits and the array heap entry is 4 bytes longer.
Note that the stored name includes the left bracket (parenthesis) and it is this which identifies the heap entry as an array.
The array descriptor is of variable length, and consists of a single byte containing the number of dimensions (suffices) and four bytes for each of the dimensions, containing the size of that dimension (number of rows, columns etc). The array descriptor for the array ARRAY%(10,20) would be stored in memory as follows:
|
Note that the size of each dimension is equal to one greater than the suffix specified in the DIM statement, since the index can take any value from zero to the specified maximum suffix.
The array data follows immediately after the array descriptor, the number of elements being equal to the product of the sizes of each dimension. For example in the case of ARRAY%(10,20) the data consists of 11*21 = 231 values. Each data value consists of either one byte (in the case of a byte array), four bytes (in the case of a 32-bit integer numeric array), five bytes (in the case of a 40-bit variant numeric array), six bytes (in the case of a version 5 string array), eight bytes (in the case of a 64-bit integer array, 64-bit variant/double numeric array, structure array or version 6 string array) or ten bytes (in the case of an 80-bit variant numeric array).
CONTENTS |
CONTINUE |