#include "kernel.h" #include /* exit() */ const unsigned char bitPower[] = {1, 2, 4, 8, 16, 32, 64, 128}; #if MAX_TASK == 64 prtyStruc runningTasks = {0, {0, 0, 0, 0, 0, 0, 0, 0}}; #else prtyStruc runningTasks = {0}; #endif taskStruc taskBuf[MAX_TASK]; #ifdef HI_TECH_C #pragma interrupt_level 0 #endif /*---------------------------------------------------------------- * setTaskBit positionne le bit correspondant à une tâche dans une * structure de stockage de priorités de tâches. * * arguments : prtyTblPtr est un pointeur vers la structure de * priorités à modifier. * task est le numéro de la tâche dont on doit * positionner le bit correspondant. * * valeur de retour : aucune. * * note : cette fonction est utilisée de manière interne par le * kernel. Les tâches et les fonctions de traitement * d'interruption NE PEUVENT EN PRINCIPE PAS appeler directement * cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ void setTaskBit(prtyStruc *prtyTblPtr, unsigned char task) { #if MAX_TASK == 64 unsigned char posY; posY = task >> 3; prtyTblPtr->x[posY] |= bitPower[task & 7]; prtyTblPtr->y |= bitPower[posY]; #else prtyTblPtr->x |= bitPower[task & 7]; #endif } /*---------------------------------------------------------------- * clearTaskBit efface le bit correspondant à une tâche dans une * structure de stockage de priorités de tâches. * * arguments : prtyTblPtr est un pointeur vers la structure de * priorités à modifier. * task est le numéro de la tâche dont on doit * effacer le bit correspondant. * * valeur de retour : aucune. * * note : cette fonction est utilisée de manière interne par le * kernel. Les tâches et les fonctions de traitement * d'interruption NE PEUVENT EN PRINCIPE PAS appeler directement * cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ void clearTaskBit(prtyStruc *prtyTblPtr, unsigned char task) { #if MAX_TASK == 64 unsigned char posY; posY = task >> 3; if (!(prtyTblPtr->x[posY] &= ~bitPower[task & 7])) prtyTblPtr->y &= ~bitPower[posY]; #else prtyTblPtr->x &= ~bitPower[task & 7]; #endif } /*---------------------------------------------------------------- * getTaskBit renvoie 0 si le bit correspondant à une tâche * est positionné dans une structure de priotité de tâche. * * arguments : prtyTblPtr est un pointeur vers la structure de * priorités. * task est le numéro de la tâche. * * note : cette fonction est utilisée de manière interne par le * kernel. Les tâches et les fonctions de traitement * d'interruption NE PEUVENT EN PRINCIPE PAS appeler directement * cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ unsigned char getTaskBit(prtyStruc *prtyTblPtr, unsigned char task) { #if MAX_TASK == 64 unsigned char posY; posY = task >> 3; return (prtyTblPtr->x[posY] & bitPower[task & 7]); #else return (prtyTblPtr->x & bitPower[task & 7]); #endif } /*---------------------------------------------------------------- * higherTask donne le numéro de la tâche la plus prioritaire dont * le bit est positionné dans la structure de stockage de * priorités de tâches donnée. * * argument : taskPrty est la structure de priorités à consulter. * * valeur de retour : le numéro de la tâche de priorité la plus * élevée dont le bit est positionné dans la * table. * * note : cette fonction est utilisée de manière interne par le * kernel. Les tâches et les fonctions de traitement * d'interruption NE PEUVENT EN PRINCIPE PAS appeler directement * cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ unsigned char higherTask(prtyStruc tasksPrty) { #if MAX_TASK == 64 char posY, posX; for(posY = 7; posY >= 0; posY--) if (tasksPrty.y & bitPower[(unsigned char) posY]) break; if (posY < 0) return MAX_TASK; /* aucune tâche n'est active? */ for(posX = 7;; posX--) /* le gardien est inutile ici car il doit y avoir une tâche active si l'exécution se poursuit jusqu'ici */ if (tasksPrty.x[(unsigned char) posY] & bitPower[(unsigned char) posX]) break; return (posX + (posY << 3)); #else char posX; for(posX = 7; posX >= 0; posX--) if (tasksPrty.x & bitPower[(unsigned char) posX]) break; if (posX < 0) return MAX_TASK; /* aucune tâche n'est active? */ return posX; #endif } #ifdef HI_TECH_C #pragma interrupt_level 0 #endif /*---------------------------------------------------------------- * startTask indique au kernel qu'une tâche devient exécutable. * Cette tâche verra ses instructions exécutées par le processeur * quand elle sera la tâche exécutable la plus prioritaire * instanciée sur le système. FIX_ME completer * * argument : task est le numéro de la tâche. * * valeur de retour : aucune. * * note : cette fonction peut être appelée dans une fonction de * traitement d'interruption. Elle est principalement utilisée de * manière interne par le kernel. Les tâches NE devraient PAS * appeler directement cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ void startTask(unsigned char task) { if (!getTaskBit(&runningTasks, task)) { taskBuf[task].pos = 0; setTaskBit(&runningTasks, task); } } /*---------------------------------------------------------------- * killTask indique au kernel qu'une tâche n'est plus exécutable. * Cette fonction retire aussi la tâche de tous les ensembles de * tâches en attente sur les sémaphores et réinitialise sa * position courante d'exécution au début de son code. * * argument : task est le numéro de la tâche. * * valeur de retour : aucune. * * note : cette fonction peut être appelée dans une fonction de * traitement d'interruption. Elle est principalement utilisée de * manière interne par le kernel. Les tâches NE devraient PAS * appeler directement cette fonction. * type : nécessaire. *---------------------------------------------------------------- */ void killTask(unsigned char task) { #ifdef USE_SEMAPHORE unsigned char i; #endif clearTaskBit(&runningTasks, task); taskBuf[task].pos = 0; #ifdef USE_SEMAPHORE /* code de suppression de la tâche dans les ensembles de tâches en attente sur les sémaphores */ for(i = 0; i < MAX_SEM; i++) remWaitingTask(i, task); #endif } /*---------------------------------------------------------------- * stopTask indique au kernel qu'une tâche n'est plus exécutable. * La position courante d'exécution de la tâche ainsi que les * informations concernant l'éventuelle attente de cette tâche * sur des sémaphores ne sont pas modifiées par cette fonction. * * argument : task est le numéro de la tâche. * * valeur de retour : aucune. * * note : cette fonction peut être appelée dans une fonction de * traitement d'interruption. Elle est principalement utilisée de * manière interne par le kernel. Les tâches NE devraient PAS * appeler directement cette fonction. * type : facultative. *---------------------------------------------------------------- */ void stopTask(unsigned char task) { clearTaskBit(&runningTasks, task); } /*---------------------------------------------------------------- * restartTask rend à nouveau exécutable une fonction qui a * précédemment été stoppée par la fonction stopTask. * * argument : task est le numéro de la tâche. * * valeur de retour : aucune. * * note : cette fonction peut être appelée dans une fonction de * traitement d'interruption. Elle est principalement utilisée de * manière interne par le kernel. Les tâches NE devraient PAS * appeler directement cette fonction. * type : facultative. *---------------------------------------------------------------- */ void restartTask(unsigned char task) { setTaskBit(&runningTasks, task); } /*---------------------------------------------------------------- * schedule est la fonction la plus importante du kernel. * Lorsqu'elle reprend le contrôle du système, elle sélectionne * la tâche exécutable la plus prioritaire et relance son * exécution. De plus, elle réalise effectivement l'appel système * demandé par une tâche. * * argument : aucun. * * valeur de retour : aucune. * * note : cette fonction NE DOIT ETRE APPELEE QU'UNE SEULE FOIS, * dans la fonction main de l'application. Le mécanisme qui permet * de lui rendre le contrôle pour lui faire exécuter des appels * systèmes est expliqué en détail dans le rapport. * type : nécessaire. *---------------------------------------------------------------- */ void schedule() { unsigned char task, sysCallCode; taskParmStruc *taskParmPtr; for(;;) { /* dans une vraie application, on boucle indéfiniment si il n'y a pas de tache active mais pour les tests sur une station de travail, on s'arrete... */ #ifdef WSDEBUG if ((task = higherTask(runningTasks)) == MAX_TASK) break; #else if ((task = higherTask(runningTasks)) == MAX_TASK) continue; #endif /* en principe, on ne peut pas interdire les interruptions ici mais si les tâches utilisent les huit niveaux de la pile, plus aucune interruption ne pourra se déclencher sans mener à un crash du système... (cas du PIC) */ di(); /* à enlever */ taskParmPtr = dispatch(task, taskBuf[task].pos); ei(); /* à enlever */ taskBuf[task].pos = taskParmPtr->pos; sysCallCode = taskParmPtr->sysCallCode; switch(sysCallCode) { case RESCHEDULE: break; case TASK_START: di(); startTask(taskParmPtr->task); ei(); break; case TASK_EXIT: di(); killTask(taskParmPtr->task); ei(); break; #ifdef USE_SEMAPHORE case SIGNAL_SEM: di(); signalSem(taskParmPtr->sem); ei(); break; case WAIT_SEM: waitSem(task, taskParmPtr->sem); break; case SET_SEM_VALUE: di(); setSemValue(taskParmPtr->sem, taskParmPtr->val); ei(); break; case GET_SEM_VALUE: di(); taskParmPtr->val = getSemValue(taskParmPtr->sem); ei(); break; #endif /* #ifdef USE_SEMAPHORE */ #ifdef WSDEBUG default: printf("unknown syscall code %d\n", sysCallCode); exit(-1); #endif } } }