Allocating arrays using the Windows or SDL 2.0 API
by Richard Russell, December 2006
You can allocate arrays, using DIM, either on BASIC's heap (global and PRIVATE arrays) or on the stack (LOCAL arrays). Using a combination of these will satisfy the needs of most programs. For example a skeleton program might be structured as follows:
DIM globalarray1(...), globalarray2(...) REPEAT PROCdo_something PROCdo_something_else UNTIL finished END DEF PROCdo_something LOCAL localarray1(), localarray2() DIM localarray1(...), localarray2(...) REM ..... ENDPROC DEF PROCdo_something_else LOCAL localarray3(), localarray4() DIM localarray3(...), localarray4(...) REM ..... ENDPROC
Here the memory occupied by arrays localarray1() and localarray2() is freed on exit from the first procedure and can be reused by localarray3() and localarray4() in the second procedure. This is a flexible arrangement and some combination of global and local arrays will usually suffice.
However there may very rarely be situations which cannot satisfactorily be resolved using the capabilities of DIM. One is when the size of an array exceeds the amount of memory available to BBC BASIC for Windows (a total of 256 Megabytes). Another is when the memory occupied by the arrays cannot be allocated and freed in the required sequence, for example:
- Create first array
- Create second array
- Destroy first array
- Destroy second array
Because LOCAL arrays are allocated on the stack they can only be destroyed in the reverse order from which they were created.
In these cases it is possible to utilise the capabilities of the Windows or SDL 2.0 API to allocate and free the memory used by arrays. By setting BASIC's internal pointers to the allocated memory such arrays can be used exactly as if they were declared using DIM, but the restrictions on size and sequence of creation and destruction are removed.
The routines listed below illustrate how this can be achieved. For simplicity a separate routine is listed for 1-dimensional and 2-dimensional arrays. It should be relatively easy to see how this can be extended to arrays with more dimensions:
BBC BASIC for Windows:
DEF PROCdim1d(RETURN A(),S%,D1%) LOCAL A% SYS "GlobalAlloc", 64, 5+S%*(D1%+1) TO A% ?A%=1 : A%!1=D1%+1 !^A() = A% ENDPROC DEF PROCdim2d(RETURN A(),S%,D1%,D2%) LOCAL A% SYS "GlobalAlloc", 64, 9+S%*(D1%+1)*(D2%+1) TO A% ?A%=2 : A%!1=D1%+1 : A%!5=D2%+1 !^A() = A% ENDPROC DEF PROCundim(RETURN A()) SYS "GlobalFree", !^A() !^A() = 0 ENDPROC
BBC BASIC for SDL 2.0:
DEF PROCdim1d(RETURN A(),S%,D1%) LOCAL a%% SYS "SDL_malloc", 5+S%*(D1%+1) TO a%% IF @platform% AND &40 ELSE a%% = !^a%% ?a%%=1 : a%%!1=D1%+1 PTR(A()) = a%% ENDPROC DEF PROCdim2d(RETURN A(),S%,D1%,D2%) LOCAL a%% SYS "SDL_malloc", 9+S%*(D1%+1)*(D2%+1) TO a%% IF @platform% AND &40 ELSE a%% = !^a%% ?a%%=2 : a%%!1=D1%+1 : a%%!5=D2%+1 PTR(A()) = a%% ENDPROC DEF PROCundim(RETURN A()) SYS "SDL_free", PTR(A()) PTR(A()) = 0 ENDPROC
In each case the parameters supplied to the PROCdim procedure are the name of the array to be created, the size (in bytes) of each array element and the array's dimension(s). The element size should be specified as follows:
byte e.g. array&() | size=1 |
32-bit integer e.g. array%() | size=4 |
40-bit float e.g. array() | size=5 |
string e.g. array$() | size=6 |
64-bit float e.g. array#() | size=8 |
string e.g. array$() | size=8+ |
64-bit integer e.g. array%%() | size=8+ |
80-bit float e.g. array() | size=10+ |
+ BB4W version 6 only
Note that if you free a string array using PROCundim you must first empty all the elements (set them to zero-length strings), for example:
array$() = "" PROCundim(array$())
You could use the above procedures to allocate and free arrays in a sequence which cannot otherwise be achieved:
PROCdim1d(arr1%(),4,99) arr1%(50) = 12345678 PROCdim1d(arr2%(),4,99) arr2%(99) = 87654321 PRINT arr1%(50) PROCundim(arr1%()) PRINT arr2%(99) PROCundim(arr2%())