Memory Allocation
Many modern computer systems use dynamic memory allocation. They maintain a "heap" of free memory. At run-time, the code allocates memory from the heap. Complex algorithms are used for "garbage collection" or the release of resources back into the heap.
All this introduces significant overhead and makes your system's operation difficult to predict fully.
Are there situations when memory runs out? It may be hard to say when designing and testing your system "on the bench."
In a lot of cases, the strategy will be to give your system so much memory that the possibility of ever running out of it is reduced to a purely theoretical concept ... and yet it still happens because of bugs, memory leakage, flawed garbage collection, etc.
Tibbo BASIC and Tibbo C rely on a simpler and more reliable static memory allocation model. All memory for all variables and functions is allocated at compile time. Your system's behavior is completely predictable: if your application compiles then there will be no nasty run-time surprises!
Memory Allocation Algorithm
The allocation starts with global variables (including those from HTML files), as well as local static variables in C. The space for these must be reserved permanently.
Included in this space is also the memory needed for passing arguments and return values between functions.
Next, TIDE builds the call chain (tree) — the "who-calls-who" for every procedure in your project. TIDE also calculates the number of memory bytes needed by each procedure.
The map has as many entry points as there are event handlers in your application. This is because all code execution starts from invoking an event handler.
Naturally, there may be procedures that belong to several event handler "paths," like boo() in the diagram below.
Next, TIDE allocates the starting memory addresses for each procedure.
After that TIDE allocates the memory space for the stack. This is based on the longest call chain existing in the project.
Finally, if the project is being compiled for debugging, TIDE adds some memory needed for handling debug commands.
So the total memory required for the project is the sum total of:
- Global and static variables plus memory needed for passing arguments and return values
- The memory needed for the stack
- The memory needed for processing debug commands (only allocated when the project is compiled for debug)
- The memory needed for the longest call chain (in this example, the chain with the on_sys_init() entry point)
There are additional memory allocation rules that are used when the doevents statement is employed.
Implications
The static memory allocation model is not without its limitations.
Since TIDE must be able to build a call chain it is impossible to use pointers to functions. Having these would mean any procedure could potentially call any other procedure — a strict no-no in a world where the call chain must be known in advance.
No recursion or re-reentrant code are allowed, either. This is because function arguments and local variables are not stored on stack and there is no possibility to re-enter a procedure and get a new fresh set of local variables.