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
30 uint8_t __xdata *stack;
31 if (ao_num_tasks == AO_NUM_TASKS)
32 ao_panic(AO_PANIC_NO_TASK);
33 ao_tasks[ao_num_tasks++] = task;
34 task->task_id = ao_num_tasks;
37 * Construct a stack frame so that it will 'return'
38 * to the start of the task
42 *stack++ = ((uint16_t) start);
43 *stack++ = ((uint16_t) start) >> 8;
45 /* and the stuff saved by ao_switch */
46 *stack++ = 0; /* acc */
47 *stack++ = 0x80; /* IE */
48 *stack++ = 0; /* DPL */
49 *stack++ = 0; /* DPH */
51 *stack++ = 0; /* R2 */
52 *stack++ = 0; /* R3 */
53 *stack++ = 0; /* R4 */
54 *stack++ = 0; /* R5 */
55 *stack++ = 0; /* R6 */
56 *stack++ = 0; /* R7 */
57 *stack++ = 0; /* R0 */
58 *stack++ = 0; /* R1 */
59 *stack++ = 0; /* PSW */
60 *stack++ = 0; /* BP */
61 task->stack_count = stack - task->stack;
65 /* Task switching function. This must not use any stack variables */
70 /* Save current context */
72 /* Push ACC first, as when restoring the context it must be restored
73 * last (it is used to set the IE register). */
75 /* Store the IE register then enable interrupts. */
96 if (ao_cur_task_index == AO_NO_TASK_INDEX)
97 ao_cur_task_index = ao_num_tasks-1;
101 __data uint8_t *stack_ptr;
102 __xdata uint8_t *save_ptr;
103 /* Save the current stack */
104 stack_len = SP - (AO_STACK_START - 1);
105 ao_cur_task->stack_count = stack_len;
106 stack_ptr = (uint8_t __data *) AO_STACK_START;
107 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
109 *save_ptr++ = *stack_ptr++;
113 /* Empty the stack; might as well let interrupts have the whole thing */
114 SP = AO_STACK_START - 1;
116 /* Find a task to run. If there isn't any runnable task,
117 * this loop will run forever, which is just fine
120 __pdata uint8_t ao_next_task_index = ao_cur_task_index;
122 ++ao_next_task_index;
123 if (ao_next_task_index == ao_num_tasks)
124 ao_next_task_index = 0;
126 ao_cur_task = ao_tasks[ao_next_task_index];
127 if (ao_cur_task->wchan == NULL) {
128 ao_cur_task_index = ao_next_task_index;
132 /* Enter lower power mode when there isn't anything to do */
133 if (ao_next_task_index == ao_cur_task_index)
140 __data uint8_t *stack_ptr;
141 __xdata uint8_t *save_ptr;
143 /* Restore the old stack */
144 stack_len = ao_cur_task->stack_count;
145 SP = AO_STACK_START - 1 + stack_len;
147 stack_ptr = (uint8_t __data *) AO_STACK_START;
148 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
150 *stack_ptr++ = *save_ptr++;
168 /* The next byte of the stack is the IE register. Only the global
169 enable bit forms part of the task context. Pop off the IE then set
170 the global enable bit to match that of the stored IE register. */
178 /* Finally pop off the ACC, which was the first register saved. */
185 ao_sleep(__xdata void *wchan)
188 ao_cur_task->wchan = wchan;
194 ao_wakeup(__xdata void *wchan)
198 for (i = 0; i < ao_num_tasks; i++)
199 if (ao_tasks[i]->wchan == wchan)
200 ao_tasks[i]->wchan = NULL;
204 ao_wake_task(__xdata struct ao_task *task)
214 for (i = ao_cur_task_index; i < ao_num_tasks; i++)
215 ao_tasks[i] = ao_tasks[i+1];
216 ao_cur_task_index = AO_NO_TASK_INDEX;
218 /* we'll never get back here */
226 __xdata struct ao_task *task;
228 for (i = 0; i < ao_num_tasks; i++) {
230 pc_loc = task->stack_count - 17;
231 printf("%12s: wchan %04x pc %04x\n",
233 (int16_t) task->wchan,
234 (task->stack[pc_loc]) | (task->stack[pc_loc+1] << 8));
239 ao_start_scheduler(void)
241 ao_cur_task_index = AO_NO_TASK_INDEX;