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; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 #define AO_NO_TASK 0xff
23 __xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];
24 __data uint8_t ao_num_tasks;
25 __data uint8_t ao_cur_task_id;
26 __xdata struct ao_task *__data ao_cur_task;
29 ao_add_task(__xdata struct ao_task * task, void (*start)(void))
31 uint8_t __xdata *stack;
32 if (ao_num_tasks == AO_NUM_TASKS)
33 ao_panic(AO_ERROR_NO_TASK);
34 ao_tasks[ao_num_tasks++] = task;
36 * Construct a stack frame so that it will 'return'
37 * to the start of the task
41 *stack++ = ((uint16_t) start);
42 *stack++ = ((uint16_t) start) >> 8;
44 /* and the stuff saved by ao_switch */
45 *stack++ = 0; /* acc */
46 *stack++ = 0x80; /* IE */
47 *stack++ = 0; /* DPL */
48 *stack++ = 0; /* DPH */
50 *stack++ = 0; /* R2 */
51 *stack++ = 0; /* R3 */
52 *stack++ = 0; /* R4 */
53 *stack++ = 0; /* R5 */
54 *stack++ = 0; /* R6 */
55 *stack++ = 0; /* R7 */
56 *stack++ = 0; /* R0 */
57 *stack++ = 0; /* R1 */
58 *stack++ = 0; /* PSW */
59 *stack++ = 0; /* BP */
60 task->stack_count = stack - task->stack;
64 /* Task switching function. This must not use any stack variables */
68 static uint8_t __data stack_len;
69 static __data uint8_t * __data stack_ptr;
70 static __xdata uint8_t * __data save_ptr;
72 /* Save current context */
74 /* Push ACC first, as when restoring the context it must be restored
75 * last (it is used to set the IE register). */
77 /* Store the IE register then disable interrupts. */
98 if (ao_cur_task_id != AO_NO_TASK)
100 /* Save the current stack */
101 stack_len = SP - AO_STACK_START;
102 ao_cur_task->stack_count = stack_len;
103 stack_ptr = (uint8_t __data *) AO_STACK_START;
104 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
106 *save_ptr++ = *stack_ptr++;
109 /* Empty the stack; might as well let interrupts have the whole thing */
112 /* Find a task to run. If there isn't any runnable task,
113 * this loop will run forever, which is just fine
117 if (ao_cur_task_id == ao_num_tasks)
119 ao_cur_task = ao_tasks[ao_cur_task_id];
120 if (ao_cur_task->wchan == NULL)
124 /* Restore the old stack */
125 stack_len = ao_cur_task->stack_count;
126 stack_ptr = (uint8_t __data *) AO_STACK_START;
127 save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
129 *stack_ptr++ = *save_ptr++;
130 SP = (uint8_t) (stack_ptr - 1);
146 /* The next byte of the stack is the IE register. Only the global
147 enable bit forms part of the task context. Pop off the IE then set
148 the global enable bit to match that of the stored IE register. */
156 /* Finally pop off the ACC, which was the first register saved. */
163 ao_sleep(__xdata void *wchan)
165 ao_cur_task->wchan = wchan;
170 ao_wakeup(__xdata void *wchan)
174 for (i = 0; i < ao_num_tasks; i++)
175 if (ao_tasks[i]->wchan == wchan)
176 ao_tasks[i]->wchan = NULL;
180 ao_start_scheduler(void)
182 ao_cur_task_id = AO_NO_TASK;