mirror of
https://github.com/forth-ev/VolksForth.git
synced 2025-02-18 03:30:36 +00:00
Finish the Implementation section of tasker.md.
This commit is contained in:
parent
3c9b171cad
commit
61e6d7814c
@ -1,10 +1,13 @@
|
|||||||
|
|
||||||
# The Multitasker
|
# The Multitasker
|
||||||
|
|
||||||
VolksForth comes with a simple but powerful multitasker which allows the creation of printer poolers, clocks, counters and other simple background tasks.
|
VolksForth comes with a simple but powerful multitasker which allows the
|
||||||
|
creation of printer spoolers, clocks, counters and other simple background
|
||||||
|
tasks.
|
||||||
|
|
||||||
The main characteristic of this multitasker is that it uses cooperative, not
|
The main characteristic of this multitasker is that it uses cooperative
|
||||||
preemtive multitasking. This means that each task has to explicitly yield
|
multitasking instad of preemtive multitasking.
|
||||||
|
This means that each task has to explicitly yield
|
||||||
execution control and access to I/O devices to make them available for other
|
execution control and access to I/O devices to make them available for other
|
||||||
tasks. However, each task can choose when this happens, esp. when to yield
|
tasks. However, each task can choose when this happens, esp. when to yield
|
||||||
execution control. Of course this must happen often enough for all tasks
|
execution control. Of course this must happen often enough for all tasks
|
||||||
@ -84,18 +87,58 @@ of the currently active task and calls the task switcher. The state of a task
|
|||||||
consists of the values of the Instruction Pointer (IP),
|
consists of the values of the Instruction Pointer (IP),
|
||||||
Return stack Pointer (RP) and Stack Pointer (SP).
|
Return stack Pointer (RP) and Stack Pointer (SP).
|
||||||
|
|
||||||
The task switcher consists of a closed loop
|
The task switcher consists of a closed round-robin loop made up of the
|
||||||
(see picture). Each task contains a machine code jump on
|
first 6 bytes of the user area of each task.
|
||||||
the next task ("jmp XXXX" in the picture), followed by
|
See the picture [Tasks' user areas](#tasks-user-areas) in the
|
||||||
"jsr (wake) in the picture. At the address, on
|
[Memory map](#memory-map) section.
|
||||||
which the jump command aims, are located
|
Each task's user area starts with a machine code jump to
|
||||||
Instructions of the next task. If this task is stopped,
|
the next task's user area (`JMP XXXX` in the picture), followed by
|
||||||
there is also a machine code jump to the next task
|
`JSR wake`. `wake` is the task wake-up routine which sets UP to the task's
|
||||||
made. If, on the other hand, the task is active,
|
user area address calculated from return address left by the `jsr`,
|
||||||
the (non-executive) BIT command has been replaced. The
|
restores SP from `user area + 6`and then restores RP and IP from the task's
|
||||||
follows the call of the wake-up procedure. This procedure invites
|
data stack. The task's state is thereby restored, and the next iteration of
|
||||||
state of task (consisting of SP ‘a RP and IP) and sets the
|
`NEXT` will call the task's next word, the one following the `PAUSE` that was
|
||||||
Userpointer (UP), so that it shows this task.
|
last invoked by the task.
|
||||||
|
|
||||||
|
A trick is employed to switch a task between active and inactive.
|
||||||
|
The JMP XXXX instruction at the user area's start, as described above, marks
|
||||||
|
an inactive task. When the round-robin loop reaches the task, the JMP
|
||||||
|
immediately forwards execution to the next task; the `JSR wake` is never
|
||||||
|
reached. For an active task, the JMP opcode is replaced by a BIT opcode (0x2c),
|
||||||
|
so that, when the task is jumped to, execution does reach the `JMP wake`
|
||||||
|
instruction, and the task runs until its reaaches the next `PAUSE`, which takes
|
||||||
|
the address of the BIT instruction for an indirect jump to the next task.
|
||||||
|
|
||||||
|
`SINGLETASK` changes `PAUSE` into a fast no-op so that no task change at all
|
||||||
|
takes place when `PAUSE` is called. This is the default of a VolksForth
|
||||||
|
system without loaded multitasker. `MULTITASK` enables the task-switching
|
||||||
|
behaviour of `PAUSE`.
|
||||||
|
|
||||||
|
The system supports the multitasker by invoking `PAUSE` during many I/O
|
||||||
|
operations such as `KEY`, `TYPE` and `BLOCK`. In many situations this is
|
||||||
|
already sufficient for a task (e.g. the printer pooler) to run smoothly.
|
||||||
|
In other situations a suitable placement of `PAUSE` calls within foreground
|
||||||
|
or background task code may be useful.
|
||||||
|
|
||||||
|
Tasks are created in the dictionary of the foreground or console task. Each
|
||||||
|
task has its own user area with a copy of the user variables.
|
||||||
|
The implementation of the system is, however, simplified through the
|
||||||
|
restriction that only the console task can interpret or compile input text.
|
||||||
|
There is e.g. only one vocabulary search order across the system; if one task
|
||||||
|
changes the search order, this affects all other tasks, too. But this is not
|
||||||
|
really disturbing, since only the console task should use the search order
|
||||||
|
anyway.
|
||||||
|
|
||||||
|
Incidentally, it is possible to forget active tasks: `FORGET` removes all
|
||||||
|
tasks from the round-robin loop that are located in the dictionary range to
|
||||||
|
forget. This can still go wrong, though, if the forgotten task holds a
|
||||||
|
"Semaphor" (see below). Semaphores are not released during forgetting,
|
||||||
|
and the associated device will remain blocked.
|
||||||
|
|
||||||
|
Finally, it should be mentioned that when invoking a task name, the address
|
||||||
|
of the task's user area will be placed on the stack.
|
||||||
|
|
||||||
|
## Memory map
|
||||||
|
|
||||||
#### Memory map of a task
|
#### Memory map of a task
|
||||||
```
|
```
|
||||||
@ -124,8 +167,8 @@ here -> ╠═════════════════|══╣ |
|
|||||||
There's a small unused area of 6 bytes between stack and heap to prevent heap
|
There's a small unused area of 6 bytes between stack and heap to prevent heap
|
||||||
corruption in case of a small stack underrun.
|
corruption in case of a small stack underrun.
|
||||||
|
|
||||||
And typically, the dictionary of any task but the main task will be empty, as
|
And typically, the dictionary of any task but the console task will be empty,
|
||||||
that is where the outer interpreter is running which usally populates the
|
as that is where the outer interpreter is running which usally populates the
|
||||||
dictionary through definitions. However, `dp` is a user variable, so each task
|
dictionary through definitions. However, `dp` is a user variable, so each task
|
||||||
has its own `here`, and if a task calls `allot`, the memory will be allocated
|
has its own `here`, and if a task calls `allot`, the memory will be allocated
|
||||||
in its own dictionary.
|
in its own dictionary.
|
||||||
@ -141,7 +184,7 @@ robin loop consists of
|
|||||||
|
|
||||||
│ ... │ task3 + 6
|
│ ... │ task3 + 6
|
||||||
├───────────┤
|
├───────────┤
|
||||||
│ jsr (wake │ task3 + 3
|
│ jsr wake │ task3 + 3
|
||||||
├───────────┤
|
├───────────┤
|
||||||
┌> -> -> │ jmp XXXX │-> ┐ task3 + 0
|
┌> -> -> │ jmp XXXX │-> ┐ task3 + 0
|
||||||
⋀ └───────────┘ ⋁
|
⋀ └───────────┘ ⋁
|
||||||
@ -151,7 +194,7 @@ robin loop consists of
|
|||||||
⋀ ⋁
|
⋀ ⋁
|
||||||
| | │ ... │ task2 + 6
|
| | │ ... │ task2 + 6
|
||||||
⋀ ⋁ ├───────────┤
|
⋀ ⋁ ├───────────┤
|
||||||
| | │ jsr (wake │ task2 + 3
|
| | │ jsr wake │ task2 + 3
|
||||||
⋀ ⋁ ├───────────┤
|
⋀ ⋁ ├───────────┤
|
||||||
| └> │ bit XXXX │-> ┐ task2 + 0
|
| └> │ bit XXXX │-> ┐ task2 + 0
|
||||||
⋀ └───────────┘ ⋁
|
⋀ └───────────┘ ⋁
|
||||||
@ -161,7 +204,7 @@ robin loop consists of
|
|||||||
⋀ ⋁
|
⋀ ⋁
|
||||||
| | │ ... │ task1 + 6
|
| | │ ... │ task1 + 6
|
||||||
⋀ ⋁ ├───────────┤
|
⋀ ⋁ ├───────────┤
|
||||||
| | │ jsr (wake │ task1 + 3
|
| | │ jsr wake │ task1 + 3
|
||||||
⋀ ⋁ ├───────────┤
|
⋀ ⋁ ├───────────┤
|
||||||
| └> │ bit XXXX │-> ┐ task1 + 0
|
| └> │ bit XXXX │-> ┐ task1 + 0
|
||||||
⋀ └───────────┘ ⋁
|
⋀ └───────────┘ ⋁
|
||||||
|
Loading…
x
Reference in New Issue
Block a user