diff options
author | Keith Packard <keithp@keithp.com> | 2009-04-12 20:25:39 -0700 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2009-04-12 20:25:39 -0700 |
commit | 1903a86bf2cc6b685ccc475e62eabe49a4ec5b43 (patch) | |
tree | bc791a4a40f84ea8890e001c2d03afd1abd51d7d /ao_task.c |
Initial AltOS import
Diffstat (limited to 'ao_task.c')
-rw-r--r-- | ao_task.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/ao_task.c b/ao_task.c new file mode 100644 index 00000000..4f7387e3 --- /dev/null +++ b/ao_task.c @@ -0,0 +1,185 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +#define AO_NO_TASK 0xff + +__xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS]; +__data uint8_t ao_num_tasks; +__data uint8_t ao_cur_task_id; +__xdata struct ao_task *__data ao_cur_task; + +void +ao_add_task(__xdata struct ao_task * task, void (*start)(void)) +{ + uint8_t __xdata *stack; + if (ao_num_tasks == AO_NUM_TASKS) + ao_panic(AO_ERROR_NO_TASK); + ao_tasks[ao_num_tasks++] = task; + /* + * Construct a stack frame so that it will 'return' + * to the start of the task + */ + stack = task->stack; + + *stack++ = ((uint16_t) start); + *stack++ = ((uint16_t) start) >> 8; + + /* and the stuff saved by ao_switch */ + *stack++ = 0; /* acc */ + *stack++ = 0x80; /* IE */ + *stack++ = 0; /* DPL */ + *stack++ = 0; /* DPH */ + *stack++ = 0; /* B */ + *stack++ = 0; /* R2 */ + *stack++ = 0; /* R3 */ + *stack++ = 0; /* R4 */ + *stack++ = 0; /* R5 */ + *stack++ = 0; /* R6 */ + *stack++ = 0; /* R7 */ + *stack++ = 0; /* R0 */ + *stack++ = 0; /* R1 */ + *stack++ = 0; /* PSW */ + *stack++ = 0; /* BP */ + task->stack_count = stack - task->stack; + task->wchan = NULL; +} + +/* Task switching function. This must not use any stack variables */ +void +ao_yield(void) _naked +{ + static uint8_t __data stack_len; + static __data uint8_t * __data stack_ptr; + static __xdata uint8_t * __data save_ptr; + + /* Save current context */ + _asm + /* Push ACC first, as when restoring the context it must be restored + * last (it is used to set the IE register). */ + push ACC + /* Store the IE register then disable interrupts. */ + push _IEN0 + clr _EA + push DPL + push DPH + push b + push ar2 + push ar3 + push ar4 + push ar5 + push ar6 + push ar7 + push ar0 + push ar1 + push PSW + _endasm; + PSW = 0; + _asm + push _bp + _endasm; + + if (ao_cur_task_id != AO_NO_TASK) + { + /* Save the current stack */ + stack_len = SP - AO_STACK_START; + ao_cur_task->stack_count = stack_len; + stack_ptr = (uint8_t __data *) AO_STACK_START; + save_ptr = (uint8_t __xdata *) ao_cur_task->stack; + while (stack_len--) + *save_ptr++ = *stack_ptr++; + } + + /* Empty the stack; might as well let interrupts have the whole thing */ + SP = AO_STACK_START; + + /* Find a task to run. If there isn't any runnable task, + * this loop will run forever, which is just fine + */ + for (;;) { + ++ao_cur_task_id; + if (ao_cur_task_id == ao_num_tasks) + ao_cur_task_id = 0; + ao_cur_task = ao_tasks[ao_cur_task_id]; + if (ao_cur_task->wchan == NULL) + break; + } + + /* Restore the old stack */ + stack_len = ao_cur_task->stack_count; + stack_ptr = (uint8_t __data *) AO_STACK_START; + save_ptr = (uint8_t __xdata *) ao_cur_task->stack; + while (stack_len--) + *stack_ptr++ = *save_ptr++; + SP = (uint8_t) (stack_ptr - 1); + + _asm + pop _bp + pop PSW + pop ar1 + pop ar0 + pop ar7 + pop ar6 + pop ar5 + pop ar4 + pop ar3 + pop ar2 + pop b + pop DPH + pop DPL + /* The next byte of the stack is the IE register. Only the global + enable bit forms part of the task context. Pop off the IE then set + the global enable bit to match that of the stored IE register. */ + pop ACC + JB ACC.7,0098$ + CLR _EA + LJMP 0099$ + 0098$: + SETB _EA + 0099$: + /* Finally pop off the ACC, which was the first register saved. */ + pop ACC + ret + _endasm; +} + +int +ao_sleep(__xdata void *wchan) +{ + ao_cur_task->wchan = wchan; + ao_yield(); +} + +int +ao_wakeup(__xdata void *wchan) +{ + uint8_t i; + + for (i = 0; i < ao_num_tasks; i++) + if (ao_tasks[i]->wchan == wchan) + ao_tasks[i]->wchan = NULL; +} + +void +ao_start_scheduler(void) +{ + ao_cur_task_id = AO_NO_TASK; + ao_cur_task = NULL; + ao_yield(); +} |