3 #include <aros/system.h>
11 #include <exec/lists.h>
12 #include <exec/execbase.h>
13 #include <hardware/intbits.h>
16 #include "kernel_intern.h"
17 #include "host_debug.h"
19 /* We have to redefine these flags here because including exec_intern.h causes conflicts
20 between dos.h and WinAPI headers. This needs to be fixed - Pavel Fedin <sonic_amiga@rambler.ru */
21 #define SFB_SoftInt 5 /* There is a software interrupt */
22 #define SFF_SoftInt (1L<<5)
24 #define ARB_AttnSwitch 7 /* Delayed Switch() pending */
25 #define ARF_AttnSwitch (1L<<7)
26 #define ARB_AttnDispatch 15 /* Delayed Dispatch() pending */
27 #define ARF_AttnDispatch (1L<<15)
29 /* We also have to define needed exec functions here because proto/exec.h also conflicts with
31 #define Exception() AROS_LC0NR(void, Exception, struct ExecBase *, SysBase, 11, Exec)
32 #define Enqueue(arg1, arg2) AROS_LC2NR(void, Enqueue, \
33 AROS_LCA(struct List *,(arg1),A0), \
34 AROS_LCA(struct Node *,(arg2),A1), \
35 struct ExecBase *, SysBase, 45, Exec)
38 * Be careful with this, actually enabling this causes AROS to abort on first exception
39 * because of OutputDebugString() calls. Looks like WinAPI functions love to perform stack
40 * check and silently abort if they think something is wrong.
44 #define DSLEEP(x) D(x)
46 #define RETURN_FROM_INTERRUPT() do { \
47 if (SysBase->IDNestCnt < 0) \
53 * Task dispatcher. Basically it may be the same one no matter what scheduling algorithm is used
55 void core_Dispatch(void)
57 struct ExecBase
*SysBase
= *SysBasePtr
;
60 D(bug("[kernel:scheduler] in core_Dispatch()\n"));
65 * Is the list of ready tasks empty? Well, increment the idle switch cound and stop the main thread.
67 if (IsListEmpty(&SysBase
->TaskReady
))
69 if (sleep_state
!= ss_RUNNING
) {
71 SysBase
->AttnResched
|= ARF_AttnSwitch
;
73 DSLEEP(bug("[kernel:scheduler] TaskReady list empty. Sleeping for a while...\n"));
75 /* We are entering sleep mode */
76 sleep_state
= ss_SLEEP_PENDING
;
79 RETURN_FROM_INTERRUPT();
82 sleep_state
= ss_RUNNING
;
85 /* Get the first task from the TaskReady list, and populate it's settings through Sysbase */
86 task
= (struct Task
*)REMHEAD(&SysBase
->TaskReady
);
87 SysBase
->ThisTask
= task
;
88 SysBase
->Elapsed
= SysBase
->Quantum
;
89 SysBase
->SysFlags
&= ~0x2000;
90 task
->tc_State
= TS_RUN
;
91 SysBase
->IDNestCnt
= task
->tc_IDNestCnt
;
93 DS(bug("[kernel:scheduler] New task = %p (%s)\n", task
, task
->tc_Node
.ln_Name
));
95 /* Handle tasks's flags */
96 if (task
->tc_Flags
& TF_EXCEPT
)
99 if (task
->tc_Flags
& TF_LAUNCH
)
101 task
->tc_Launch(SysBase
);
104 /* Leave interrupt and jump to the new task */
105 RETURN_FROM_INTERRUPT();
108 void core_Switch(void)
110 struct ExecBase
*SysBase
= *SysBasePtr
;
113 D(bug("[kernel:scheduler] in core_Switch()\n"));
115 /* disable interrupts */
118 task
= SysBase
->ThisTask
;
120 DS(bug("[kernel:scheduler] switching out task 0x%08lx %d %s\n", task
, task
->tc_Node
.ln_Pri
, task
->tc_Node
.ln_Name
));
122 /* store IDNestCnt into tasks's structure */
123 task
->tc_IDNestCnt
= SysBase
->IDNestCnt
;
124 task
->tc_SPReg
= (APTR
) get_stack_pointer((void *) GetIntETask(task
)->iet_Context
);
126 /* And enable interrupts */
127 SysBase
->IDNestCnt
= -1;
130 /* TF_SWITCH flag set? Call the switch routine */
131 if (task
->tc_Flags
& TF_SWITCH
)
133 task
->tc_Switch(SysBase
);
141 * Schedule the currently running task away. Put it into the TaskReady list
142 * in some smart way. This function is subject of change and it will be probably replaced
143 * by some plugin system in the future
145 void core_Schedule(void)
147 struct ExecBase
*SysBase
= *SysBasePtr
;
150 D(bug("[kernel:scheduler] in core_Schedule()\n"));
152 /* disable interrupts */
155 task
= SysBase
->ThisTask
;
157 /* Clear the pending switch flag. */
158 SysBase
->AttnResched
&= ~ARF_AttnSwitch
;
160 /* If task has pending exception, reschedule it so that the dispatcher may handle the exception */
161 if (!(task
->tc_Flags
& TF_EXCEPT
))
163 /* Is the TaskReady empty? If yes, then the running task is the only one. Let it work */
164 if (IsListEmpty(&SysBase
->TaskReady
))
165 RETURN_FROM_INTERRUPT();
167 /* Does the TaskReady list contains tasks with priority equal or lower than current task?
168 * If so, then check further... */
169 if (((struct Task
*)GetHead(&SysBase
->TaskReady
))->tc_Node
.ln_Pri
<= task
->tc_Node
.ln_Pri
)
171 /* If the running task did not used it's whole quantum yet, let it work */
172 if (!(SysBase
->SysFlags
& 0x2000))
173 RETURN_FROM_INTERRUPT();
178 * If we got here, then the rescheduling is necessary.
179 * Put the task into the TaskReady list.
181 task
->tc_State
= TS_READY
;
182 Enqueue(&SysBase
->TaskReady
, (struct Node
*)task
);
184 /* Select new task to run */
190 * Leave the interrupt. This function receives the register frame used to leave the supervisor
191 * mode. It reschedules the task if it was asked for.
193 void core_ExitInterrupt(void)
195 struct ExecBase
*SysBase
= *SysBasePtr
;
198 D(bug("[kernel:scheduler] in core_ExitInterrupt()\n"));
203 bug(" SysBase 0x%08x\n", SysBase
);
205 bug(" current task: 0x%08x %d %s\n", SysBase
->ThisTask
, SysBase
->ThisTask
->tc_Node
.ln_Pri
, SysBase
->ThisTask
->tc_Node
.ln_Name
);
207 bug(" ready tasks:\n");
208 for (task
= (struct Task
*) SysBase
->TaskReady
.lh_Head
; task
->tc_Node
.ln_Succ
!= NULL
; task
= (struct Task
*) task
->tc_Node
.ln_Succ
)
209 bug(" 0x%08x %d %s\n", task
, task
->tc_Node
.ln_Pri
, task
->tc_Node
.ln_Name
);
211 bug(" waiting tasks:\n");
212 for (task
= (struct Task
*) SysBase
->TaskWait
.lh_Head
; task
->tc_Node
.ln_Succ
!= NULL
; task
= (struct Task
*) task
->tc_Node
.ln_Succ
)
213 bug(" 0x%08x %d %s\n", task
, task
->tc_Node
.ln_Pri
, task
->tc_Node
.ln_Name
);
218 /* Soft interrupt requested? It's high time to do it */
219 if (SysBase
->SysFlags
& SFF_SoftInt
) {
220 DS(bug("[kernel:scheduler] Causing SoftInt\n"));
224 if (sleep_state
!= ss_RUNNING
) {
229 /* If task switching is disabled, leave immediatelly */
230 TDNestCnt
= SysBase
->TDNestCnt
; /* BYTE is unsigned in Windows so we can't use SysBase->TDNestCnt directly */
231 DS(bug("[kernel:scheduler] TDNestCnt is %d\n", TDNestCnt
));
235 * Do not disturb task if it's not necessary.
236 * Reschedule only if switch pending flag is set. Exit otherwise.
238 if (SysBase
->AttnResched
& ARF_AttnSwitch
)
240 DS(bug("[kernel:scheduler] Rescheduling\n"));
244 RETURN_FROM_INTERRUPT();
247 DS(else printf("[kernel:scheduler] SysBase is NULL\n");)
250 void core_Cause(struct ExecBase
*SysBase
)
252 struct IntVector
*iv
= &SysBase
->IntVects
[INTB_SOFTINT
];
254 D(bug("[kernel:scheduler] in core_Cause()\n"));
256 /* If the SoftInt vector in SysBase is set, call it. It will do the rest for us */
259 iv
->iv_Code(0, 0, 0, iv
->iv_Code
, SysBase
);