2 Copyright © 1995-2005, The AROS Development Team. All rights reserved.
8 #include <exec/execbase.h>
9 #include <exec/memory.h>
10 #include <utility/tagitem.h>
11 #include <aros/libcall.h>
12 #include <aros/config.h>
13 #include <proto/exec.h>
15 #include "exec_util.h"
17 #include "exec_debug.h"
18 #ifndef DEBUG_NewAddTask
19 # define DEBUG_NewAddTask 0
25 #include <aros/debug.h>
27 /*****************************************************************************
31 AROS_LH4(APTR
, NewAddTask
,
34 AROS_LHA(struct Task
*, task
, A1
),
35 AROS_LHA(APTR
, initialPC
, A2
),
36 AROS_LHA(APTR
, finalPC
, A3
),
37 AROS_LHA(struct TagItem
*, tagList
, A4
),
40 struct ExecBase
*, SysBase
, 152, Exec
)
43 Add a new task to the system. If the new task has the highest
44 priority of all and task switches are allowed it will be started
47 You must initialise certain fields, and allocate a stack before
48 calling this function. The fields that must be initialised are:
49 tc_SPLower, tc_SPUpper, tc_SPReg, and the tc_Node structure.
51 If any other fields are initialised to zero, then they will be
52 filled in with the system defaults.
54 The value of tc_SPReg will be used as the location for the stack
55 pointer. You can place any arguments you wish to pass to the Task
56 onto the stack before calling AddTask(). However note that you may
57 need to consider the stack direction on your processor.
59 Memory can be added to the tc_MemEntry list and will be freed when
60 the task dies. The new task's registers are set to 0.
63 task - Pointer to task structure.
64 initialPC - Entry point for the new task.
65 finalPC - Routine that is called if the initialPC() function
66 returns. A NULL pointer installs the default finalizer.
69 The address of the new task or NULL if the operation failed.
82 ******************************************************************************/
86 D(bug("Call NewAddTask (%08lx (\"%s\"), %08lx, %08lx)\n"
88 , task
->tc_Node
.ln_Name
92 ASSERT_VALID_PTR(task
);
94 /* Set node type to NT_TASK if not set to something else. */
95 if(task
->tc_Node
.ln_Type
== 0)
96 task
->tc_Node
.ln_Type
= NT_TASK
;
98 /* Sigh - you should provide a name for your task. */
99 if(task
->tc_Node
.ln_Name
== NULL
)
100 task
->tc_Node
.ln_Name
= "unknown task";
102 /* This is moved into SysBase at the tasks's startup */
103 task
->tc_IDNestCnt
=-1;
104 task
->tc_TDNestCnt
=-1;
106 task
->tc_State
= TS_ADDED
;
109 task
->tc_SigWait
= 0;
110 task
->tc_SigRecvd
= 0;
111 task
->tc_SigExcept
= 0;
113 /* Signals default to all system signals allocated. */
114 if(task
->tc_SigAlloc
== 0)
115 task
->tc_SigAlloc
= SysBase
->TaskSigAlloc
;
118 * tc_SigWait, tc_SigRecvd, tc_SigExcept are default 0
119 * tc_ExceptCode, tc_ExceptData default to NULL.
122 /* Currently only used for segmentation violation */
123 if(task
->tc_TrapCode
== NULL
)
124 task
->tc_TrapCode
= SysBase
->TaskTrapCode
;
126 if (task
->tc_ExceptCode
== NULL
)
127 task
->tc_ExceptCode
=SysBase
->TaskExceptCode
;
130 If you can't to store the registers on the signal stack, you
133 task
->tc_Flags
|= TF_ETASK
;
135 /* Allocate the ETask structure if requested */
136 if (task
->tc_Flags
& TF_ETASK
)
139 * We don't add this to the task memory, it isn't free'd by
140 * RemTask(), rather by somebody else calling ChildFree().
141 * Alternatively, an orphaned task will free its own ETask.
143 task
->tc_UnionETask
.tc_ETask
= AllocVec
145 sizeof (struct IntETask
),
149 if (!task
->tc_UnionETask
.tc_ETask
)
152 InitETask(task
, task
->tc_UnionETask
.tc_ETask
);
156 task
->tc_UnionETask
.tc_ETrap
.tc_ETrapAlloc
= SysBase
->TaskTrapAlloc
;
157 task
->tc_UnionETask
.tc_ETrap
.tc_ETrapAble
= 0;
160 /* Get new stackpointer. Note, the doc says this MUST be initialised. */
161 if (task
->tc_SPReg
==NULL
)
162 #if AROS_STACK_GROWS_DOWNWARDS
163 task
->tc_SPReg
= (UBYTE
*)(task
->tc_SPUpper
) - SP_OFFSET
;
165 task
->tc_SPReg
= (UBYTE
*)(task
->tc_SPLower
) - SP_OFFSET
;
170 UBYTE
*startfill
, *endfill
;
172 #if AROS_STACK_GROWS_DOWNWARDS
173 startfill
= (UBYTE
*)task
->tc_SPLower
;
174 endfill
= ((UBYTE
*)task
->tc_SPReg
) - 16;
176 startfill
= ((UBYTE
*)task
->tc_SPReg
) + 16;
177 endfill
= ((UBYTE
*)task
->tc_SPUpper
) - 1; /* FIXME: -1 correct ?? */
180 while(startfill
<= endfill
)
188 /* Default finalizer? */
190 finalPC
= SysBase
->TaskExitCode
;
192 /* Init new context. */
193 if (!PrepareContext (task
, initialPC
, finalPC
, tagList
))
195 FreeVec(task
->tc_UnionETask
.tc_ETask
);
199 /* Set the task flags for switch and launch. */
201 task
->tc_Flags
|=TF_SWITCH
;
204 task
->tc_Flags
|=TF_LAUNCH
;
206 /* tc_MemEntry _must_ already be set. */
209 Protect the task lists. This must be done with Disable() because
210 of Signal() which is usable from interrupts and may change those
215 /* Add the new task to the ready list. */
216 task
->tc_State
=TS_READY
;
217 Enqueue(&SysBase
->TaskReady
,&task
->tc_Node
);
220 Determine if a task switch is necessary. (If the new task has a
221 higher priority than the current one and the current one
222 is still active.) If the current task isn't of type TS_RUN it
225 if(task
->tc_Node
.ln_Pri
>SysBase
->ThisTask
->tc_Node
.ln_Pri
&&
226 SysBase
->ThisTask
->tc_State
==TS_RUN
)
228 /* Are taskswitches allowed? (Don't count own Disable() here) */
229 if(SysBase
->TDNestCnt
>=0||SysBase
->IDNestCnt
>0) {
230 /* No. Store it for later. */
231 SysBase
->AttnResched
|=0x80;
233 /* Switches are allowed. Move the current task away. */
234 SysBase
->ThisTask
->tc_State
=TS_READY
;
235 Enqueue(&SysBase
->TaskReady
,&SysBase
->ThisTask
->tc_Node
);
237 /* And force a rescedule. */
244 ReturnPtr ("NewAddTask", struct Task
*, task
);
248 /* Default finaliser. */
249 void Exec_TaskFinaliser(void)
251 /* Get rid of current task. */
252 RemTask(SysBase
->ThisTask
);