@DATABASE Libraries Manual
@NODE MAIN "Amiga RKM Libraries: 21 Exec Tasks"
@INDEX Lib_Index/MAIN
@TOC Libraries_Manual/Exec
One of the most powerful features of the Amiga operating system is its
ability to run and manage multiple independent program tasks, providing
each task with processor time based on their priority and activity. These
tasks include system device drivers, background utilities, and user
interface environments, as well as normal application programs. This
multitasking capability is provided by the Exec library's management of
task creation, termination, scheduling, event signals, traps, exceptions,
and mutual exclusion.

This chapter deals with Exec on a lower level than most applications
programmers need and assumes you are already familiar with the Exec basics
discussed in the "@{"Introduction to Exec" link ADCD_v1.2:Reference_Library/Libraries/Lib_17/MAIN}" chapter of this manual.

@{" Task Structure " link 21-1}    @{" Task Exceptions " link 21-5}
@{" Task Creation " link 21-2}     @{" Task Traps " link 21-6}
@{" Task Termination " link 21-3}  @{" Processor and Cache Control " link 21-7}
@{" Task Exclusion " link 21-4}    @{" Function Reference " link 21-8}

@ENDNODE

@NODE 21-1 "21 Exec Tasks / Task Structure"
Exec maintains task context and state information in a task-control data
structure. Like most Exec structures, Task structures are dynamically
linked onto various task queues through the use of an embedded Exec list
@{"Node" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-1 19} structure (see the "@{"Exec Lists and Queues" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-1}" chapter).  Any task can
find its own task structure by calling @{"FindTask" link 21-8 5}(NULL).  The C-language
form of this structure is defined in the <exec/@{"tasks.h" link ADCD_v1.2:Inc&AD2.1/includes/exec/tasks.h/MAIN 23}> include file:

    struct Task  {
        struct Node tc_Node;
        UBYTE       tc_Flags;
        UBYTE       tc_State;
        BYTE        tc_IDNestCnt;   /* intr disabled nesting */
        BYTE        tc_TDNestCnt;   /* task disabled nesting */
        ULONG       tc_SigAlloc;    /* sigs allocated */
        ULONG       tc_SigWait;     /* sigs we are waiting for */
        ULONG       tc_SigRecvd;    /* sigs we have received */
        ULONG       tc_SigExcept;   /* sigs we will take excepts for */
        UWORD       tc_TrapAlloc;   /* traps allocated */
        UWORD       tc_TrapAble;    /* traps enabled */
        APTR        tc_ExceptData;  /* points to except data */
        APTR        tc_ExceptCode;  /* points to except code */
        APTR        tc_TrapData;    /* points to trap code */
        APTR        tc_TrapCode;    /* points to trap data */
        APTR        tc_SPReg;       /* stack pointer */
        APTR        tc_SPLower;     /* stack lower bound */
        APTR        tc_SPUpper;     /* stack upper bound + 2*/
        VOID      (*tc_Switch)();   /* task losing CPU */
        VOID      (*tc_Launch)();   /* task getting CPU */
        struct List tc_MemEntry;    /* allocated memory */
        APTR        tc_UserData;    /* per task data */
    };

A similar assembly code structure is available in the <exec/@{"tasks.i" link ADCD_v1.2:Inc&AD2.1/includes/exec/tasks.i/MAIN 27}>
include file.

Most of these fields are not relevant for simple tasks; they are used by
Exec for state and administrative purposes.  A few fields, however, are
provided for the advanced programs that support higher level environments
(as in the case of processes) or require precise control (as in devices).
The following sections explain these fields in more detail.

@ENDNODE

@NODE 21-2 "21 Exec Tasks / Task Creation"
To create a new task you must allocate a task structure, initialize its
various fields, and then link it into Exec with a call to @{"AddTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTask()}. The
task structure may be allocated by calling the @{"AllocMem()" link ADCD_v1.2:Reference_Library/Libraries/Lib_20/20-1} function with
the @{"MEMF_CLEAR" link ADCD_v1.2:Reference_Library/Libraries/Lib_20/20-1-1 37} and @{"MEMF_PUBLIC" link ADCD_v1.2:Reference_Library/Libraries/Lib_20/20-1-1 31} allocation attributes.  These attributes
indicate that the data structure is to be pre-initialized to zero and that
the structure is shared.

The @{"Task" link 21-1} fields that require initialization depend on how you intend to
use the task.  For the simplest of tasks, only a few fields must be
initialized:

    tc_Node
        The task list node structure.  This includes the task's
        priority, its type, and its name (refer to the chapter
	"@{"Exec Lists and Queues" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-2}").

    tc_SPLower
        The lower memory bound of the task's stack.

    tc_SPUpper
        The upper memory bound of the task's stack.

    tc_SPReg
        The initial stack pointer.  Because task stacks grow downward in
        memory, this field is usually set to the same value as
        tc_SPUpper.

Zeroing all other unused fields will cause Exec to supply the appropriate
system default values.  Allocating the structure with the @{"MEMF_CLEAR" link ADCD_v1.2:Reference_Library/Libraries/Lib_20/20-1-1 37}
attribute is an easy way to be sure that this happens.

Once the structure has been initialized, it must be linked to Exec. This
is done with a call to @{"AddTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTask()} in which the following parameters are
specified:

    AddTask(struct Task *task, APTR initialPC, APTR finalPC )

The task argument is a pointer to your initialized @{"Task" link 21-1} structure.  Set
initialPC to the entry point of your task code.   This is the address of
the first instruction the new task will execute.

Set finalPC to the address of the finalization code for your task.  This
is a code section that will receive control if the initialPC routine ever
performs a return (RTS). This exists to prevent your task from being
launched into random memory upon an accidental return.  The finalPC
routine should usually perform various program-related clean-up duties and
should then remove the task.  If a zero is supplied for this parameter,
Exec will use its default finalization code (which simply calls the
@{"RemTask()" link 21-8 5} function).

Under Release 2, @{"AddTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTask()} returns the address of the newly added task or
NULL for failure.  Under 1.3 and older versions of the OS, no values are
returned.

@{" Task Creation With Amiga.lib " link 21-2-1}  @{" Task Stack " link 21-2-2}  @{" Task Priority " link 21-2-3}

@ENDNODE

@NODE 21-2-1 "21 / Task Creation / Task Creation With Amiga.lib"
A simpler method of creating a task is provided by the amiga.lib Exec
support function @{"CreateTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/CreateTask()}, which can be accessed if your code is
linked with amiga.lib.

    CreateTask(char *name, LONG priority, APTR initialPC, ULONG stacksize)

A task created with @{"CreateTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/CreateTask()} may be removed with the amiga.lib
@{"DeleteTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/DeleteTask()} function, or it may simply return when it is finished.
CreateTask() adds a @{"MemList" link ADCD_v1.2:Reference_Library/Libraries/Lib_20/20-2} to the @{"tc_MemEntry" link 21-1 28} of the task it creates,
describing all memory it has allocated for the task, including the task
stack and the @{"Task" link 21-1} structure itself.  This memory will be deallocated by
Exec when the task is either explicitly removed (@{"RemTask()" link 21-8 5} or
DeleteTask()) or when it exits to Exec's default task removal code
(RemTask()).

Note that a bug in the @{"CreateTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/CreateTask()} code caused a failed memory allocation
to go unnoticed in V33 and early versions of Release 2 amiga.lib.

If your development language is not linkable with amiga.lib, it may
provide an equivalent built-in function, or you can create your own based
on the @{"createtask.c" link ADCD_v1.2:Reference_Library/Libraries/lib_examples/createtask.c/MAIN} code in the Amiga ROM Kernel Reference Manual:
Includes and Autodocs.

Depending on the priority of a new task and the priorities of other tasks
in the system, the newly added task may begin execution immediately.

    Sharing Library Pointers
    ------------------------
    Although in most cases it is possible for a parent task to pass a
    library base to a child task so the child can use that library, for
    some libraries, this is not possible.  For this reason, the only
    library base sharable between tasks is Exec's library base.

Here is an example of simple task creation.  In this example there is no
coordination or communication between the main process and the simple task
it has created.  A more complex example might use named ports and messages
to coordinate the activities and shutdown of two tasks.  Because our task
is very simple and never calls any system functions which could cause it
to be signalled or awakened, we can safely remove the task at any time.

    Keep This In Mind.
    ------------------
    Because the simple task's code is a function in our program, we must
    stop the subtask before exiting.

    @{" simpletask.c " link ADCD_v1.2:Reference_Library/Libraries/Lib_Examples/simpletask.c/MAIN}

@ENDNODE

@NODE 21-2-2 "21 / Task Creation / Task Stack"
Every task requires a stack.  All task stacks are user mode stacks (in the
language of the 68000) and are addressed through the A7 CPU register.  All
normal code execution occurs on this task stack.  Special modes of
execution (processor traps and system interrupts for example) execute on a
single @{"supervisor mode" link 21-7-1} stack and do not directly affect task stacks.

Task stacks are normally used to store local variables, subroutine return
addresses, and saved register values.  Additionally, when a task loses the
processor, all of its current registers are preserved on this stack (with
the exception of the stack pointer itself, which must be saved in the task
structure).

The amount of stack used by a task can vary widely.  The theoretical
minimum stack size is 72 bytes, which is the number required to save 17
CPU registers and a single return address.  Of course, a stack of this
size would not give you adequate space to perform any subroutine calls
(because the return address occupies stack space).  On the other hand, a
stack size of 1K would suffice to call most system functions but would not
allow much in the way of local variable storage.  Processes that call DOS
library functions need an additional 1500 bytes of stack.

Because stack-bounds checking is not provided as a service of Exec, it is
important to provide enough space for your task stack.  Stack overflows
are always difficult to debug and may result not only in the erratic
failure of your task but also in the mysterious malfunction of other Amiga
subsystems.  Some compilers provide a stack-checking option.

    You Can't Always Check The Stack.
    ---------------------------------
    Such stack-checking options generally cannot be used if part of your
    code will be running on the system stack (interrupts, 680x0
    exceptions, handlers, servers), or on a different task's stack
    (libraries, devices, created tasks).

When choosing your stack size, do not cut it too close. Remember that any
recursive routines in your code may use varying amounts of stack, and that
future versions of system routines may use additional stack variables.  By
dynamically allocating buffers and arrays, most application programs can
be designed to function comfortably within the default process stack size
of 4000 bytes.

@ENDNODE

@NODE 21-2-3 "21 / Task Creation / Task Priority"
A task's priority indicates its importance relative to other tasks.
Higher-priority tasks receive the processor before lower-priority tasks
do.  Task priority is stored as a signed number ranging from -128 to +127.
Higher priorities are represented by more positive values; zero is
considered the neutral priority.  Normally, system tasks execute somewhere
in the range of +20 to -20, and most application tasks execute at
priority 0.

It is not wise to needlessly raise a task's priority.  Sometimes it may be
necessary to carefully select a priority so that the task can properly
interact with various system tasks.  The @{"SetTaskPri()" link 21-8 32} Exec function is
provided for this purpose.

@ENDNODE

@NODE 21-3 "21 Exec Tasks / Task Termination"
Task termination may occur as the result of a number of situations:

  * A program returning from its @{"initialPC" link 21-2 35} routine and dropping into its
    @{"finalPC" link 21-2 35} routine or the system default finalizer.

  * A task trap that is too serious for a recovery action. This includes
    traps like processor bus error, odd address access errors, etc.

  * A trap that is not handled by the task.  For example, the task might
    be terminated if your code happened to encounter a processor TRAP
    instruction and you did not provide a trap handling routine.

  * An explicit call to Exec @{"RemTask()" link 21-8 5} or amiga.lib @{"DeleteTask()" link 21-2-1 6}.

Task termination involves the deallocation of system resources and the
removal of the task structure from Exec.  The most important part of task
termination is the deallocation of system resources.  A task must return
all memory that it allocated for its private use, it must terminate any
outstanding I/O commands, and it must close access to any system libraries
or devices that it has opened.

It is wise to adopt a strategy for task clean-up responsibility.  You
should decide whether resource allocation and deallocation is the duty of
the creator task or the newly created task.  Often it is easier and safer
for the creator to handle the resource allocation and deallocation on
behalf of its offspring.  In such cases, before removing the child task,
you must make sure it is in a safe state such as @{"Wait" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2}(0L) and not still
using a resources or waiting for an event or signal that might still occur.

    NOTE:
    -----
    Certain resources, such as signals and created ports, must be
    allocated and deallocated by the same task that will wait on them.
    Also note that if your subtask code is part of your loaded program,
    you must not allow your program to exit before its subtasks have
    cleaned up their allocations, and have been either deleted or placed
    in a safe state such as @{"Wait" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2}(0L).

@ENDNODE

@NODE 21-4 "21 Exec Tasks / Task Exclusion"
From time to time the advanced system program may find it necessary to
access global system data structures.  Because these structures are shared
by the system and by other tasks that execute asynchronously to your task,
a task must prevent other tasks from using these structures while it is
reading from or writing to them.  This can be accomplished by preventing
the operating system from switching tasks by forbidding or disabling.  A
section of code that requires the use of either of these mechanisms to
lock out access by others is termed a critical section.  Use of these
methods is discouraged.  For arbitrating access to data between your
tasks, semaphores are a superior solution.  (See the "@{"Exec Semaphores" link ADCD_v1.2:Reference_Library/Libraries/Lib_25/MAIN}"
chapter)

@{" Forbidding Task Switching " link 21-4-1}  @{" Disabling Tasks " link 21-4-2}  @{" Task Semaphores " link 21-4-3}

@ENDNODE

@NODE 21-4-1 "21 / Task Exclusion / Forbidding Task Switching"
Forbidding is used when a task is accessing shared structures that might
also be accessed at the same time from another task.  It effectively
eliminates the possibility of simultaneous access by imposing
nonpreemptive task scheduling.  This has the net effect of disabling
multitasking for as long as your task remains in its running state. While
forbidden, your task will continue running until it performs a call to
@{"Wait()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2} or exits from the forbidden state.  Interrupts will occur normally,
but no new tasks will be dispatched, regardless of their priorities.

When a task running in the forbidden state calls the @{"Wait()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2} function,
directly or indirectly, it implies a temporary exit from its forbidden
state. Since almost all stdio, device I/O, and file I/O functions must
Wait() for I/O completion, performing such calls will cause your task to
Wait(), temporarily breaking the forbid.  While the task is waiting, the
system will perform normally.  When the task receives one of the signals
it is waiting for, it will again reenter the forbidden state. To become
forbidden, a task calls the @{"Forbid()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Forbid()} function.  To escape, the @{"Permit()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Permit()}
function is used.  The use of these functions may be nested with the
expected affects; you will not exit the forbidden mode until you call the
outermost Permit().

As an example, the Exec task list should only be accessed when in a
@{"Forbid()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Forbid()} state.  Accessing the list without forbidding could lead to
incorrect results or it could crash the entire system.  To access the task
list also requires the program to disable interrupts which is discussed in
the @{"next section" link 21-4-2}.

@ENDNODE

@NODE 21-4-2 "21 / Task Exclusion / Disabling Tasks"
Disabling is similar to forbidding, but it also prevents interrupts from
occurring during a critical section.  Disabling is required when a task
accesses structures that are shared by interrupt code.  It eliminates the
possibility of an interrupt accessing shared structures by preventing
interrupts from occurring.  Use of disabling is strongly discouraged.

To disable interrupts you can call the @{"Disable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Disable()} function.  To enable
interrupts again, use the @{"Enable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enable()} function.  Although assembler DISABLE
and ENABLE macros are provided, assembler programmers should use the
system functions rather than the macros for upwards compatibility, ease of
debugging, and smaller code size.

Like forbidden sections, disabled sections can be nested.  To restore
normal interrupt processing, an @{"Enable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enable()} call must be made for every
@{"Disable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Disable()}.  Also like forbidden sections, any direct or indirect call to
the @{"Wait()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2} function will enable interrupts until the task regains the
processor.

    WARNING:
    --------
    It is important to realize that there is a danger in using disabled
    sections.  Because the software on the Amiga depends heavily on its
    interrupts occurring in nearly real time, you cannot disable for more
    than a very brief instant. Disabling interrupts for more than 250
    microseconds can interfere with the normal operation of vital system
    functions, especially serial I/O.

    WARNING:
    --------
    Masking interrupts by changing the 68000 processor interrupt
    priority levels with the MOVE SR instruction can also be dangerous
    and is very strongly discouraged.  The disable- and enable-related
    functions control interrupts through the 4703 custom chip and not
    through the 68000 priority level.  In addition, the processor
    priority level can be altered only from @{"supervisor mode" link 21-7-1} (which means
    this process is much less efficient).

It is never necessary to both @{"Disable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Disable()} and @{"Forbid()" link 21-4-1 9}.  Because disabling
prevents interrupts, it also prevents preemptive task scheduling.  When
disable is used within an interrupt, it will have the effect of locking
out all higher level interrupts (lower level interrupts are automatically
disabled by the CPU).  Many Exec lists can only be accessed while
disabled.  Suppose you want to print the names of all system tasks.  You
would need to access both the @{"TaskReady" link ADCD_v1.2:Inc&AD2.1/includes/exec/execbase.h/MAIN 83} and @{"TaskWait" link ADCD_v1.2:Inc&AD2.1/includes/exec/execbase.h/MAIN 83} lists from within a
single disabled section.  In addition, you must avoid calling system
functions that would break a disable by an indirect call to @{"Wait()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2}
(@{"printf()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/printf()} for example).  In this example, the names are gathered into a
list while task switching is disabled. Then task switching is enabled and
the names are printed.

    @{" tasklist.c " link ADCD_v1.2:Reference_Library/Libraries/Lib_Examples/tasklist.c/MAIN}

@ENDNODE

@NODE 21-4-3 "21 / Task Exclusion / Task Semaphores"
Semaphores can be used for the purposes of mutual exclusion.  With this
method of locking, all tasks agree on a locking convention before
accessing shared data structures.  Tasks that do not require access are
not affected and will run normally, so this type of exclusion is
considered preferable to forbidding and disabling.  This form of exclusion
is explained in more detail in the "@{"Exec Semaphores" link ADCD_v1.2:Reference_Library/Libraries/Lib_25/25-1-1-3}" chapter.

@ENDNODE

@NODE 21-5 "21 Exec Tasks / Task Exceptions"
Exec can provide a task with its own task-local "interrupt" called an
exception.  When some exceptional event occurs, an Exec exception occurs
which stops a particular task from executing its normal code and forces it
to execute a special, task-specific exception handling routine.

If you are familiar with the 680x0, you may be used to using the term
"exceptions" in a different way.  The 680x0 has its own form of exception
that has nothing to do with an Exec exception.  These are discussed in
more detail in the "@{"Task Traps" link 21-6}" section of this chapter.  Do not confuse
Exec exceptions with 680x0 exceptions.

To set up an exception routine for a task requires setting values in the
task's control structure (the @{"Task" link 21-1} structure).  The @{"tc_ExceptCode" link 21-1 19} field
should point to the task's exception handling routine.  If this field is
zero, Exec will ignore all exceptions.  The @{"tc_ExceptData" link 21-1 19} field should
point to any data the exception routine needs.

Exec exceptions work using signals.  When a specific signal or signals
occur, Exec will stop a task and execute its exception routine.  Use the
Exec function @{"SetExcept()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SetExcept()} to tell Exec which of the task's signals should
trigger the exception.

When an exception occurs, Exec stops executing the tasks normal code and
jumps immediately into the exception routine, no matter what the task was
doing.  The exception routine operates in the same context the task's
normal code; it operates in the CPU's user mode and uses the task's stack.

Before entering the exception routine, Exec pushes the normal task code's
context onto the stack.  This includes the PC, SR, D0-D7, and A0-A6
registers.  Exec then puts certain parameters in the processor registers
for the exception routine to use.  D0 contains a signal mask indicating
which signal bit or bits caused the exception. Exec disables these signals
when the task enters its exception routine.  If more than one signal bit
is set (i.e. if two signals occurred simultaneously), it is up to the
exception routine to decide in what order to process the two different
signals.  A1 points to the related exception data (from @{"tc_ExceptData" link 21-1 19}),
and A6 contains the Exec library base. You can think of an exception as a
subtask outside of your normal task. Because task exception code executes
in user mode, however, the task stack must be large enough to supply the
extra space consumed during an exception.

While processing a given exception, Exec prevents that exception from
occurring recursively.  At exit from your exception-processing code, you
should make sure D0 contains the signal mask the exception routine
received in D0 because Exec looks here to see which signals it should
reactivate.  When the task executes the RTS instruction at the end of the
exception routine, the system restores the previous contents of all of the
task registers and resumes the task at the point where it was interrupted
by the exception signal.

    Exceptions Are Tricky.
    ----------------------
    Exceptions are difficult to use safely.  An exception can interrupt
    a task that is executing a critical section of code within a system
    function, or one that has locked a system resource such as the disk
    or blitter (note that even simple text output uses the blitter.)
    This possibility makes it dangerous to use most system functions
    within an exception unless you are sure that your interrupted task
    was performing only local, non-critical operations.

@ENDNODE

@NODE 21-6 "21 Exec Tasks / Task Traps"
Task traps are synchronous exceptions to the normal flow of program
control.  They are always generated as a direct result of an operation
performed by your program's code.  Whether they are accidental or
purposely generated, they will result in your program being forced into a
special condition in which it must immediately handle the trap.  Address
error, privilege violation, zero divide, and trap instructions all result
in task traps.  They may be generated directly by the 68000 processor
(Motorola calls them "exceptions") or simulated by software.

A task that incurs a trap has no choice but to respond immediately. The
task must have a module of code to handle the trap.  Your task may be
aborted if a trap occurs and no means of handling it has been provided.
Default trap handling code (@{"tc_TrapCode" link 21-1 19}) is provided by the OS.  You may
instead choose to do your own processing of traps.  The tc_TrapCode field
is the address of the handler that you have designed to process the trap.
The tc_TrapData field is the address of the data area for use by the trap
handler.

The system's default trap handling code generally displays a Software
Error Requester or Alert containing an exception number and the program
counter or task address.  Processor exceptions generally have numbers in
the range hex 00 to 2F.  The 68000 processor exceptions of particular
interest are as follows.


            Table 21-1: Traps (68000 Exception Vector Numbers)

    2      Bus error            access of nonexistent memory
    3      Address error        long/word access of odd address (68000)
    4      Illegal instruction  illegal opcode (other than Axxx or Fxxx)
    5      Zero divide          processor division by zero
    6      CHK instruction      register bounds error trap by CHK
    7      TRAPV instruction    overflow error trap by TRAPV
    8      Privilege violation  user execution of supervisor opcode
    9      Trace                @{"status register" link 21-7-2} TRACE bit trap
    10     Line 1010 emulator   execution of opcode beginning with $A
    11     Line 1111 emulator   execution of opcode beginning with $F
    32-47  Trap instructions    TRAP N instruction where N = 0 to 15


A system alert for a processor exception may set the high bit of the
longword exception number to indicate an unrecoverable error (for example
$80000005 for an unrecoverable processor exception #5).  System alerts
with more complex numbers are generally Amiga-specific software failures.
These are built from the definitions in the <exec/@{"alerts.h" link ADCD_v1.2:Inc&AD2.1/includes/exec/alerts.h/MAIN 90}> include file.

The actual stack frames generated for these traps are processor-dependent.
The 68010, 68020, and 68030 processors will generate a different type of
stack frame than the 68000.  If you plan on having your program handle its
own traps, you should not make assumptions about the format of the
supervisor stack frame.  Check the flags in the @{"AttnFlags" link ADCD_v1.2:Inc&AD2.1/includes/exec/execbase.h/MAIN 72} field of the
@{"ExecBase" link ADCD_v1.2:Inc&AD2.1/includes/exec/execbase.h/MAIN 35} structure for the type of processor in use and process the stack
frame accordingly.

@{" Trap Handlers " link 21-6-1}  @{" Trap Instructions " link 21-6-2}

@ENDNODE

@NODE 21-6-1 "21 / Task Traps / Trap Handlers"
For compatibility with the 68000, Exec performs trap handling in
@{"supervisor mode" link 21-7-1}.  This means that all task switching is disabled during
trap handling. At entry to the task's trap handler, the system stack
contains a processor-dependent trap frame as defined in the 68000/10/20/30
manuals.  A longword exception number is added to this frame.  That is,
when a handler gains control, the top of stack contains the exception
number and the trap frame immediately follows.

To return from trap processing, remove the exception number from the stack
(note that this is the supervisor stack, not the user stack) and then
perform a return from exception (RTE).

Because trap processing takes place in @{"supervisor mode" link 21-7-1}, with task
dispatching disabled, it is strongly urged that you keep trap processing
as short as possible or switch back to user mode from within your trap
handler.  If a trap handler already exists when you add your own trap
handler, it is smart to propagate any traps that you do not handle down to
the previous handler.  This can be done by saving the previous address
from @{"tc_TrapCode" link 21-1 19} and having your handler pass control to that address if
the trap which occurred is not one you wish to handle.

The following example installs a simple trap handler which intercepts
processor divide-by-zero traps, and passes on all other traps to the
previous default trap code.  The example has two code modules which are
linked together.  The trap handler code is in assembler.  The C module
installs the handler, demonstrates its effectiveness, then restores the
previous @{"tc_TrapCode" link 21-1 19}.

    @{" trap_c.c example " list Lib_Examples/trap_c.c/MAIN}

@ENDNODE

@NODE 21-6-2 "21 / Task Traps / Trap Instructions"
The TRAP instructions in the 68000 generate traps 32-47.  Because many
independent pieces of system code may desire to use these traps, the
@{"AllocTrap()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AllocTrap()} and @{"FreeTrap()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FreeTrap()} functions are provided.  These work in a
fashion similar to that used by @{"AllocSignal()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-1 5} and @{"FreeSignal()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-1 5}, mentioned
in the "@{"Exec Signals" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-1}" chapter.

Allocating a trap is simply a bookkeeping job within a task.  It does not
affect how the system calls the trap handler; it helps coordinate who owns
what traps.  Exec does nothing to determine whether or not a task is
prepared to handle a particular trap.  It simply calls your code. It is up
to your program to handle the trap.

To allocate any trap, you can use the following code:

    if (-1 == (trap = AllocTrap(-1)))
        printf("all trap instructions are in use\n");

Or you can select a specific trap using this code:

    if (-1 == (trap = AllocTrap(3)))
        printf("trap #3 is in use\n");

To free a trap, you use the @{"FreeTrap()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FreeTrap()} function passing it the trap number
to be freed.

@ENDNODE

@NODE 21-7 "21 Exec Tasks / Processor and Cache Control"
Exec provides a number of to control the processor mode and, if available,
the caches.  All these functions work independently of the specific M68000
family processor type.  This enables you to write code which correctly
controls the state of both the MC68000 and the MC68040.  Along with
processor mode and cache control, functions are provided to obtain
information about the condition code register (CCR) and status register
(SR).  No functions are provided to control a paged memory management unit
(PMMU) or floating point unit (FPU).


          Table 21-2: Processor and Cache Control Functions
   __________________________________________________________________
  |                                                                  |
  |     Function              Description                            |
  |==================================================================|
  |         @{"GetCC()" link 21-7-3}  Get processor condition codes.                  |
  |         @{"SetSR()" link 21-7-2}  Get/set processor status register.              |
  |    @{"SuperState()" link 21-7-1 21}  Set supervisor mode with user stack.            |
  |    @{"Supervisor()" link 21-7-1 10}  Execute a short supervisor mode function.       |
  |     @{"UserState()" link 21-7-1 21}  Return to user mode with user stack.            |
  |------------------------------------------------------------------|
  |   @{"CacheClearE()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheClearE()}  Flush CPU instruction and/or data caches (V37). |
  |   @{"CacheClearU()" link 21-7-6 45}  Flush CPU instruction and data caches (V37).    |
  |  @{"CacheControl()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheControl()}  Global cache control (V37).                     |
  |  @{"CachePostDMA()" link 21-7-5}  Perform actions prior to hardware DMA (V37).    |
  |   @{"CachePreDMA()" link 21-7-5}  Perform actions after hardware DMA (V37).       |
  |__________________________________________________________________|


@{" Supervisor Mode " link 21-7-1}  @{" Condition Code Register " link 21-7-3}  @{" DMA Cache Functions " link 21-7-5}
@{" Status Register " link 21-7-2}  @{" Cache Functions " link 21-7-4}          @{" The 68040 and CPU Caches " link 21-7-6}

@ENDNODE

@NODE 21-7-1 "21 / Processor and Cache Control / Supervisor Mode"
While in supervisor mode, you have complete access to all data and
registers, including those used for task scheduling and exceptions, and
can execute privileged instructions.  In application programs, normally
only task trap code is directly executed in supervisor mode, to be
compatible with the MC68000.  For normal applications, it should never be
necessary to switch to supervisor mode itself, only indirectly through
Exec function calls.  Remember that task switching is disabled while in
supervisor mode.  If it is absolutely needed to execute code in supervisor
mode, keep it as brief as possible.

Supervisor mode can only be entered when a 680x0 exception occurs (an
interrupt or trap).  The @{"Supervisor()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Supervisor()} function allows you to trap an
exception to a specified assembly function.  In this function your have
full access to all registers.  No registers are saved when your function
is invoked.  You are responsible for restoring the system to a sane state
when you are done. You must return to user mode with an RTE instruction.
You must not return to user mode by executing a privileged instruction
which clears the supervisor bit in the @{"status register" link 21-7-2}.  Refer to a manual
on the M68000 family of CPUs for information about supervisor mode and
available privileged instructions per processor type.

The MC68000 has two stacks, the user stack (USP) and supervisor stack
(SSP). As of the MC68020 there are two supervisor stacks, the interrupt
stack pointer (ISP) and the master stack pointer (MSP). The @{"SuperState()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SuperState()}
function allows you to enter supervisor mode with the USP used as SSP. The
function returns the SSP, which will be the MSP, if an MC68020 or greater
is used. Returning to user mode is done with the @{"UserState()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/UserState()} function.
This function takes the SSP as argument, which must be saved when
SuperState() is called.  Because of possible problems with stack size,
@{"Supervisor()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Supervisor()} is to be preferred over SuperState().

@ENDNODE

@NODE 21-7-2 "21 / Processor and Cache Control / Status Register"
The processor status register bits can be set or read with the @{"SetSR()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SetSR()}
function.  This function operates in @{"supervisor mode" link 21-7-1}, thus both the upper
and lower byte of the SR can be read or set.  Be very sure you know what
you are doing when you use this function to set bits in the SR and above
all never try to use this function to enter supervisor mode. Refer to the
M68000 Programmers Reference Manual by Motorola Inc. for information about
the definition of individual SR bits per processor type.

@ENDNODE

@NODE 21-7-3 "21 / Processor and Cache Control / Condition Code Register"
On the MC68000 a copy of the processor condition codes can be obtained
with the MOVE SR,<ea> instruction.  On MC68010 processors and up however,
the instruction MOVE CCR,<ea> must be used.  Using the specific MC68000
instruction on later processors will cause a 680x0 exception since it is a
privileged instruction on those processors.  The @{"GetCC()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/GetCC()} function provides
a processor independent way of obtaining a copy of the condition codes.
For all processors there are 5 bits which can indicate the result of an
integer or a system control instruction:

    X - extend    N - negative    Z - zero    V - overflow    C - carry

The X bit is used for multiprecision calculations. If used, it is copy of
the carry bit.  The other bits state the result of a processor operation.

@ENDNODE

@NODE 21-7-4 "21 / Processor and Cache Control / Cache Functions"
As of the MC68020 all processors have an instruction cache, 256 bytes on
the MC68020 and MC68030 and 4 KBytes on a @{"MC68040" link 21-7-6}.  The MC68030 and
MC68040 have data caches as well, 256 bytes and 4 KBytes respectively. All
the processors load instructions ahead of the program counter (PC), albeit
it that the MC68000 and MC68010 only prefetch one and two words
respectively. This means the CPU loads instructions ahead of the current
program counter.  For this reason self-modifying code is strongly
discouraged.  If your code modifies or decrypts itself just ahead of the
program counter, the pre-fetched instructions may not match the modified
instructions.  If self-modifying code must be used, flushing the cache is
the safest way to prevent this.

@ENDNODE

@NODE 21-7-5 "21 / Processor and Cache Control / DMA Cache Functions"
The @{"CachePreDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePreDMA()} and @{"CachePostDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePostDMA()} functions allow you to flush the data
cache before and after Direct Memory Access. Typically only DMA device
drivers benefit from this. These functions take the processor type,
possible MMU and cache mode into account. When no cache is available they
end up doing nothing.  These functions can be replaced with ones suitable
for different cache hardware.  Refer to the ROM Kernel Reference Manual:
Includes and Autodocs for implementation specifics.

Since DMA device drivers read and write directly to memory, they are
effected by the @{"CopyBack" link 21-7-6 10} feature of the @{"MC68040" link 21-7-6} (explained below).  Using
DMA with CopyBack mode requires a cache flush.  If a DMA device needs to
read RAM via DMA, it must make sure that the data in the caches has been
written to memory first, by calling @{"CachePreDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePreDMA()}.  In case of a write to
memory, the DMA device should first clear the caches with CachePreDMA(),
write the data and flush the caches again with @{"CachePostDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePostDMA()}.

@ENDNODE

@NODE 21-7-6 "21 / Processor and Cache Control / The 68040 and CPU Caches"
The 68040 is a much more powerful CPU than its predecessors.  It has 4K of
cache memory for instructions and another 4K cache for data.  The reason
for these two separate caches is so that the CPU core can access data and
CPU instructions at the same time.

Although the 68040 provides greater performance it also brings with it
greater compatibility problems.  Just the fact that the caches are so much
larger than Motorola's 68030 CPU can cause problems.  However, this is not
its biggest obstacle.

The 68040 data cache has a mode that can make the system run much faster
in most cases.  It is called CopyBack mode.  When a program writes data to
memory in this mode, the data goes into the cache but not into the
physical RAM.  That means that if a program or a piece of hardware were to
read that RAM without going through the data cache on the 68040, it will
read old data. CopyBack mode effects two areas of the Amiga: DMA devices
and the CPU's instruction reading.

CopyBack mode effects DMA devices because they read and write data
directly to memory.  Using DMA with CopyBack mode requires a cache flush.
If a DMA device needs to read RAM via DMA, it must first make sure that
data in the caches has been written to memory.  It can do this by calling
the Exec function @{"CachePreDMA()" link 21-7-5}. If a DMA device is about to write to
memory, it should call CachePreDMA() before the write, do the DMA write,
and then call @{"CachePostDMA()" link 21-7-5},  which makes sure that the CPU uses the data
just written to memory.

An added advantage of using the @{"CachePreDMA()" link 21-7-5} and @{"CachePostDMA()" link 21-7-5} functions
is that they give the OS the chance to tell the DMA device that the
physical addresses and memory sizes are not the same.  This will make it
possible in the future to add features such as virtual memory.  See the
Autodocs for more information on these calls.

The other major compatibility problem with the 68040's CopyBack mode is
with fetching CPU instructions.  CPU instructions have to be loaded into
memory so the CPU can copy them into its instruction cache.  Normally,
instructions that will be executed are written to memory by the CPU (i.e.,
loading a program from disk).  In CopyBack mode, anything the CPU writes
to memory, including CPU instructions, doesn't actually go into memory, it
goes into the data cache.  If instructions are not flushed out of the data
cache to RAM, the 68040 will not be able to find them when it tries to
copy them into the instruction cache for execution.  It will instead find
and attempt to execute whatever garbage data happened to be left at that
location in RAM.

To remedy this, any program that writes instructions to memory must flush
the data cache after writing.  The V37 Exec function @{"CacheClearU()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheClearU()} takes
care of this.  Release 2 of the Amiga OS correctly flushes the caches as
needed after it does the @{"LoadSeg()" link ADCD_v1.2:Inc&AD2.1/autodocs/dos/LoadSeg()} of a program (LoadSeg() loads Amiga
executable programs into memory from disk).  Applications need to do the
same if they write code to memory.  It can do that by calling
CacheClearU() before the call to @{"CreateProc()" link ADCD_v1.2:Inc&AD2.1/autodocs/dos/CreateProc()}.  In C that would be:

    extern struct ExecBase *SysBase;
    . . .

    /* If we are in 2.0, call CacheClearU() before CreateProc() */
    if (SysBase->LibNode.lib_Version >= 37) CacheClearU();

    /* Now do the CreateProc() call... */
    proc=CreateProc(... /* whatever your call is like */ ...);
    . . .

For those of you programming in assembly language:

    ***********************************************************************
    * Check to see if we are running in V37 ROM or better.  If so, we want
    * to call CacheClearU() to make sure we are safe on future hardware
    * such as the 68040.  This section of code assumes that a6 points at
    * ExecBase.  a0/a1/d0/d1 are trashed in CacheClearU()
    *
            cmpi.w  #37,LIB_VERSION(a6)    ; Check if exec is >= V37
            bcs.s   TooOld                 ; If less than V37, too old...
            jsr     _LVOCacheClearU(a6)    ; Clear the cache...
    TooOld:                                ; Exit gracefully.
    ***********************************************************************

Note that @{"CreateProc()" link ADCD_v1.2:Inc&AD2.1/autodocs/dos/CreateProc()} is not the only routine where CopyBack mode could
be a problem.  Any program code copied into memory for execution that is
not done via @{"LoadSeg()" link ADCD_v1.2:Inc&AD2.1/autodocs/dos/LoadSeg()} will need to call @{"CacheClearU()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheClearU()}.  Many input device
handlers have been known to allocate and copy the handler code into memory
and then exit back to the system.  These programs also need to have this
call in them.  The above code will work under older versions of the OS,
and will do the correct operations in Release 2 (and beyond).

@ENDNODE

@NODE 21-8 "21 Exec Tasks / Function Reference"
The following chart gives a brief description of the Exec functions that
control tasks.  See the Amiga ROM Kernel Reference Manual: Includes and
Autodocs for details about each call.


     Table 21-3: Exec Task, Processor and Cache Control Functions
  ___________________________________________________________________
 |                                                                   |
 |      Exec Task                                                    |
 |      Function          Description                                |
 |===================================================================|
 |       @{"AddTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTask()}  Add a task to the system.                        |
 |     @{"AllocTrap()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AllocTrap()}  Allocate a processor trap vector.                |
 |       @{"Disable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Disable()}  Disable interrupt processing.                    |
 |        @{"Enable()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enable()}  Enable interrupt processing.                     |
 |      @{"FindTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FindTask()}  Find a specific task.                            |
 |        @{"Forbid()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Forbid()}  Forbid task rescheduling.                        |
 |      @{"FreeTrap()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FreeTrap()}  Release a process trap.                          |
 |        @{"Permit()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Permit()}  Permit task rescheduling.                        |
 |    @{"SetTaskPri()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SetTaskPri()}  Set the priority of a task.                      |
 |       @{"RemTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemTask()}  Remove a task from the system.                   |
 |-------------------------------------------------------------------|
 |   @{"CacheClearE()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheClearE()}  Flush CPU instruction and/or data caches (V37).  |
 |   @{"CacheClearU()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheClearU()}  Flush CPU instruction and data caches (V37).     |
 |  @{"CacheControl()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CacheControl()}  Global cache control (V37).                      |
 |  @{"CachePostDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePostDMA()}  Perform actions prior to hardware DMA (V37).     |
 |   @{"CachePreDMA()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/CachePreDMA()}  Perform actions after hardware DMA (V37).        |
 |         @{"GetCC()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/GetCC()}  Get processor @{"condition codes" link 21-7-3}.                   |
 |         @{"SetSR()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SetSR()}  Get/set processor @{"status register" link 21-7-2}.               |
 |    @{"SuperState()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/SuperState()}  Set @{"supervisor mode" link 21-7-1} with user stack.             |
 |    @{"Supervisor()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Supervisor()}  Execute a short supervisor mode function.        |
 |     @{"UserState()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/UserState()}  Return to user mode with user stack.             |
 |-------------------------------------------------------------------|
 |    @{"CreateTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/CreateTask()}  Amiga.lib function to setup and add a new task.  |
 |    @{"DeleteTask()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/DeleteTask()}  Amiga.lib function to delete a task created with |
 |                  CreateTask().                                    |
 |___________________________________________________________________|

@ENDNODE
