P21Forth provides a traditional Forth cooperative multitasker. The idea behind the multitasker is that while programs are waiting for something to happen they can let other code run by executing multiple tasks.
The most important word is the word PAUSE. PAUSE is the word that switches control from one task the next task in the task list. Each task will have control until it executes the word PAUSE and control is passed on to the next task in the list. PAUSE will execute in just a couple of microseconds to switch tasks. If there is only one task in the list the word PAUSE will just return to the word after PAUSE in the same task.
` P?RX `?KEY !will set the execution vector for ?KEY to the word P?RX. P?RX contains both a PAUSE and a ?RX. This way the system will switch tasks each time it checks the keyboard.
There are several steps needed to used the multitasker. First the multitasker should be installed into the word that reads the keyboard as in the above example. Then you must allot the memory for use by the task. The word to do this is HAT, as in I put on a different hat when I do a different job. The word HAT takes three parameters from the stack. These specify the number of memory cells to be allocated for use by any user variables beyond the five default user variables, the number of memory cells to be used for the data stack by this task, and the the number of memory cells to be used by the return stack for this task. The word HAT also takes a name from the input stream.
0 64 64 HAT MYTASKwould create a new word called MYTASK and allot enough memory for this word to contain the default 5 user variables for each task, and 64 cells of memory for the data stack and 64 cells for the return stack for the task MYTASK.
MYTASK BUILDwill initialize the data structure of the word MYTASK, and link it into the task list.
If you define a word called TASKDEMO as:
: TASKDEMO ( -- ) \ define a new word called TASKDEMO 0 V1 ! \ set variable V1 to 0 MYTASK ACTIVATE \ start a background task in MYTASK at the next word BEGIN \ TASK( begin a loop PAUSE 1 V1 +! \ switch tasks each time and increment variable V1 AGAIN ; \ )TASK repeat the loopA background task is now ready to run. If you now type TASKDEMO this task will begin to run in the background. This will be obvious if you test the value in the variable V1 by typing the command:
V1 ?Each time you test V1 it will have a different value. This is because it is being incremented tens of thousands of times per second by the background task TASKDEMO.
MYTASK SLEEPwill suspend the execute of the task. The command:
MYTASK AWAKEwill restart the task if it is sleeping.
The word TASKS can be used to examine the current list of tasks and see the status and name of each task.
Each task will have its own stacks, and its own user variables. The variable UP (User Pointer) points to the area used for user variables and stacks by a task. In P21Forth UP will contain a pointer to the location of the bottom of the return stack for a task. The return stack will build up in memory from this location. The five default user variables and any extra user variables for each task will be directly below this location, and the data stack for this task will be just below that. WINDOW is not automatically assigned a space for each task declared. Only space for five default user variables is set up. If a task wants to reserve a protected space for a WINDOW user variable to manipulate its own graphics windows a space can be reserved in the user variable parameter in the word HAT.
<-data stack area-><-user variables-><-return stack area->There are six default user variables for each task.
WINDOW \ pointer to a table of graphic window parameters STATUS \ contains either the executable vector wake or pass FOLLOWER \ contains the address of the next task's STATUS TOS \ contains the top stack location for a task when not running TID \ contains a pointer to the task name TF \ contains a THROW FRAME for error handling in this taskTasks can read each others user variable by using the `S word. In the above example one can get the address of the TID user variable for the MYTASK task with the command:
MYTASK TID `S
TASKS ( -- ) \ show the names and status of the tasks in the task list SUP ( -- tid ) \ start up area HAT ( n n n -< name>- ) \ create a new task ACTIVATE ( -- ) \ start a task with the code following ACTIVATE BUILD ( tid -- ) \ initialize and link in a new task AWAKE ( tid -- ) \ awaken a task SLEEP ( tid -- ) \ put a task to sleep STOP ( -- ) \ sleep the current task 'S ( tid a -- a ) \ access a different task'S user variables PAUSE ( -- ) \ switch tasks WAKE ( -- a ) \ address of wake wake ( -- ) \ used in STATUS for tasks that are awake PASS ( -- a ) \ address of pass pass ( -- ) \ used in STATUS for tasks that are asleep TF ( -- a ) \ THROW FRAME for error handling TID ( -- a ) \ Task ID for this task TOS ( -- a ) \ Top of Stack storage FOLLOWER ( -- a ) \ address of next tasks STATUS word STATUS ( -- a ) \ contains wake or pass WINDOW ( -- a ) \ contains a pointer to a table of graphics window parameters UP ( -- a ) \ pointer to the user are of this task USER ( n -< name >- ) \ creates new user variables GET ( semaphore -- ) \ get shared resource RELEASE ( semaphore -- ) \ release shared resource
9 B THRU DECIMAL TASKDEMO MULTI-SERIAL (or MULTI-PARALLEL)If you are using serial input it is best to turn the multitasker on at the end of the phrase because the multitasker may interfere with correctly reading the start bit of serial input at higher baud rates.
The demo source can be viewed with 9 LIST, A LIST, B LIST. The demo sets up 8 tasks, the P21Forth interpretter is the first task, there are 6 sleeping tasks T2 thru T7 and the task T8 increments a double word counter V by 8 every time it runs. T8 also displays the counter in the upper left hand corner of the screen on every 1024 task changes. It will demonstate about 200k task switches per second. The phrase T8 SLEEP will halt the T8 task and the phrase T8 AWAKE will start it back up.
Return to Table of Contents