Alchemy
Loading...
Searching...
No Matches

scheduler.c

Go to the documentation of this file.
1/// @file
2/// The task scheduler: per-frame and VBlank routines in order.
3/// Part of runtime.c, the runtime object, compiled only through it.
4
6
7#ifndef UBFIX
8typedef s32 (*KeyCallbackFn)(void);
9#endif
10
11// The task scheduler: up to 20 routines run each frame by WaitFrames or each
12// VBlank, kept sorted by their order value (see SCHEDULER_KEY_MAIN_LOOP).
13
14/// Empties the task table: every slot gets no routine, order 0xffff (sorted
15/// last) and no flags. Clears gSchedulerReady while it works and sets it at
16/// the end, so neither pass runs a half-cleared table. Called by
17/// System_Initialize and by the battle, Lucky Dice and other modes that start
18/// from a clean table.
21 s32 remaining;
22
25 remaining = SCHEDULER_TBS_TASK_COUNT - 1;
26 do {
27 task->callback = 0;
28 task->state = 0xffff;
29 task->mask = 0;
30 task++;
31 remaining--;
32 } while (remaining >= 0);
34}
35
36/// Copies `byteCount` bytes a word at a time. Nothing in ☀️ calls it.
37UNUSED void Scheduler_CopyWords(u32 *destination, u32 *source, u32 byteCount) {
38 u32 index;
39 byteCount >>= 2;
40 for (index = 0; index < byteCount; index++)
41 *destination++ = *source++;
42}
43
44/// Bubble-sorts the task table by order, highest first, so each pass runs its
45/// tasks from the highest order down; removed tasks (order 0x7fff) rise to the
46/// front. Called by Scheduler_AddOrUpdateCallback with interrupts disabled.
48 /* FAKEMATCH: the initializer repeats the first pass's assignment, so the
49 * table loads before the loop and at the top of every pass; without it, 28
50 * instructions differ in tbs-en. */
51 struct SchedulerTask saved;
53 s32 pass;
54 s32 index;
55
56 // BUG: the last pass, which would settle the first two tasks, never runs,
57 // so the tasks in slots 0 and 1 can stay out of order
58#ifdef BUGFIX
59 for (pass = SCHEDULER_TBS_TASK_COUNT - 1; pass > 0; pass--) {
60#else
61 for (pass = SCHEDULER_TBS_TASK_COUNT - 1; pass > 1; pass--) {
62#endif
64 for (index = 0; index < pass; index++) {
65 if ((s16)task[1].state > (s16)task->state) {
66 memcpy(&saved, task, sizeof(saved));
67 memcpy(task, task + 1, sizeof(saved));
68 memcpy(task + 1, &saved, sizeof(saved));
69 }
70 task++;
71 }
72 }
73}
74
75/// Returns the table index of the task running `callback`, or -1. Nothing in
76/// ☀️ calls it.
78 s32 result;
79 struct SchedulerTask *task;
80 u32 savedInterruptMaster;
81 s32 i;
82
83 result = -1;
85 IRQ_LOCK(savedInterruptMaster);
86 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
87 if (task->callback == (u32)callback) {
88 result = i;
89 break;
90 }
91 }
92 IRQ_UNLOCK(savedInterruptMaster);
93 return result;
94}
95
96/// Schedules `callback` with `order`, or gives a task already running it the
97/// new order, and re-sorts the table. Writes the task's routine, order and
98/// flags (cleared for a new task) with interrupts disabled. Returns the task's
99/// index, or -1 when all 20 slots are taken. Called throughout the game to
100/// start per-frame and VBlank work; `order` is SCHEDULER_KEY_MAIN_LOOP or
101/// SCHEDULER_KEY_VBLANK's high byte with a priority in the low byte.
103 u32 savedInterruptState;
104 s32 index;
105 struct SchedulerTask *task;
106 s32 i;
107
108 index = -1;
109 task = gSchedulerTaskTable;
110 (void)gSchedulerStatus;
111 IRQ_LOCK(savedInterruptState);
112 // Update an existing callback before looking for an unused task slot.
113 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
114 if (task->callback == (u32)callback) {
115 task->state = order;
116 index = i;
117 break;
118 }
119 }
120 task = gSchedulerTaskTable;
121 if (index == -1) {
122 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
123 if (task->callback == 0) {
124 task->callback = (u32)callback;
125 task->state = order;
126 task->mask = 0;
127 index = i;
128 break;
129 }
130 }
131 }
133 IRQ_UNLOCK(savedInterruptState);
134 return index;
135}
136
137/// An empty routine; nothing in ☀️ calls it.
139
140/// An empty routine; nothing in ☀️ calls it.
142
143/// Unschedules the task running `callback`: clears its routine and gives it
144/// order 0x7fff, which no pass matches. Returns its old index, or -1. Called
145/// throughout the game when the work a task did is finished.
147 struct SchedulerTask *task;
148 u32 savedInterruptMaster;
149 s32 result;
150 s32 i;
151
152 result = -1;
153 task = gSchedulerTaskTable;
154 IRQ_LOCK(savedInterruptMaster);
155 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
156 if (task->callback == (u32)callback) {
157 task->callback = 0;
158 task->state = 0x7fff;
159 result = i;
160 break;
161 }
162 }
163 IRQ_UNLOCK(savedInterruptMaster);
164 return result;
165}
166
167/// Suspends the task running `callback`, or every task when `callback` is
168/// null, by setting bit 0 of its order's high byte, which then matches neither
169/// pass key. Returns the last index changed, or -1. Called by the map
170/// animation and battle code, through the far-call table, to pause tasks
171/// Scheduler_ResumeTasks restarts later.
173 struct SchedulerTask *task;
174 u32 savedInterruptMaster;
175 s32 result;
176 s32 i;
177
178 result = -1;
179 task = gSchedulerTaskTable;
180 IRQ_LOCK(savedInterruptMaster);
181 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
182 if (callback != 0) {
183 if (task->callback != (u32)callback)
184 continue;
185 }
186 TASK_STATE_HIGH(task) |= 1;
187 result = i;
188 }
189 IRQ_UNLOCK(savedInterruptMaster);
190 return result;
191}
192
193/// Suspends every task whose routine is in an overlay (EWRAM, 0x02xxxxxx)
194/// unless its flag bit 0 asks to keep it running, as Scheduler_SuspendTasks
195/// does. Called by the menus' common code while a menu is open, and by the
196/// world map's visibility transition. Returns the last index changed, or -1.
198 struct SchedulerTask *task;
199 u32 savedInterruptMaster;
200 s32 result;
201 s32 i;
202
203 result = -1;
204 task = gSchedulerTaskTable;
205 IRQ_LOCK(savedInterruptMaster);
206 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
207 if ((task->callback >> 24) == 2 && (task->mask & 1) == 0) {
208 TASK_STATE_HIGH(task) |= 1;
209 result = i;
210 }
211 }
212 IRQ_UNLOCK(savedInterruptMaster);
213 return result;
214}
215
216/// Stores `mask` as the flags of the task running `callback`; bit 0 keeps it
217/// running while the overlay tasks are suspended. Called by the link lobby
218/// and Colosso scenes for the tasks that must keep running under a menu.
219/// Returns its index, or -1.
221 struct SchedulerTask *task;
222 u32 savedInterruptMaster;
223 s32 result;
224 s32 i;
225
226 result = -1;
227 task = gSchedulerTaskTable;
228 IRQ_LOCK(savedInterruptMaster);
229 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
230 if (task->callback == (u32)callback) {
231 task->mask = mask;
232 result = i;
233 break;
234 }
235 }
236 IRQ_UNLOCK(savedInterruptMaster);
237 return result;
238}
239
240/// Resumes the task running `callback`, or every task when `callback` is
241/// null, by clearing bit 0 of its order's high byte. Called by the map
242/// animation and field object code. Returns the last index changed, or -1.
244 struct SchedulerTask *task;
245 u32 savedInterruptMaster;
246 s32 result;
247 s32 i;
248
249 result = -1;
250 task = gSchedulerTaskTable;
251 IRQ_LOCK(savedInterruptMaster);
252 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
253 if (callback == 0 || task->callback == (u32)callback) {
254 TASK_STATE_HIGH(task) &= ~1;
255 result = i;
256 }
257 }
258 IRQ_UNLOCK(savedInterruptMaster);
259 return result;
260}
261
262/// Resumes every task whose routine is in an overlay, flagged or not. Called
263/// by the menus' common code when a menu closes, and by the world map's
264/// visibility transition. Returns the last index changed, or -1.
266 struct SchedulerTask *task;
267 u32 savedInterruptMaster;
268 s32 result;
269 s32 i;
270
271 result = -1;
272 task = gSchedulerTaskTable;
273 IRQ_LOCK(savedInterruptMaster);
274 for (i = 0; i < SCHEDULER_TBS_TASK_COUNT; i++, task++) {
275 if ((task->callback >> 24) == 2) {
276 TASK_STATE_HIGH(task) &= ~1;
277 result = i;
278 }
279 }
280 IRQ_UNLOCK(savedInterruptMaster);
281 return result;
282}
283
284/// Runs one pass of the scheduler: every task whose order's high byte matches
285/// `key`'s (SCHEDULER_KEY_MAIN_LOOP or SCHEDULER_KEY_VBLANK), in table order.
286/// Called once a frame by WaitFrames and once a VBlank by
287/// System_VBlankHandler. Nothing runs until Scheduler_ResetTaskTable has set
288/// gSchedulerReady.
290 struct SchedulerTask *task = gSchedulerTaskTable;
291 s32 remaining;
292
293 key >>= 8;
294 if (gSchedulerReady == 1) {
295 remaining = SCHEDULER_TBS_TASK_COUNT + 1;
296 task--;
297 for (;;) {
298 if (--remaining == 0)
299 return;
300 task++;
301 if (TASK_STATE_HIGH(task) == key) {
302 // UB: callbacks return void, and the stored word is not a pointer
303 // object. The native integer-return call uses r0; a void call uses
304 // r3 instead (two instructions and the call relocation differ).
305#ifdef UBFIX
307#else
309#endif
310
311 callback();
312 }
313 }
314 }
315}
#define TASK_STATE_HIGH(task)
struct SchedulerTask gSchedulerTaskTable[]
u8 gSchedulerReady
1 once the task table is set up
volatile u8 gSchedulerStatus
void(*) SchedulerCallback(void)
@ SCHEDULER_TBS_TASK_COUNT
#define IRQ_UNLOCK(saved)
Restores the IME value saved by IRQ_LOCK.
Definition irqlock.h:50
#define IRQ_LOCK(saved)
Disables interrupts and saves IME in saved; IRQ_UNLOCK restores it.
Definition irqlock.h:38
What every part of the runtime object shares: its includes and the library call two parts make.
s32 Scheduler_ResumeTasks(SchedulerCallback callback)
Resumes the task running callback, or every task when callback is null, by clearing bit 0 of its orde...
Definition scheduler.c:243
s32(*) KeyCallbackFn(void)
Definition scheduler.c:8
s32 Scheduler_AddOrUpdateCallback(SchedulerCallback callback, s32 order)
Schedules callback with order, or gives a task already running it the new order, and re-sorts the tab...
Definition scheduler.c:102
void Scheduler_ResetTaskTable(void)
Empties the task table: every slot gets no routine, order 0xffff (sorted last) and no flags.
Definition scheduler.c:19
void Scheduler_RunPass(s32 key)
Runs one pass of the scheduler: every task whose order's high byte matches key's (SCHEDULER_KEY_MAIN_...
Definition scheduler.c:289
UNUSED void Scheduler_CopyWords(u32 *destination, u32 *source, u32 byteCount)
Copies byteCount bytes a word at a time. Nothing in ☀️ calls it.
Definition scheduler.c:37
s32 Scheduler_SuspendTasks(SchedulerCallback callback)
Suspends the task running callback, or every task when callback is null, by setting bit 0 of its orde...
Definition scheduler.c:172
s32 Scheduler_ResumeOverlayTasks(void)
Resumes every task whose routine is in an overlay, flagged or not.
Definition scheduler.c:265
s32 Scheduler_SuspendOverlayTasks(void)
Suspends every task whose routine is in an overlay (EWRAM, 0x02xxxxxx) unless its flag bit 0 asks to ...
Definition scheduler.c:197
UNUSED void Scheduler_Idle(void)
An empty routine; nothing in ☀️ calls it.
Definition scheduler.c:138
void Scheduler_SortTasks(void)
Bubble-sorts the task table by order, highest first, so each pass runs its tasks from the highest ord...
Definition scheduler.c:47
s32 Scheduler_RemoveCallback(SchedulerCallback callback)
Unschedules the task running callback: clears its routine and gives it order 0x7fff,...
Definition scheduler.c:146
UNUSED s32 Scheduler_FindCallback(SchedulerCallback callback)
Returns the table index of the task running callback, or -1.
Definition scheduler.c:77
s32 Scheduler_SetTaskFlags(SchedulerCallback callback, u32 mask)
Stores mask as the flags of the task running callback; bit 0 keeps it running while the overlay tasks...
Definition scheduler.c:220
UNUSED void Scheduler_EmptyCallback(void)
An empty routine; nothing in ☀️ calls it.
Definition scheduler.c:141
A scheduled task: its routine's address, its order (see SCHEDULER_KEY_) and its flags,...
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
#define UNUSED
Definition types.h:22
void * memcpy(void *destination, const void *source, unsigned long size)