2 * Copyright © 2009 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 #define AO_NO_TASK_INDEX 0xff
22 __xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];
23 __data uint8_t ao_num_tasks;
24 __data uint8_t ao_cur_task_index;
25 __xdata struct ao_task *__data ao_cur_task;
28 ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant
32 if (ao_num_tasks == AO_NUM_TASKS)
33 ao_panic(AO_PANIC_NO_TASK);
34 for (task_id = 1; task_id != 0; task_id++) {
35 for (t = 0; t < ao_num_tasks; t++)
36 if (ao_tasks[t]->task_id == task_id)
38 if (t == ao_num_tasks)
41 ao_tasks[ao_num_tasks++] = task;
42 task->task_id = task_id;
46 * Construct a stack frame so that it will 'return'
47 * to the start of the task
49 ao_arch_init_stack(task, start);
52 /* Task switching function. This must not use any stack variables */
54 ao_yield(void) ao_arch_naked_define
56 ao_arch_save_context();
58 if (ao_cur_task_index == AO_NO_TASK_INDEX)
59 ao_cur_task_index = ao_num_tasks-1;
63 __data uint8_t *stack_ptr;
64 __xdata uint8_t *save_ptr;
65 /* Save the current stack */
66 stack_len = SP - (AO_STACK_START - 1);
67 ao_cur_task->stack_count = stack_len;
68 stack_ptr = (uint8_t __data *) AO_STACK_START;
69 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
71 *save_ptr++ = *stack_ptr++;
75 /* Empty the stack; might as well let interrupts have the whole thing */
76 SP = AO_STACK_START - 1;
78 /* Find a task to run. If there isn't any runnable task,
79 * this loop will run forever, which is just fine
82 __pdata uint8_t ao_next_task_index = ao_cur_task_index;
85 if (ao_next_task_index == ao_num_tasks)
86 ao_next_task_index = 0;
88 ao_cur_task = ao_tasks[ao_next_task_index];
89 if (ao_cur_task->wchan == NULL) {
90 ao_cur_task_index = ao_next_task_index;
94 /* Check if the alarm is set for a time which has passed */
95 if (ao_cur_task->alarm &&
96 (int16_t) (ao_time() - ao_cur_task->alarm) >= 0) {
97 ao_cur_task_index = ao_next_task_index;
101 /* Enter lower power mode when there isn't anything to do */
102 if (ao_next_task_index == ao_cur_task_index)
109 __data uint8_t *stack_ptr;
110 __xdata uint8_t *save_ptr;
112 /* Restore the old stack */
113 stack_len = ao_cur_task->stack_count;
114 SP = AO_STACK_START - 1 + stack_len;
116 stack_ptr = (uint8_t __data *) AO_STACK_START;
117 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
119 *stack_ptr++ = *save_ptr++;
137 /* The next byte of the stack is the IE register. Only the global
138 enable bit forms part of the task context. Pop off the IE then set
139 the global enable bit to match that of the stored IE register. */
147 /* Finally pop off the ACC, which was the first register saved. */
154 ao_sleep(__xdata void *wchan)
157 ao_cur_task->wchan = wchan;
160 ao_cur_task->alarm = 0;
161 if (ao_cur_task->wchan) {
162 ao_cur_task->wchan = NULL;
169 ao_wakeup(__xdata void *wchan)
173 for (i = 0; i < ao_num_tasks; i++)
174 if (ao_tasks[i]->wchan == wchan)
175 ao_tasks[i]->wchan = NULL;
179 ao_alarm(uint16_t delay)
181 /* Make sure we sleep *at least* delay ticks, which means adding
182 * one to account for the fact that we may be close to the next tick
184 if (!(ao_cur_task->alarm = ao_time() + delay + 1))
185 ao_cur_task->alarm = 1;
189 ao_exit(void) __critical
193 for (i = ao_cur_task_index; i < ao_num_tasks; i++)
194 ao_tasks[i] = ao_tasks[i+1];
195 ao_cur_task_index = AO_NO_TASK_INDEX;
197 /* we'll never get back here */
205 __xdata struct ao_task *task;
207 for (i = 0; i < ao_num_tasks; i++) {
209 pc_loc = task->stack_count - 17;
210 printf("%12s: wchan %04x pc %04x\n",
212 (int16_t) task->wchan,
213 (task->stack[pc_loc]) | (task->stack[pc_loc+1] << 8));
218 ao_start_scheduler(void)
220 ao_cur_task_index = AO_NO_TASK_INDEX;