Loading and Executing Separate Code Modules


Using the LoadProgram() and LoadProgramPrio() calls mentioned previously, you can load an executable file from external storage, and launch it as a totally independent task. This gives the code its own memory pages and resource tracking. It also means that even if the program being loaded is only 4 KB big, it will still consume an entire page of RAM, which is normally 32 KB or 16 KB. Loading such programs can waste a lot of memory if you launch many of them.

Instead of launching separate tasks, it is generally better for an application to launch separate threads. As described above, you can use CreateThread() to do this with functions that are within your program.

In many cases, it is desirable to split up a program into multiple pieces, only one of which needs to be in memory at any given time. For example, the code to control the high score table of your game doesn't need to reside in memory when playing the game. It only needs to be loaded when the time comes to display the high score table.

Portfolio supports the partitioning of a game into multiple executable files with the LoadCode() function. Using LoadCode(), you can load the data for an executable file in memory, and then launch the code as a thread using ExecuteAsThread() or ExecuteAsSubroutine(). Once you finish with the code module, you can unload it from memory using the UnloadCode() function.

LoadCode() is defined as:

Err LoadCode( char *fileName, CodeHandle *code )
The fileName argument specifies the name of the executable file to load. This is a regular executable file as generated by the ARM linker, except that it must be linked with different startup code. If you plan on executing this file as a thread, then you must link the file with threadstartup.o instead of cstartup.o. If you plan on executing the file as a subroutine, it must be linked with subroutinestartup.o. The code argument is a pointer to a variable where a handle to the loaded code is stored.

LoadCode() reads the file and allocates memory to hold its contents. The allocated memory is within the address space of the current task. If the current task exits, the memory is automatically freed.

Once code has been loaded, it can be executed using either ExecuteAsThread() or ExecuteAsSubroutine().

Err ExecuteAsThread( CodeHandle code, uint32 argc, char **argv, char *threadName, int32 priority )
Err ExecuteAsSubroutine( CodeHandle code, uint32 argc, char **argv )

The code argument specifies the loaded code to run. This is the value stored by LoadCode() in the CodeHandle variable passed to it. argc and argv are two 32-bit values Portfolio does not use, and are just passed through to the code being executed. These are the values that the main() routine in the loaded code receives for its argc and argv parameters. Finally, when executing a thread, you must specify a thread name, and priority, just like when using CreateThread().

Once the code completes execution, call UnloadCode():

Err UnloadCode( CodeHandle code )
This removes the loaded code and frees its memory.

When you load external code and execute it using the above function, it is important to understand that the code is actually a separate executable file. This means that global variables in one code module cannot be accessed by a different code module.

When you execute loaded code as a thread, any memory this thread allocates does not get freed when the thread exits or when the code is unloaded. The thread must clean up after itself, just like a thread created using CreateThread().

If loaded code is being executed as a subroutine and calls exit(), this call causes the entire program, not just the subroutine, to exit.