56
End Sub
Sub MyTask()
' set the interval timer to 5 seconds
Call SetInterval(5.0)
Do
WaitForInterval()
<stuff-to-do-periodically>
Loop
End Sub
In this partial example, a task is set up to perform some important activity periodically. If this were part of
a system to control the chlorine level in a pool, MyTask might read a transducer that indicates chlorine
level and, if its below a certain level, cause a known amount of chlorine solution to be injected into the
circulation stream.
One important aspect of creating and using tasks is the allocation of a task stack. The task stack has two
components: a task control block and the portion used as an execution stack. The task control block is a
fixed size (see Section 3.27 for details) and resides either at the beginning (for VM mode devices) or at
end (for native mode devices) of the task stack. The remainder of the task stack is dedicated to the
execution stack for the task. The execution stack is used for local variables in the task routine,
parameters passed to other subroutines and functions that might be invoked as well as local variables
used in those routines, and space required for expression evaluation.
If a task stack is allocated that is much larger than is actually necessary, User RAM is wasted since no
other task can use the excess space. On the other hand, if a task stack is smaller than required, data
items preceding or following the task stack in memory will be overwritten. This will generally cause your
application to misbehave and may result in the processor resetting.
This begs the question, of course, of what is the optimum task stack size. Unfortunately, this is not as
easy to answer as it might seem. The difficulty in answering this question lies in the fact that stack use is
dynamic and may depend on external factors such as a signal applied by an external device or a user
pressing a key. Not only that, the stack use may depend on the order or the timing of such external
events. Further, recursive invocation of routines adds to the dynamic nature of stack use and is
impossible to account for (in most cases) using any kind of static analysis.
For VM mode ZX devices (e.g. the ZX-24a), the ZBasic compiler automatically estimates the minimum
task stack size. Information about the estimate, including the stack use of individual routines, is displayed
in the .map file (assuming that one is generated as is the default). If the size of the stack that you have
allocated is smaller than the estimated size plus a safety margin, the compiler will generate a warning
indicating the deficiency and indicating the minimum allocation required. By default, the safety margin is
10 bytes but you may specify a larger or smaller safety margin (including zero) using
Register.StackMargin.
For native mode ZX devices (e.g. the ZX-24n), the minimum task stack size cannot be determined at
compile time. To assist you in determining the appropriate task stack size a special System Library
function is provided that determines, at run time, how much unused space exists in the task stack. By
exercising your application and periodically checking the amount of unused task stack space exists, you
may be able to determine empirically a suitable minimum task stack size. See the description of
System.TaskHeadRoom()
3.5.1 Advanced Multi-tasking Options
For advanced users, a tasks main routine may also be defined with parameters. This may be useful to
provide information for the task to perform its function rather than doing so using global variables.
Parameters for the task are specified in a comma-separated list enclosed in parentheses just as they are
for a subroutine invocation. The number and types of the parameters specified must match those of the
task being invoked.