The Prizm OS supports 10 software timers, which allow a function to be called at a certain time in the future and then periodically until its timer is stopped (or the function to call could be made to stop its timer after, for example, one call). Timers provide what is, so far, the thing closest to multi-threading available on unofficial Prizm add-in development. The OS doesn't appear to have any other multi-threading mechanisms in place.
Timers can be controlled using a few syscalls.
System and user timers
Out of the 10 timers, only six (numbers 5 through 10) are intended for use by child processes, that is, add-ins and built-in OS apps. These user timers are automatically stopped when the child process is not on the foreground: for example, whenever the Main Menu is open or the main eActivity screen is shown while a strip is kept in the background. The user timers are also uninstalled automatically when the child process is switched (i.e., when an add-in or built-in app quits for another to take place).
The first four timers (numbers 1 to 4) are system timers and are never stopped as long as the OS is running. Because of this, their handlers must be located somewhere which doesn't get cleared when switching child processes. Some system timer slots are always busy, for example the timers that blink the cursor or handle USB communication (timer 4). Using system timers is almost always a bad idea and can lead to a permanent brick, so if you use them make sure you know what you are doing.
Although timers are very useful, allowing software to give the appearance of multi-threading operation on simple tasks (like setting the status area text) even when GetKey is employed, their use has some drawbacks:
- Bfile syscalls cause unexpected behavior, when called from add-ins, if there are timers installed (apparently, they don't even need to be running) - see Incompatibility between Bfile Syscalls and Timers. If one wants to use timers in an add-in, one must stop and uninstall them before performing operations on the storage memory, and not forget to restore them later. Developing wrappers to the Bfile and Timer_* syscalls, that automatically keep track of used timers and uninstall and restore them when a Bfile operation is performed, is certainly a valid solution for this;
- Direct-drawing (DD) display functions can't be called from timer handlers. This unfortunately includes Bdisp_PutDisp_DD. This is because DD functions are not reentrant. However, modifying VRAM is mostly fine.
Because of this, timer usage may become a bit complex, or impossible, in certain cases. If all you need in your code is performing an operation after a certain delay, you might not need to use timers: you can develop a custom function which checks with the Real-Time Clock if the delay has passed, or you can use a syscall like RTC_Elapsed_ms (non-blocking) or OS_InnerWait_ms (blocking). Otherwise you'll need to find ways to deal with the limitations above.
Information on timers is kept on a table with 10 slots. Each timer slot is 16 bytes long, and is composed of four int variables (note: this information is valid for the fx-9860G, but is yet to be confirmed for the Prizm):
- slot - Indicates timer state. -1 when the slot is free, 0 when it is stopped and 1 when running;
- slot - Address of the handler, 0 when the slot is free;
- slot - Value that controls the timer handler calling frequency, -1 when the slot is free;
- slot - Counts down to zero from the value in slot; the timer handler is called when it reaches zero. Is -1 when the slot is free.