68xxx multitasker





The 68xxx multitasker follows the model introduced with the v6.1 compilers. A few extensions are also provided.




Configuration - normally performed earlier

0 equ test-multi?       \ true to compile test code

If not previously defined, TEST-MULTI? is set to zero and test code is not compiled.




TCB data structure layout


cell       LINK    link to next task
cell       SSP     Saved Stack Pointer
cell       STAT    BIT 0   1 = running, 0 = halted
                   BIT 1   1 = message pending
                   BIT 2   1 = event triggered
                   BIT 3   1 = event handler run
                others  1 = set to run task, available to user
cell       TASK    Task # that sent message here
cell       MESG    Message address
cell       EVNTw   CFA of word run as event handler

This structure is allocated at the start of the USER area. Consequently the TCB of the current task is given by UP.

struct /TCB     \ -- size

The structure used by the code that matches the description above.




Task handling primitives

init-u0 constant main   \ -- addr ; tcb of main task

Returns the base address of the main task's USER area.

0 value multi?  \ -- flag ; true if tasker enabled

Returns true if the tasker is enabled.

: single        \ -- ; disable scheduler

Disable scheduler.

: multi         \ -- ; enable scheduler

Enable scheduler.

CODE pause      \ -- ; the scheduler

The software scheduler itself.

code status     \ -- task-status

Returns the current task's status cell, but with the run bit masked out.

CODE restart    \ task -- ; mark task TCB as running

Sets the RUN bit in the task's status cell.

CODE halt       \ task# -- ; reset running bit in TCB

Clears the RUN bit in the task's status cell.

: stop          \ -- ; halt oneself

HALT's the current task, and executes PAUSE.




Event handling

Event handling is only compiled if the equate EVENT-HANDLER? is set non-zero in the control file.

: set-event     \ task -- ; set event trigger in task TCB

Set the event trigger in task TCB.

: event?        \ task -- flag ; true if task had event

Returns true if true if task has received an event trigger which has not been cleared yet.

: clr-event-run \ -- ; reset own EVENT_RUN flag

Reset the current task's EVENT_RUN flag.

: to-event      \ xt task -- ; define action of a task

Sets XT as the event handler for the task.




Message handling

Message handling is only compiled if the equate MESSAGE-HANDLER? is set non-zero in the control file.

: msg?          \ task -- flag ; true if task has message

Returns true if task has received a message.

: send-message  \ addr task -- ; send message to task

Send a message to a task.

: get-message   \ -- addr task ; wait for any message

Wait for any message and return the message and the task it came from.

: wait-event/msg        \ -- ; wait for message or event trigger

Wait for a message or an event trigger.




Task structure management

code init-task  \ xt task -- ; Initialise a task stack

Initialise a task's stack before running it and set it to execute the word whose XT is given.

: add-task      \ task -- ; insert into list

Add the task to the list of tasks after the current task.

: sub-task      \ task -- ; remove task from chain

Remove the task from the task list.

: initiate      \ xt task -- ; start task from scratch and run it

Start the given task executing the word whose XT is given, e.g.

  ['] <name> <task> INITIATE
: sleeper       \ xt task -- ; start task from scratch, but leave it HALTed

Use in the form:


  ['] <action> <taskname> SLEEPER

to put a task on the active task list, but as if HALTed. SLEEPER allows you to make a task ready for waking up later, perhaps by another task. This avoids having to put STOP as the first word in a task. Note that SLEEPER does not call PAUSE. See also INITIATE.

: terminate     \ task -- ; stop task, and remove from list

Stop a task, and remove it from the list.

: init-multi    \ -- ; initialisation with multi-tasking

Initialise the multitasker and start it. If tasking is selected by setting the equate TASKING? in the control file, KERNEL62.FTH will automatically run this word. Make sure that your initialisation code includes INIT-MULTI or your code will crash.

: his           \ task uservar -- addr ; produce address of user var in another task

Given a task id and a USER variable, returns the address of that variable in the given task. This word is used to set up USER variables in other tasks.




semaphores

The semaphore code is only compiled if the equate SEMAPHORES? is set non-zero in the control file.

A SEMAPHORE is an extended variable used for signalling between tasks, and for resource allocation. The counter field is used as a count of the number of times the resource may be used, and the arbiter field contains the TCB of the task that last gained access. This field can be used for priority arbitration and deadlock detection/arbitration.

: semaphore     \ -- ; -- addr [child]

Creates a semaphore which returns its address at runtime. Use in the form:

Semaphore <name>
: signal        \ addr --

SIGNAL increments the counter field of a semaphore, indicating either that another item has been allocated to the resource, or that it is available for use again, 0 indicating in use by a task REQUEST waits until the counter field of a semaphore is non-zero, and then decrements the counter field by one. This allows the semaphore to be used as a COUNTED semaphore. For example a character buffer may be used where the semaphore counter shows the number of available characters. Alternatively the semaphore may be used purely to share resources. The semaphore is initialised to one. The first task to REQUEST it gains access, and all other tasks must wait until the accessing task SIGNALs that it has finished with the resource.




TASK and START:

TASK <name> builds a named task user area. The action of a task is assigned and the task started by the word INITIATE

  ['] <action> <task> INITIATE

START: is used inside a colon definition. The code before START: is the task's initialisation, performed by the current task. The code after START: up to the closing ; is the action of the task. For example:


  TASK FOO
  : RUN-FOO
    ...
    FOO START:
    ...
    begin ... pause again
  ;

All tasks must run in an endless loop, except for initialisation code. When RUN-FOO is executed, the code after START: is set up as the action of task FOO and started. RUN-FOO then exits.

If you want to perform additional actions after starting the task, you should use INITIATE to start the task.

variable task-chain     \ -- addr

Anchors list of all tasks created by TASK and friends.

: task          \ -- ; -- task ; TASK <name> builds a task

Note that the cross-interpreter's version of TASK has been modified from v6.2 onwards to leave the current section as CDATA.

: task          \ -- ; -- task ; TASK <name> builds a task

Creates a new task and data area, returning the address of the user area at run time. The task is also linked into the task chain anchored by TASK-CHAIN.

: start:        \ task -- ; exits from caller

Used inside a colon definition. The code following START: up to the ending semi-colon forms the action of the task. The word containing START: finishes at START:.




Debugging tools

: .task         \ task --

Display task's name if it has one.

: .tasks        \ task -- ; display all task names

Display all the tasks anchored by TASK-CHAIN.

: .running      \ --

Display running tasks.