by Richard Russell, June 2020
In BBC BASIC, a block of memory can be allocated either from the heap or from the stack; the statements to do this are as follows:
DIM memory%% size%
This statement allocates size%+1 bytes of contiguous memory from the heap and returns a pointer to the first byte in the variable memory%%. Because the heap grows upwards, and only ever increases in size, this block of memory can only be freed by using CLEAR, which destroys all variables, arrays, structures etc. (except the static integer variables A% to Z%). Therefore this kind of allocation is not suitable for 'dynamic' memory.
DIM memory%% LOCAL size%
This statement, which can only be used inside a function (FN) or procedure (PROC), allocates size%+1 bytes of memory from the stack and returns a pointer to the first byte in the variable memory%%. The memory is automatically freed on return from the function or procedure (or on execution of a RESTORE LOCAL statement). As such it is suitable for some dynamic memory applications but only when it's acceptable for the memory to exist only for the duration of a single FN/PROC, and when it's OK to exit and re-enter the FN/PROC in order to resize the block.
Although these two options will satisfy most memory allocation requirements, sometimes they may be too limiting. If you want to be able to allocate and free memory blocks without the constraints they impose, one option is to call an Operating System API function to do so. For example in BBC BASIC for Windows you can allocate memory using SYS “GlobalAlloc” and free it again using SYS “GlobalFree”. Similarly in BBC BASIC for SDL 2.0 you can allocate memory using SYS “SDL_malloc” and free it with SYS “SDL_free”.
But, when the amounts of memory you want to allocate are relatively small, there is an alternative approach that does not require API calls and works in both BB4W and BBCSDL. BBC BASIC already has a reasonably sophisticated dynamic memory management mechanism which it uses for strings, so the trick is to leverage this by allocating the memory block in a string! It turns out that this is relatively straightforward, as the function and procedure listed below demonstrate:
DEF FN_heapalloc(N%) LOCAL a$, p%% a$ = STRING$(N% + 23, CHR$0) p%% = PTR(a$) + 23 AND -16 SWAP ](p%%-8), ]^a$ = p%%
This function takes as a parameter the number of bytes of memory that you want to allocate, and returns a pointer to the first byte. The memory is automatically 16-byte aligned (which is typically the case for the OS-provided allocation functions too) and zero-filled.
DEF PROC_heapfree(p%%) LOCAL a$ : SWAP ]^a$, ](p%%-8) : a$ = "" : ENDPROC
This procedure takes as a parameter a pointer to a block of memory allocated with FN_heapalloc() and frees it.
These routines may not be as efficient or fast as the OS equivalents, and they use up space in BASIC's heap which the OS allocation functions don't, but nevertheless there may be occasions when they are useful.
When this article was originally published I didn't include an equivalent of the realloc() functionality, such as can be achieved in BBCSDL using SYS “SDL_realloc”. To rectify that omission here is a function which can re-size a block previously allocated with FN_heapalloc():
DEF FN_heaprealloc(p%%, N%) LOCAL a$ : N% += 23 SWAP ]^a$, ](p%%-8) p%% -= PTR(a$) IF N% < LENa$ a$ = LEFT$(a$,N%) IF N% > LENa$ a$ += STRING$(N% - LENa$, CHR$0) p%% += PTR(a$) SWAP ](p%%-8), ]^a$ = p%%
Please note that whilst FN_heapalloc() always returns a paragraph-aligned block of memory, FN_heaprealloc() doesn't. If it's essential that the memory be aligned you should create a new block of the wanted size, copy the contents of the old block into it, and free the old block.