femtoos_source/femtoos_core.c

Go to the documentation of this file.
00001 /*
00002  * Femto OS v 0.91 - Copyright (C) 2008-2009 Ruud Vlaming
00003  *
00004  * This file is part of the Femto OS distribution.
00005  *
00006  * This program is free software: you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation, version 3 of the License.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * Please note that, due to the GPLv3 license, for application of this
00019  * work and/or combined work in embedded systems special obligations apply.
00020  * If these are not to you liking, please know the Femto OS is dual
00021  * licensed. A commercial license and support are available.
00022  * See http://www.femtoos.org/ for details.
00023  */
00024 
00025 #include "femtoos_core.h"
00026 #include "femtoos_shared.h"
00027 
00028 
00029 /* ========================================================================= */
00030 /* FEMTO OS INTERNAL FUNCTIONS  ============================================ */
00031 /* ========================================================================= */
00032 
00033 /*
00034  * Body definitions. All switching methods are called via an assembler
00035  * redirect, making it possible to switch stack, save context and preserve
00036  * all registers (including the parameters) while doing so. Without this
00037  * trick gcc may push the parameter registers or construct a frame pointer
00038  * first thereby destroying other registers. Notice functions never return,
00039  * but are not 'noreturn'. They are naked or bikini.
00040  */
00041 
00049 #if (includeTaskDelayFromNow == cfgTrue)
00050   static void privDelayFromNowBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00051 #endif
00052 
00062 #if (includeTaskDelayFromWake == cfgTrue)
00063   static void privDelayFromWakeBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00064 #endif
00065 
00071 #if (includeTaskRecreate == cfgTrue)
00072   static void privRecreateBody(Tuint08 uiTaskNumber) __attribute__((used)) defSysReduceProEpilogue;
00073 #endif
00074 
00080 #if (includeTaskRestart == cfgTrue)
00081   static void privRestartBody(Tuint08 uiRestartMode, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00082 #endif
00083 
00089 #if (includeTaskYield == cfgTrue)
00090   static void privYieldBody(void) __attribute__((used)) defSysReduceProEpilogue;
00091 #endif
00092 
00098 #if (includeTaskTerminate == cfgTrue)
00099   static void privTerminateBody(Tuint08 uiTaskNumber) __attribute__((used)) defSysReduceProEpilogue;
00100 #endif
00101 
00107 #if (includeTaskSuspend == cfgTrue)
00108   static void privSuspendBody(Tuint08 uiSuspendMode) __attribute__((used)) defSysReduceProEpilogue;
00109 #endif
00110 
00116 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleep == cfgTrue)
00117   static void privSleepBody(void) __attribute__((used)) defSysReduceProEpilogue;
00118 #endif
00119 
00125 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue)
00126   static void privSleepAllBody(void) __attribute__((used)) defSysReduceProEpilogue;
00127 #endif
00128 
00134 #if (cfgUseSynchronization != cfgSyncNon) && (includeTaskWaitForTasks == cfgTrue)
00135   #if (cfgUseTimeout == cfgTrue)
00136     static void privWaitForTasksBody(Tuint08 uiSlot, Tuint08 uiNumberOfTasks, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00137   #else
00138     static void privWaitForTasksBody(Tuint08 uiSlot, Tuint08 uiNumberOfTasks) __attribute__((used)) defSysReduceProEpilogue;
00139   #endif
00140 #endif
00141 
00147 #if (cfgUseSynchronization != cfgSyncNon) && ( (includeTaskQueu == cfgTrue) || (includeTaskMutex == cfgTrue) )
00148   #if (cfgUseTimeout == cfgTrue)
00149     #if (cfgUseSynchronization == cfgSyncDoubleBlock)
00150       static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeLeftFilling, Tsint08 siFreeRightFilling, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00151     #else
00152       static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeRightFilling, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00153     #endif
00154   #else
00155     #if (cfgUseSynchronization == cfgSyncDoubleBlock)
00156       static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeLeftFilling, Tsint08 siFreeRightFilling) __attribute__((used)) defSysReduceProEpilogue;
00157     #else
00158       static void privSyncRequestBody(Tuint08 uiSlotSlot, Tsint08 siFreeRightFilling) __attribute__((used)) defSysReduceProEpilogue;
00159     #endif
00160   #endif
00161 #endif
00162 
00168 #if (cfgUseSynchronization != cfgSyncNon) && ( (includeTaskQueu == cfgTrue) || (includeTaskMutex == cfgTrue) )
00169   static void privSyncReleaseBody(Tuint08 uiSlotSlot) __attribute__((used)) defSysReduceProEpilogue;
00170 #endif
00171 
00177 #if (cfgUseFileSystem  ==  cfgTrue) && (includeTaskFileAccess == cfgTrue)
00178   #if (cfgUseTimeout == cfgTrue)
00179     #if (cfgUseFileSystemConcurrentRead == cfgTrue)
00180       static void privFileOpenBody(Tbool bReadOnly, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00181     #else
00182       static void privFileOpenBody(Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00183     #endif
00184   #else
00185     #if (cfgUseFileSystemConcurrentRead == cfgTrue)
00186       static void privFileOpenBody(Tbool bReadOnly) __attribute__((used)) defSysReduceProEpilogue;
00187     #else
00188       static void privFileOpenBody(void) __attribute__((used)) defSysReduceProEpilogue;
00189     #endif
00190   #endif
00191 #endif
00192 
00198 #if (cfgUseFileSystem  ==  cfgTrue) && (includeTaskFileAccess == cfgTrue)
00199   static void privFileCloseBody(void) __attribute__((used)) defSysReduceProEpilogue;
00200 #endif
00201 
00207 #if (cfgUseFileSystem  ==  cfgTrue)
00208   static void privWaitForFsAccessBody(void) __attribute__((used)) defSysReduceProEpilogue;
00209 #endif
00210 
00216 #if (cfgUseEvents == cfgTrue) && (includeTaskWaitForEvents == cfgTrue)
00217   #if (cfgUseTimeout == cfgTrue)
00218     static void privWaitForEventBody(Tuint08 uiEvent, Tuint16 uiTicksToWait) __attribute__((used)) defSysReduceProEpilogue;
00219   #else
00220     static void privWaitForEventBody(Tuint08 uiEvent) __attribute__((used)) defSysReduceProEpilogue;
00221   #endif
00222 #endif
00223 
00231 #if (defCheckReportingError == cfgTrue)
00232   #if (cfgCheckAlwaysFatal == cfgTrue)
00233     static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo) __attribute__ (( noreturn ));
00234 #else
00235     static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo);
00236   #endif
00237 #endif
00238 
00245 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
00246   static void privCheckOsStackLevel(void);
00247 #endif
00248 
00255 #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse)
00256   static void privCheckOsStackRegion(void);
00257 #endif
00258 
00264 #ifdef portInitContext
00265   static Taddress privInitContext(Taddress pTaskStart, Taddress pStackTop, Tuint08 uiRegisterCount, Tuint08 uiInterruptStart);
00266 #endif
00267 
00276 static TtaskControlBlock * privTcbList(Tuint08 uiTaskNumber) __attribute__ ((pure, noinline));
00277 
00284 static Tuint08 privTaskNumber(Tuint08 uiTaskNumber) __attribute__ ((pure, noinline, unused));
00285 
00293 #if (cfgCheckTaskStack == cfgTrue) && (StackSafety > 0)
00294   static void privTestStackRegion(void) __attribute__ (( noinline));
00295 #endif
00296 
00305 #if (cfgUseLowPowerSleep == cfgTrue)
00306   static void privWakeupFromLowPower(void) __attribute__ ((noinline));
00307 #endif
00308 
00319 #if (defRegisterUseConstant == cfgFalse)
00320   static Tuint08 privRegisterCount(Tuint08 uiRegisterUse) __attribute__ ((const));
00321 #endif
00322 
00323 /*DISCUSSION
00324  * Some methods use quite a bit of stack, i.e. they start by saving
00325  * say 10 registers on the context. This implies not only 10 more bytes
00326  * of ram use, but also 40 extra flash bytes just to store and retrieve
00327  * the bytes. This must be weighed against inlining those methods.
00328  * Less stack, more flash, but maybe not that much more. It really depends
00329  * which resource is limited.
00330  * Also that discussion cannot be made per function, since you cannot simply
00331  * add the stack use. Functions could be inlined if needed by adding
00332  *  __attribute__ ( ( always_inline ) );
00333  * to their definitions.
00334  */
00335 
00344 static void privTaskInit(Tuint08 uiTaskNumber, Tuint08 uiInitControl);
00345 
00354 static void privEnterOS(Tuint08 uiAction) defSysReduceProEpilogue;
00355 
00363 static void privIncrementTick(void);
00364 
00371 static Tuint08 privSwitchContext(void);
00372 
00380 static Tselect privSelectTask(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd) __attribute__ (( always_inline ));
00381 
00388 #if (cfgUseHierarchicalRoundRobin == cfgTrue)
00389   static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiPriority, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) __attribute__ (( always_inline ));
00390 #else
00391   static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip) __attribute__ (( always_inline ));
00392 #endif
00393 
00401 #if (cfgUseLowPowerSleep == cfgTrue)
00402   static void privEnterSleep(Tuint08 uiTickMinDelay);
00403 #endif
00404 
00412 static void privEnterIdle(void) defSysReduceProEpilogue;
00413 
00421 static void privEnterTask(void) defSysReduceProEpilogue;
00422 
00429 #if (cfgUseLoadMonitor == cfgTrue)
00430   static void privCopyLoad(void);
00431 #endif
00432 
00439 #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue)
00440   static void privTraceWatermarks(void);
00441 #endif
00442 
00449 #if (defUseDelay == cfgTrue)
00450   static void privWakeupFromDelay(Tuint08 uiTaskNumber, TtaskControlBlock * taskTCB);
00451 #endif
00452 
00459 #if (defUseDelay == cfgTrue)
00460   #if (includeTaskDelayFromWake == cfgTrue)
00461     #define privDelayCalcFromNow(DT)   privDelayCalc(DT,true)
00462     #define privDelayCalcFromWake(DT)  privDelayCalc(DT,false)
00463     static void privDelayCalc(Tuint16 uiDelayTime, Tbool bFromNow) defConditionalInline;
00464   #else
00465     static void privDelayCalcFromNow(Tuint16 delayTime) defConditionalInline;
00466   #endif
00467 #endif
00468 
00474 #if (cfgUseSynchronization != cfgSyncNon) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue ))
00475   static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB);
00476 #endif
00477 
00484 #if (cfgUseSynchronization != cfgSyncNon)
00485   static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot);
00486 #endif
00487 
00493 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
00494   static Tuint08 privGetQueuSize(Tuint08 uiQueuNumber) __attribute__ ((always_inline, const));
00495 #endif
00496 
00497 
00507 #if (cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue)
00508   static void privUnblockTask(Tuint08 uiControlTaskNumber);
00509 #endif
00510 
00518 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue)
00519   static void privLiftLocksOnSlot(Tuint08 uiSlot);
00520 #endif
00521 
00529 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue)
00530   static void privRestoreInitialPriority(Tuint08 uiTaskNumber);
00531 #endif
00532 
00540 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
00541   static Tuint08 privQueuTest(Tuint08 uiSlot, Tsint08 siFreeFilling);
00542 #endif
00543 
00552 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))
00553   static void privReleaseSyncBlockingTasks(void) defConditionalInline;
00554 #endif
00555 
00564 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))
00565   static Tuint08 privFreeLockAbsent(Tuint08 uiSlot);
00566 #endif
00567 
00576 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
00577   static Tbool privSizeFitsQueu(Tuint08 uiSlot, Tsint08 siFreeFilling);
00578 #endif
00579 
00587 #if (cfgUseFileSystem  ==  cfgTrue)
00588   static void privReleaseFileBlocks(void);
00589 #endif
00590 
00598 #if (cfgUseFileSystem  ==  cfgTrue)
00599   static Taddress privFileLocation(Tuint08 uiFileNumber, Tuint08 uiOffset);
00600 #endif
00601 
00608 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue)
00609   static void privPutAllTasksToSleep(void);
00610 #endif
00611 
00620 #if (cfgUseFileSystem  ==  cfgTrue)
00621   static void privPrepareFileClose(Tuint08 uiTaskNumber);
00622 #endif
00623 
00632 #if (cfgUseFileSystem  ==  cfgTrue) && (cfgCheckMethodUse == cfgTrue)
00633   static void privCheckFileSpecsWriting(Tuint08 uiFileNumber, Tuint08 uiOffset, Tuint08 uiSize, Tuint08 uiCallId);
00634 #endif
00635 
00644 #if (cfgUseFileSystem  ==  cfgTrue) && (cfgCheckMethodUse == cfgTrue)
00645   static void privCheckFileSpecsReading(Tuint08 uiFileNumber, Tuint08 uiOffset, Tuint08 uiSize, Tuint08 uiCallId);
00646 #endif
00647 
00654 #if (cfgUseFileSystem  ==  cfgTrue) && ( ((cfgUseFileSystemMaintainFAT == cfgTrue) && (includeTaskFileAppendByte == cfgTrue)) || (cfgCheckMethodUse == cfgTrue) )
00655   static Tuint08 privFileSpace(Tuint08 uiFileNumber);
00656 #endif
00657 
00664 #if (cfgCheckMethodUse == cfgTrue)
00665   #if (defCapabilitiesFull == cfgFalse)
00666     static void privCheckCapabilities(Tuint08 uiCallId, Tuint08 uiTaskCaps) __attribute__ ((unused));
00667   #else
00668     #define privCheckCapabilities(X,Y)
00669   #endif
00670 #endif
00671 
00672 
00673 /* ========================================================================= */
00674 /* FEMTO OS CORE IMPLEMENTATION ============================================ */
00675 /* ========================================================================= */
00676 
00677 
00678 #if (defCheckReportingError == cfgTrue)
00679 
00680 static void privShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfo)
00681 { /* We may arrive here from OS space, isr or task space. In the latter case is important we do
00682    * not get tick interrupts (only possible when coming from a task), since reporting an error
00683    * is usually a critical situation. But since we want the user to see the error it is best
00684    * we stop all interrupts.
00685    * Note that for tasks this method should not return to the point of calling but invoke a
00686    * context switch instead. When coming from an isr, we cannot stop that particular isr so
00687    * we should treat that situation as fatal. Only when coming here from the OS itself, we
00688    * may return if that was specifically asked for, or we must switch to a new task (usually
00689    * can a switching method was called from the task). Only when the possibility exists we
00690    * return to the point of origin we need to keep the stack.
00691    * The only question remains, how do we decide where the error occurred. On course we
00692    * can ask the uiOsStatus. If we come from genXXX that is the only way to find out. But,
00693    * if we come for example from privInitOS, this information has not yet been updated,
00694    * or if we come from a switching call, we are not really inside OS space, but we are
00695    * handling a call from the task in OS space. Therefore we have one bit controlling
00696    * if we should use the uiOsState "as-is" or if we should use the Os State forced.
00697    *
00698    * Then, with the resulting state being (idle, isr, task, os)
00699    *  idle => issue an internal error, this should not happen. TODO: not implemented yet.
00700    *  isr  => make error fatal, renew stack, never return
00701    *       => we should not arrive here from a sleeping state.
00702    *  task => if fatal error, renew stack, never return
00703    *       => if non-fatal error, renew stack, terminate task, issue a switch-task
00704    *       => for shared tasks, reset the share state
00705    *  os   => if fatal error, renew stack, never return
00706    *       => if non-fatal error return to place from which the call originated.
00707    * Of course, when cfgCheckAlwaysFatal is activated the error is ... always fatal,
00708    * so we can ignore this hocus-pocus.
00709    * A switch task is a context switch without the without the  context-save and os
00710    * initialization operations.
00711    *
00712    * One extra note. Errors reporting an incorrect task number must be fatal since this
00713    * routine assumes that, when the error is not fatal, the task number represents the
00714    * failing task.
00715    */
00716   privDisableGlobalInterrupts();
00717   /* Strip the error type information from the message */
00718   Tuint08 uiBareMessage = (uiMessage & errMessageGetMask);
00719   /* Errors are traced first, to be sure that tracing is done in case the portError holds the system for ever. */
00720   privTrace(traceErrorBase | uiBareMessage);
00721   /* First check if we have fatal errors per default */
00722   #if (cfgCheckAlwaysFatal == cfgTrue)
00723     /* If so, the matter is simple. All errors are fatal and we never return. */
00724     const Tbool bFatal = true;
00725     const Tbool bReturn = false;
00726   #else
00727     /* uiWorkStatus represents the status we will use upon which we decide what to do. */
00728     Tuint08 uiWorkStatus;
00729     /* See if we must use the uiOsStatus as a basis (standard) or the given by the uiCallId. */
00730     if ((uiMessage & errOsStateGetMask) == errOsStateAsIs)
00731     { /* Extract the upper two bits  containing the status info from the standard status. */
00732       uiWorkStatus = uiOsStatus & defContextGetMask; }
00733     else
00734     { /* Use the StateOs as forced status (i.e. assume we are in the OS state) */
00735       uiWorkStatus = defContextStateOs; }
00736     /* We first calculate when we have a fatal error. That is the case is the error in itself is
00737      * fatal, or if we arrive here from an isr. */
00738     const Tbool bFatal = (uiBareMessage >= errFatalError) || (uiWorkStatus == defContextStateIsr);
00739     /* We may normally return only in one special case. Of course the error may not be fatal, and we must come
00740      * here from the OS itself, (or we must be told we come from the OS itself) */
00741     const Tbool bReturn = !bFatal && (uiWorkStatus == defContextStateOs);
00742   #endif
00743   /* Strip the error type information from the call Id */
00744   Tuint08 uiBareCallId = (uiCallId & errCallIdGetMask);
00745   /* If we do not return we reset the stack to prevent stack overflow when handling the error code.
00746    * We don't need the preserve stack anymore. Note, that we should note have a stack frame present.
00747    * Pushed variables are not important when we no not return. */
00748   if (!bReturn) { privSetStack(&xOS.StackOS[OSstackInit]); }
00749   /* We disable tick interrupts, needed to be deactivated in case we should return. We do that here, after a possible
00750    * stack transplant since this call may make use of the stack. It must be done before global interrupts are
00751    * activated again. */
00752   privDisableTickInterrupts();
00753   /* Now determine the task number. */
00754   Tuint08 uiTaskNumber;
00755   /* Test if we must use the given task number by uiInfo or the current one. This flag is used because
00756    * from a lot of places its much sorter to set the flag than to calculate the current task number.
00757    * Do not use the privTaskNumber() method, for we do not want to introduce stack use here. */
00758   if ((uiMessage & errTaskStateGetMask) == errTaskStateCurrent)
00759   { /* Deduce the current task number from the Status */
00760     uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift;
00761     /* Reconstruct the uiInfo byte, that will be send to the error method later on */
00762     uiInfo = (uiInfo & errTaskNumberSetMask) | (uiTaskNumber << errTaskNumberShift); }
00763   else
00764   { /* Read the task number from the given byte, probably the current task is not the one giving problems */
00765     uiTaskNumber = (uiInfo & errTaskNumberGetMask) >> errTaskNumberShift ; }
00766   /* Now, show the error,  if we had a fatal error it makes no sense to continue operations,
00767    * so the only thing we can do is show the error again*/
00768   do { portShowError(uiBareMessage,uiBareCallId,uiInfo); } while (bFatal);
00769   /* Now, if we return, we must be sure we have enough OS stack space. Since error handling can be
00770    * stack hungry, let us check that here. This may invoke an other error. But since we know
00771    * that, if it occurs, it will be fatal, there cannot be a loop. */
00772   #if (cfgCheckOsStack == cfgTrue)
00773     privCheckOsStackLevel();
00774   #endif
00775   /* In case we had a normal error we want to continue, but since it is reasonable to assume that it
00776    * may have taken some time, the user has probably stop the timer, if he did not misused the timer
00777    * to make some leds blink. In any case its best to restart the timer. We have to live with the
00778    * fact that real time and timer ticks are out of sync anyway. Note that the user may use the timer,
00779    * but may not activate the interrupt. */
00780   portSetupTimerInterrupt();
00781   /* We return from this call with a setup timer, and with tick interrupts disabled. The global interrupts
00782    * are still disabled. If we have no os protection we must re-enable global interrupts again. */
00783   #if (cfgIntOsProtected == cfgFalse)
00784     privEnableGlobalInterrupts();
00785   #endif
00786   /* All errors not being fatal at least must stop the current or requested task and put it in error mode.
00787    * (This cannot be an isr anymore, and all non fatal Os errors are about some task.) */
00788   TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber);
00789   /* Note that the task is terminated, but it's locks are not removed, since this is an error
00790    * condition, it is unclear what the result would be, so that this cannot be compared with
00791    * a normal terminate, in which case the user can take precautions for released blocks etc. */
00792   taskTCB->uiTaskStatus = defBaseTerminatedTask;
00793   /* In case we may have a shared task, we must check if this was one and thus if we must
00794    * reset the ShareStatus. */
00795   #if (defUseSharedStack == cfgTrue)
00796     /* In case all tasks are shared, we know that we must reset. */
00797     #if (defAllSharedStack == cfgFalse)
00798       /* If not, we need the initial status of the task */
00799       #if (defInitialStatusConstant == cfgTrue)
00800         /* if we are lucky that is a constant. */
00801         Tuint08 uiInitialStatus = defInitialStatusFixed;
00802       #else
00803         /* if not, that must be read from flash. */
00804         Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus);
00805       #endif
00806     /* Test if this task is a shared task.  */
00807     if ((uiInitialStatus & defInitialSharedGetMask) == defInitialSharePresent)
00808     #endif
00809     { /* If so, we must identify this shared task as shared. */
00810       taskTCB->pcStackLevel = defStackEmpty;
00811       /* and then we know, since is was running, we must reset the share state. */
00812       uiOsStatus = ((uiOsStatus & defShareStateSetMask) | defShareStateAbsent);  }
00813   #endif
00814   /* If we are at a task, or at some operations within the OS that handle a specific task,
00815    * we can leave this method by asking for a task switch. This is not a normal return.
00816    * Note that in this case the AuxRegBit (if used) can be set. Here, that is not a problem
00817    * for there are no switching interrupts that can take place. */
00818   if (!bReturn) { privEnterOS(defActionTaskStateSwitch); }
00819   /* Otherwise we are done, and return to the OS operations that generated the error. */ }
00820 
00821 #endif
00822 
00823 
00824 #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
00825 
00826 static void privCheckOsStackLevel(void)
00827 { /* We are going to check if we overflowed the Os Stack. We should only arrive here if we are
00828    * in OS space, i.e. if we are sure the stack pointer actually should be inside the OS stack. */
00829   Taddress pStack;
00830   Tuint16 uiOsStackSize;
00831   /* Obtain the current stack pointer. */
00832   privGetStack(pStack);
00833   /* Calculate the Stacksize, we have to distinguish between the different stack types. Note we
00834    * assumed the stack pointer points to the byte that will be written next, i.e. the stack pointer
00835    * is post decrement.  */
00836   #if (cfgSysStackGrowthUp == cfgTrue)
00837     uiOsStackSize = ((Tuint16) pStack - (Tuint16) &xOS.StackOS[0]);
00838   #else
00839     uiOsStackSize = ((Tuint16) &xOS.StackOS[(StackSizeOS)-1] - (Tuint16) pStack);
00840   #endif
00841   /* Now, we do not incorporate the StackSafety parameter into our calculation. That parameter is
00842    * used for task stacks only. And, besides that, the call to this function, i.e.. privCheckOsStack
00843    * costs two bytes extra anyway */
00844   #if (cfgCheckWatermarks == cfgTrue)
00845     if (uiOsStackMax<uiOsStackSize) uiOsStackMax=uiOsStackSize;
00846   #endif
00847   #if (cfgCheckOsStack == cfgTrue)
00848     if (uiOsStackSize>(StackSizeOS)) { privShowError((fatOsStackOverflowed | errTaskStateNon), callIdSystem ,errNoInfo); }
00849   #endif
00850 }
00851 
00852 #endif
00853 
00854 
00855 #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse)
00856 
00857 static void privCheckOsStackRegion(void)
00858 { /* We are going to check which part of the stack space is used. Note, this routine is not capable
00859    * of detecting a stack overflow. In that case, it will simple see that the whole of the space
00860    * has been used. */
00861   Taddress pStack;
00862   /* Start with the largest detectable stack size. */
00863   Tuint08 uiOsStackSize = StackSizeOS;
00864   /* If the whole stack already has been used, there is nothing to check */
00865   if (uiOsStackMax < uiOsStackSize)
00866   { /* Calculate the Stack size, we have to distinguish between the different stack types. */
00867     #if (cfgSysStackGrowthUp == cfgTrue)
00868       /* Find the beginning of the stack. */
00869       pStack = &xOS.StackOS[(StackSizeOS)-1];
00870       /* loop downwards until we find the first used byte, or until we reach a previous boundary whatever comes first. */
00871       while ((*(pStack--) == defStackInitByte) && ((uiOsStackSize--)>uiOsStackMax));
00872     #else
00873       /* Find the beginning of the stack. */
00874       pStack = &xOS.StackOS[0];
00875       /* loop upwards until we find the first used byte, or until we reach a previous boundary whatever comes first. */
00876       while ((*(pStack++) == defStackInitByte) && ((uiOsStackSize--)>uiOsStackMax));
00877     #endif
00878     /* Load the new boundary. */
00879     uiOsStackMax=uiOsStackSize; } }
00880 
00881 #endif
00882 
00883 
00884 
00885 #if (cfgCheckMethodUse == cfgTrue) && (defCapabilitiesFull == cfgFalse)
00886 
00887 static void privCheckCapabilities(Tuint08 uiCallId, Tuint08 uiTaskCaps)
00888 { /* Use this function to see if the task is capable of the required functions.
00889    * The function is not needed if all tasks have full capabilities, for there is nothing
00890    * to check in that case. Note this function can only check the capabilities of the
00891    * current function. If we happen to be in isr space (genXXXX functions) the current
00892    * function has no meaning, and the check is omitted. */
00893   Tuint08 uiTaskNumber;
00894   /* The current task has no meaning inside an isr, so there is nothing to check in that case */
00895   if ((uiOsStatus & defContextGetMask) == defContextStateIsr) { return; }
00896   /* If no, extract the current task number. */
00897   uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift;
00898   /* Get the capabilities of the task*/
00899   Tuint08 uiDefinedCaps = portFlashReadByte(Tuint08,uiCapabilities[uiTaskNumber]);
00900   /* Check if the capabilities match, with ~ all non defined caps turn high, if it matches with a
00901    * required cap, this is an error. */
00902   Tuint08 uiViolation = (uiTaskCaps & ~uiDefinedCaps);
00903   /* Thus if the value equals zero all required capabilities are present, otherwise some are absent. */
00904   if (uiViolation != 0)
00905   { /* Determine the highest violation (binary log) */
00906     Tuint08 uiInfo = 8;
00907     /* Check if this is the violating bit */
00908     while ( (uiViolation & 0x80) == 0 )
00909     { /* If not it could be bit 6, so we decrease the counter one ... */
00910       uiInfo--;
00911       /*  and shift the 6th bit to the 7th */
00912       uiViolation <<= 1; }
00913     /* Report an error and stop the task if the requested capabilities are absent. The ControlTaskNumber
00914      * also contains the realm in which we currently are.  */
00915     privShowError((errInsufficientCapabilities | errTaskStateInfo | errOsStateAsIs), uiCallId, (uiInfo << errInfoNumberShift) | (uiTaskNumber << errTaskNumberShift)); } }
00916 
00917 #endif
00918 
00919 
00920 #ifdef portInitContext
00921 
00922 static Taddress privInitContext(Taddress pTaskStart, Taddress pStackTop, Tuint08 uiRegisterCount, Tuint08 uiInterruptStart)
00923 { /* Arrive here to set up the initial stack. Of course this is machine dependent, but not very
00924    * strongly. For 8 bits cpu's it is fairly general. Just place the return address, the registers
00925    * and the status. Here the values are just cleaned. If needed we can add a portInitConext call
00926    * on the basis of an option. */
00927   Tuint16 uiStartAddress = (Tuint16) pTaskStart;
00928   /* At the bottom of the artificial stack the start address of each task is defined. This is a pointer
00929    * to your Loop code. */
00930   *(pStackTop--) = (Tuint08) uiStartAddress;
00931   *(pStackTop--) = (Tuint08) (uiStartAddress >> 8);
00932   /* If we have a program counter of more than 16 bit, call instructions push three bytes
00933    * into the stack. We must take that into account. Unfortunately, i believe, gcc is not
00934    * able to handle pointer larger as 64K words in the current setting. Thus this will
00935    * effectively be zero, thus although you need something like
00936    *   *(pStackTop--) = (Tuint08) (pTaskStart >> 16);
00937    * we will use: */
00938   #if (defThreeByteAddress == cfgTrue)
00939     *(pStackTop--) = 0;
00940   #endif
00941   /* We rely upon register cleaning done by the Femto OS, or the precleaning done by privTaskInit(). */
00942   pStackTop -= uiRegisterCount;
00943   /* The way the status register is setup depends on the state of the interrupts. These can be specified per task
00944    * or (easier, and shorter) in general. Note that the optimization is not needed in a strict sense, the variable
00945    * uiInterruptStart constrains the interrupt start information, even if this is constant for all tasks. But, of course,
00946    * the lower part generates shorter code. */
00947   #if (defInterruptStartConstant == cfgFalse)
00948     /* If we specify the CPU status register per task, we need to prepare them per task. uiInterruptStart contains
00949      * the information about which interrupts must be activated. The other bits of the status register cannot be
00950      * set individually (there is no need in general) */
00951     Tuint08 uiInitCPUStatusRegister = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc);
00952     /* Test if global interrupts must be activated at the start, if so set the specified bit. */
00953     #if (cfgIntGlobalOnly ==  cfgTrue)
00954       /* In case we have a mapping from tick interrupts on global interrupts, we require both interrupts
00955        * to be activated before activating the global interrupt. */
00956       if ((uiInterruptStart & ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) == ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); }
00957     #else
00958       /* Otherwise we just test the global interrupt setting. */
00959       if ((uiInterruptStart & (cfgGlobSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); }
00960     #endif
00961     /* Test if tick interrupts must be activated at the start, if so set the specified bit. */
00962     #if (cfgIntTickTrack == cfgTrue)
00963       if ((uiInterruptStart & (cfgTickSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitTickInterruptLoc); }
00964     #endif
00965     /* The status register is put at the end of the stack. (See portSaveContext for the reason why) */
00966     *(pStackTop--) = uiInitCPUStatusRegister;
00967   #else
00968     /* Set up the status register with the initial interrupt states: global set and tick set */
00969     #if (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet)
00970       *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc);
00971     /* Set up the status register with the initial interrupt states: global set and tick clear */
00972     #elif (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear)
00973       #if (cfgIntGlobalOnly == cfgTrue)
00974         /* In case we have a mapping from tick interrupts on global interrupts, global interrupts cannot be activated if tick interrupts are not activated */
00975         *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc);
00976       #else
00977         /* Default situation. */
00978         *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc);
00979       #endif
00980     /* Set up the status register with the initial interrupt states: global clear and tick set */
00981     #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet)
00982       *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc);
00983     /* Set up the status register with the initial interrupt states: global clear and tick clear */
00984     #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear)
00985       *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc);
00986     #elif (defNumberOfTasks == 0)
00987       /* without tasks there is nothing to set. */
00988     #else
00989       /* well, we never come here. */
00990       #error "Parameter 'defInterruptStartFixed' is misspeld or incorrect (You should not arrive here)."
00991     #endif
00992   #endif
00993   /* Done, let the caller know where the stack ended. */
00994   return pStackTop; }
00995 #endif
00996 
00997 
00998 static TtaskControlBlock * privTcbList(Tuint08 uiTaskNumber)
00999 { /* This method may not be called with defCurrentTaskNumber any longer. Unfortunately we
01000    * cannot check if we violate that, because that may corrupt stack in privInitOs().
01001    * The location of the task control block is stored in flash, be careful, use the correct
01002    * instructions to retrieve it. */
01003   return portFlashReadWord(TtaskControlBlock *,pxTCBlist[uiTaskNumber]); }
01004 
01005 
01006 static Tuint08 privTaskNumber(Tuint08 uiTaskNumber)
01007 { /* Check if we are interested in the current task */
01008   if ((uiTaskNumber & defCurrentTaskMask) == defCurrentTaskNumber)
01009   { /* If yes, replace the uiTaskNumber with the current task number. */
01010     uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift; }
01011   return uiTaskNumber; }
01012 
01013 
01014 #if (defRegisterUseConstant == cfgFalse)
01015 
01016 static Tuint08 privRegisterCount(Tuint08 uiRegisterUse)
01017 { /* We want to count the number of register this uiRegisterUse byte represent. Each set bit
01018    * counts for four registers.  */
01019   /* uiCount accumulates the number of set bits */
01020   Tuint08 uiCount = 0;
01021   /* Loop  through all bits of the uiRegisterUse byte. Start with 'do' for the most optimal compiler result. */
01022   do
01023   { /* Increase the counter if the bit is set by four since every bit represents four registers. */
01024     if ((uiRegisterUse & 0x01) == 0x01) { uiCount+=4; }
01025     /* Shift to the next bit */
01026     uiRegisterUse >>= 1; }
01027   while (uiRegisterUse);
01028   /* When uiRegisterUse equals zero there are no bits left. We are done. */
01029   return uiCount; }
01030 
01031 #endif
01032 
01033 
01034 static void privTaskInit(Tuint08 uiTaskNumber, Tuint08 uiInitControl)
01035 { /* Tasks are initialized in this routine. Note the fields:
01036    * uxDelay, siQueuLock, uiLoadCollect, uiLoadTotal, uiStackMax, uiRegisterUse
01037    * are not cleared when we re-initialize the task. Only about uxDelay there could
01038    * be debate since the would indicate the last wake time. But what is a last wake
01039    * time in this case. We could set it to the current time, but have not done so.  */
01040   /* Report that we are initializing this task. */
01041   privTrace(traceTaskInit | uiTaskNumber);
01042   TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber);
01043   /* In case we might return here due to a watchdog bark or a restart or so. some old connections
01044    * must be closed when present. */
01045   #if (defReUseTaskInit == cfgTrue)
01046     /* And if asked for it. */
01047     if ((uiInitControl & defInitLockGetMask) == defInitLockRelease)
01048     { /* In case we use synchronization and the watchdog or want to be able to manually restart a task
01049        * it may be that the task that has to be re-initiated is holding some locks. These must be released properly
01050        * We need only to release locks on other tasks that are held by this task. Since waits cannot be non-blocking
01051        * it can safely be ignored here. Hmm, some other task may call restart on a blocking task on wait. So it seems
01052        * better to always clean when synchronization is used. */
01053       #if (cfgUseSynchronization != cfgSyncNon)
01054         /* we only need to check if the task contains a slot. */
01055         if (uiTaskNumber < defNumberOfTasksWithSlot)
01056         { /* We may only enter here if the particular task indeed has a slot stack, otherwise there is nothing clean
01057            * If we made use of priority lifting, we do not need to do anything. We can simply clean the slot stack. */
01058           privCleanSlotStack((TtaskExtendedControlBlock *) taskTCB );
01059           /* and any task that may run freely now will be release by the call below. Please note that is not possible
01060            * that is call releases the present task, since all its slots have been wiped. */
01061           #if ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))
01062             privReleaseSyncBlockingTasks();
01063           #endif
01064           }
01065       #endif
01066       /* At this point we must check if we have some kind of lock in the file system and we need
01067        * to release this and other tasks. Only include code if this is possible at all. */
01068       #if (cfgUseFileSystem == cfgTrue)
01069         /* We can simply try to close a file. If no lock is hold and no blocks are present this method returns
01070          * without any harm done. Of course it is more overhead, but we assume this part of the code is rarely executed
01071          * and therefore we do not want to invest the bytes. There are quite a number of situations in which the task
01072          * can be caught when killed. All these situations are handled by the method. */
01073         privPrepareFileClose(uiTaskNumber);
01074       #endif
01075     }
01076   #endif
01077   /* Read the priority the task will start with is contained in the lowest nibble this number, the start state
01078    * information is contained in the highest nibble. the interrupt state in both. While the handling for the
01079    * priority is simple, simply replace it or not, depending on the defInitStatusPrio, the start state
01080    * requires a more complex handling. Globally this routine provides the following services to the caller
01081    * (1) Load the start state from flash, this is the default action
01082    * (2) Load the start state from the uiInitControl, use defInitStatusCopyDo
01083    * (3) Load the start state from the uiInitControl, but allow for a fall back scenario for the default state
01084    * (4) Make sure that the start state is 'shared' instead of 'running' for shared tasks.
01085    * Apart from these facilities, it also provide a facility to set the shared state bit in
01086    * the OS status. If the task was running before, but is not running afterwards, this but must
01087    * be reset and vice versa.
01088    * First read the initial status from flash, or use a constant value. */
01089   #if (defInitialStatusConstant == cfgTrue)
01090     Tuint08 uiInitialStatus = defInitialStatusFixed;
01091   #else
01092     Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus);
01093   #endif
01094   /* We can call this with a predefined state or with a default state, in which case we want to restart
01095    * like we started at the beginning. */
01096   #if (defUseSharedStack == cfgTrue) || (includeTaskRestart == cfgTrue)
01097     /* Since the bit pattern of the default start state equals the one of the shared state, and since we
01098      * know we can never explicitly set the state to be shared from the outside (this is always a modified
01099      * running state) We must first test if we have a default state. */
01100     if ((uiInitControl & defInitialStartGetMask) == defRestartDefault )
01101     { /* We do not need to copy the start information from the uiInitControl if the caller asked for a default
01102        * If it is not equal, we leave leave the copy bit alone. Most of the time however, there will not
01103        * be a request for a copy, so this does nothing.*/
01104        uiInitControl =  (uiInitControl & defInitStatusCopySetMask) | defInitStatusCopyDont; }
01105   #endif
01106  /* If we have a suspend request, this will be honored at this place. This may override the previous
01107   * situation. Tasks cannot ignore a suspend request at this moment. Suspend requests are only possible
01108   * when genSuspend() is present: */
01109   #if (includeGenSuspend == cfgTrue)
01110     /* test if the request has been made */
01111     if ((taskTCB->defSusField & defSusGetMask) == defSusRequest)
01112     { /* if so, we must set the StatusCopy and the suspended restart: */
01113       uiInitControl =  (uiInitControl & (defInitStatusCopySetMask & defInitialStartSetMask)) | (defRestartSuspended | defInitStatusCopyDo);
01114       /* We do not need to clear the SusRequest separately since that is done below in
01115        * the clearance of the TaskMonitor. */ }
01116   #endif
01117   /* If we were running, and make use of a shared stack, we must inform the OS that we stopped a running
01118    * shared state (unless we continue to run, but that is not the default, and not possible in this routine)
01119    * This is however only needed if we are not actively are creating a context .*/
01120   /* If we make use of shared tasks we must inform the OS about the situation. */
01121   #if (defUseSharedStack == cfgTrue)
01122     /* And only if the current task is indeed a shared task */
01123     #if (defAllSharedStack == cfgFalse)
01124        /* If all tasks are shared this test is always true, so skip it. */
01125       if ((uiInitialStatus & defInitialSharedGetMask) == defInitialSharePresent)
01126     #endif
01127     { /* Retrieving the uiOsStatus is a costly operation, so lets work via a register
01128        *  gcc optimization does not recognize this.  */
01129       Tuint08 uiStatusCopy = uiOsStatus;
01130        /* If so, we must test if we want to activate a shared task, if so we must create a context (see below) */
01131       if ((uiInitControl & defInitSharedGetMask) == defInitSharedActive)
01132       { /* and set the shared bit of the OS status */
01133         uiStatusCopy = ((uiStatusCopy & defShareStateSetMask) | defShareStateRunning); }
01134       else
01135       { /* Task that are not rescheduled right now need no context */
01136         uiInitControl  = (uiInitControl & defInitContextSetMask) | defInitContextKeep;
01137         /* Shared tasks that are not scheduled for running may not be in the shared mode,
01138          * but be put to sleep or be suspended from the outside. The only way to recognize
01139          * such tasks is at the stack level being zero. Other way around, if the stack
01140          * level does not equal zero, it must have been running, or been put to sleep/suspend
01141          * block while running, and is holding the share state. Since we are to passivy this
01142          * state, we clear the bit.  */
01143         if (taskTCB->pcStackLevel != defStackEmpty)
01144         { /* If so, we are stopping the only running shared task, and must inform the OS
01145            * so it can select a new one. */
01146           uiStatusCopy = ((uiStatusCopy & defShareStateSetMask) | defShareStateAbsent);
01147           /* and make sure that the stack is empty so this is recognized */
01148           taskTCB->pcStackLevel = defStackEmpty; }
01149         /* This is the place to correct the situation where we have asked for a running state
01150          * but we are going to get into a shared state, since a shared tasks can only be
01151          * in the running state when we have a valid context, ie.e  on defInitContextRenew.
01152          * We need only to check the first bit of the state, since if it is set the second
01153          * must be set too, or if it is not, it is already in the shared state (what is a
01154          * defRestartDefault actually. */
01155         if ((uiInitControl & defBaseStopStateGetMask) == defBaseStopStateGo )
01156         { /* So we have requested for a defRestartRunning, change it to defRestartShared. */
01157           uiInitControl = (uiInitControl & defBaseModeSetMask) | defBaseModeShared; } }
01158         /* Dont forget to put the copy back */
01159         uiOsStatus = uiStatusCopy; }
01160     /* If we are putting a task to sleep, and that task happens to be a shared task, the sleep
01161      * instruction must be interpreted as a restart. However, this is only the case for shared
01162      * tasks. Therefore we have a special option to indicate this situation, which is only
01163      * uses for this special case. For regular tasks we want to leave this routine after
01164      * the specialties for shared tasks have been skipped.
01165      * Test if we have a low power sleep possibility */
01166     #if (cfgUseLowPowerSleep == cfgTrue)
01167       /* If all tasks are shared, we do not test for the defInitialSharePresent thus we also
01168        * not have an alternative branch. We simply always have to test if we want to leave. */
01169       #if (defAllSharedStack == cfgFalse)
01170         /* otherwise we will only perform the test below in case we are certain we have
01171          * a regular task. */
01172         else
01173       #endif
01174       { /* if we have a regular task, but we wanted to process shared tasks only we are done. */
01175         if ((uiInitControl & defInitProcessGetMask) == defInitProcessSharedOnly ) { return; } }
01176     #endif
01177   #endif
01178  /* The defInitStatus indicates if we want to prepare a new TaskStatus. This is needed upon first use, as it
01179   * contains the initial run state, its priority etc. The structure of the information is not identical to
01180   * the uiTaskStatus. The priority and start state are on the same location, but on the location if the lock bit,
01181   * the delay bit and the dress bit other information is stored. This must be corrected. For shared tasks, the
01182   * task starts in the shared mode. Upon first pass thats OK (no shared running, but if a running task is
01183   * modified to shared extra measures might be needed. In any case, a new or renewed task, is not blocked,
01184   * delayed of dominant, therefore we set those bits. */
01185   Tuint08 uiIntialFilteredStatus = (uiInitialStatus & (defBaseBlockStateSetMask & defBaseDelayStateSetMask & defBaseDressSetMask) ) | (defBaseBlockStateFree | defBaseDelayStateWake | defBaseDressDone);
01186   /* Depending on the fact if we are able to return here we can directly set its initial state, or
01187    * we must be more careful. In case there is the possibility to return here: */
01188   #if (defReUseTaskInit == cfgTrue)
01189     /* See what must be kept from the original status, and what must be read from flash. uiInitControl contains
01190      * a bit mask for those pieces we want to retain from the original task status. The default is to
01191      * read everything from flash except for the priority information. The start state may be overwritten
01192      * later on. */
01193     if ((uiInitControl & defInitStatusPrioGetMask) == defInitStatusPrioKeep)
01194     { /* If we indeed must replace the priority, do so in the uiIntialFilteredStatus */
01195       uiIntialFilteredStatus =  (uiIntialFilteredStatus  & defInitialPrioritySetMask) | (taskTCB->uiTaskStatus & defInitialPriorityGetMask); }
01196     /* In three situations we may ask for a copy of the start state from the uiInitControl parameter */
01197     #if (defUseSharedStack == cfgTrue) || (includeTaskRestart == cfgTrue) || ((cfgUseTaskWatchdog == cfgTrue) && (includeGenSuspend == cfgTrue))
01198       /* If we have a request for copying the start state from the control (this request may be from outside,
01199        * but is not valid for the a start state being defRestartDefault, in which case the copyDo was cleared
01200        * above */
01201       if ((uiInitControl & defInitStatusCopyGetMask) == defInitStatusCopyDo)
01202       { /* Replace the first start bits by the bits given in the control parameter. */
01203         uiIntialFilteredStatus = (uiIntialFilteredStatus & defBaseRestartSetMask) | (uiInitControl  & defBaseRestartGetMask); }
01204     #endif
01205   #endif
01206   /* Set the newly prepared  task status. */
01207   taskTCB->uiTaskStatus = uiIntialFilteredStatus;
01208   /* Fields uiTaskMonitor is initialized. Usually the init bytes are 0x00. If we
01209    * have no watchdog and no TaskRestart/TaskRecreate we can only arrive here one time. If the init fields are
01210    * 0x00 so we may rely upon the cleaning of the .bss section to reset these fields.
01211    * In other cases however we must explicitly clean/initialize those fields. */
01212   #if (defUseTaskMonitor == cfgTrue) && ((defReUseTaskInit == cfgTrue) || (defTaskMonitorInit != 0x00))
01213     taskTCB->uiTaskMonitor = defTaskMonitorInit;
01214   #endif
01215   /* Fields uiTasksLevels is initialized. Usually the init bytes are 0x00. If we
01216    * have no watchdog and no TaskRestart/TaskRecreate we can only arrive here one time. If the init fields are
01217    * 0x00 so we may rely upon the cleaning of the .bss section to reset these fields.
01218    * In other cases however we must explicitly clean/initialize those fields. */
01219   #if (defUseTaskLevels == cfgTrue) && ((defReUseTaskInit == cfgTrue) || (defTaskLevelsInit != 0x00))
01220     taskTCB->uiTaskLevels = defTaskLevelsInit;
01221   #endif
01222   /* If we keep track of the watermarks we renew them when we renew the priority */
01223   #if (defReUseTaskInit == cfgTrue) && (cfgCheckWatermarks == cfgTrue)
01224     /* ... Initializing the task may (requested through the call) require resetting those levels. */
01225     if ((uiInitControl & defInitStatusPrioGetMask) == defInitStatusPrioRenew)
01226     { /* If so, clean the StackMax level and the use of the registers. */
01227       taskTCB->uiStackMax = 0;
01228       taskTCB->uiRegisterUse = 0; }
01229   #endif
01230   /* We make a new context if this is a one time call, ... */
01231   #if (defReUseTaskInit == cfgTrue)
01232     /* ... or if we specially ask for it ... */
01233     if ((uiInitControl & defInitContextGetMask) == defInitContextRenew)
01234   #endif
01235   { /* Report that we are making the context of this task. */
01236     privTrace(traceCreateContext);
01237     /* pcStackOffset is a pointer to the beginning of the stack of this task, it is located in flash. */
01238     Taddress pcStackOffset = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Taddress,pcStackOffset);
01239     /* We need the place where the task starts. This is placed on the context as return address. It is located in flash */
01240     Taddress pTask = portFlashReadWord(Taddress,pxLooplist[uiTaskNumber]);
01241     /* Register compression requires information on which registers must be saved. Every bit stands for
01242      * four bits to save. If we make use of Register Compression with variable registers per task we must
01243      * load the information from flash. otherwise we just may use the constant here.
01244      * Here we count how may registers will be saved on the stack. For each register one byte will be reserved.
01245      * If all RegisterUse parameters are equal, we can calculate the number at compile time. */
01246     #if (defRegisterUseConstant == cfgTrue)
01247       /* The constant defRegisterCount holds the number of registers used.*/
01248       Tuint08 uiRegCount = defRegisterCount;
01249     #else
01250       /* The register use must be read from flash.  */
01251       Tuint08 uiRegisterUse = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiRegisterUse);
01252       /* We have to calculate the number dynamically */
01253       Tuint08 uiRegCount = privRegisterCount(uiRegisterUse);
01254     #endif
01255     /* We need the StackSize if we must clean the stack before use  */
01256     #if (defStackClean == cfgTrue)
01257       /* Get the reserved stack space from flash, or use the constant if all stack sizes are identical Note that, if we
01258        * don't check, we don;t need the uiStackSize information. It is simply assumed there is enough. */
01259       #if (defStackSizeConstant == cfgTrue)
01260         Tstack uiStackSize = defStackSizeFixed;
01261       #else
01262         Tstack uiStackSize = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tstack,uiStackSize);
01263       #endif
01264     #endif
01265     /* Clean the stack and set up the context. The method portInitContext makes room the return address.
01266      * the device status register and all registers used. */
01267     #if (cfgSysStackGrowthUp == cfgTrue)
01268       /* First fill the whole stack with the defStackInitByte if needed */
01269       #if (defStackClean == cfgTrue)
01270         Taddress pStackTop = pcStackOffset;
01271         while (uiStackSize--) { *(pStackTop++) = defStackInitByte; }
01272       #endif
01273       /* Subsequently define the context. */
01274       taskTCB->pcStackLevel = (Tstack) ((Tuint16) portInitContext(pTask, pcStackOffset, uiRegCount, uiInitialStatus) - (Tuint16) pcStackOffset);
01275     #else
01276       /* First fill the whole stack with the defStackInitByte if needed */
01277       #if (defStackClean == cfgTrue)
01278         Taddress pStackTop = pcStackOffset;
01279         while (uiStackSize--) { *(pStackTop--) = defStackInitByte; }
01280       #endif
01281       /* Subsequently define the context. */
01282       taskTCB->pcStackLevel = (Tstack) ((Tuint16) pcStackOffset - (Tuint16) portInitContext(pTask, pcStackOffset, uiRegCount, uiInitialStatus));
01283     #endif
01284   } }
01285 
01286 
01287 static void privEnterOS(Tuint08 uiAction)
01288 { /* This is the method to call to start the OS functions. Its is typically called from yield,
01289    * delay etc, but also from the switching api functions after the completed their task. The
01290    * action parameter states the intention of the caller. It contains whether or not we should
01291    * switch the task, and if a tick occurred.
01292    * Report that we are starting OS specific actions */
01293   privTrace(traceOsStart);
01294   /* Now is the time to permanently set the status of the system to OS. */
01295   uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateOs);
01296   /* First we check if no event has taken place. */
01297   #if (cfgUseEvents == cfgTrue)
01298   /* Handling events must take place in an protected environment. */
01299     #if (cfgIntOsProtected == cfgFalse)
01300       privDisableGlobalInterrupts();
01301     #endif
01302     /* Check if there are any event set */
01303     if (portEventRegister != defAllEventsReset)
01304     { /* OK, we have to handle the events and possibly deblock some tasks. If we invert the Event
01305        * register, we have '1' on all places that must be left alone, and '0' on those places where
01306        * the bits may be reset. */
01307       Tuint08 uiInvEvent = ~portEventRegister;
01308       /* Don't forget to reset the event register */
01309       portEventRegister = defAllEventsReset;
01310       /* Loop through all tasks possibly waiting on an event. */
01311       Tuint08 uiLoopTask;
01312       for (uiLoopTask=defTaskNumberEventBegin; uiLoopTask<defTaskNumberEventEnd; uiLoopTask++)
01313       { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
01314         /* Test if the task is really waiting on events */
01315         if (loopTCB->uiTaskEvents != defAllEventsReset)
01316         { /* reset all events that are fired */
01317           loopTCB->uiTaskEvents &= uiInvEvent;
01318           /* Check if there are any events left. */
01319           if (loopTCB->uiTaskEvents == defAllEventsReset)
01320           { /* If not, unblock this task. */
01321             privUnblockTask(uiLoopTask | defParaLockStateUnlock | defParaRetStateTrue);
01322             /* If we have unblocked a task, we make sure we switch tasks asap */
01323             /* TODO v0.90: If we are returning a full byte, it must not be possible to
01324              * switch the task since there will be only one return value register. */
01325             uiAction |=  defActionTaskStateSwitch; } } } }
01326     /* Done, restore interrupts if needed */
01327     #if (cfgIntOsProtected == cfgFalse)
01328       privEnableGlobalInterrupts();
01329     #endif
01330   #endif
01331   /* If we do not want to switch, we are quickly done. Note that time measurement is also
01332    * skipped, so, we may not skip this to often, often being a full round of the subticktimer.
01333    * Normally however, this is a one time action, because a regular entry over here due to
01334    * an interrupt will not skip this section. */
01335   if ((uiAction & defActionTaskGetMask) == defActionTaskStateSwitch)
01336   { /* If we make use of a file system we must check if there is a task waiting for a burn lock
01337            * to finish (this can only be one task at most. */
01338     #if (cfgUseFileSystem  ==  cfgTrue)
01339       /* Look if we have a burn lock active, if so there must be a write lock too, so we do not need to check that. */
01340       if ((uiFsStatus & defFsBurnBlockGetMask) == defFsBurnBlockActive)
01341       { /* If so we test if the file system is ready for a new byte to be written */
01342         if (portFSWriteReady())
01343         { /* Tell we may burn again. In this case we must clean the burn lock in the uiFsStatus, we do so below. */
01344           privTrace(traceBurnFree);
01345           /* Check if we are just waiting for a FS release. In this situation there is no task
01346            * actually writing, but one of the write tasks was somehow terminated, and the system
01347            * must wait with the release of new tasks until the burn block is gone. */
01348           if ((uiFsStatus & defFsReadBlockGetMask) == defFsReadBlockReleaseRequest)
01349           { /* Clear everything in the status register, except a possible request for sleep. */
01350             uiFsStatus = (uiFsStatus & defFsFreeSetMask) | defFsFree;
01351             /* There could be other tasks waiting to be released and perform file operations. Always, directly
01352              * after a task completed its file operations, it must call for a release of new tasks. There is
01353              * no automatic dispatch mechanism to activate the tasks independently. */
01354             privReleaseFileBlocks(); }
01355           else
01356           { /* We still have to clear the burn lock */
01357             uiFsStatus = ((uiFsStatus & defFsBurnBlockSetMask) | defFsBurnBlockClear);
01358             /* In this case we are in a simple file write operation, so extract the task
01359              * that was waiting for burning.  The Burn lock can not be active if no task is waiting,
01360              * which is essential since we have no task number indicating a 'free' state. We could check if that
01361              * task found has a write lock, but that should be the case */
01362             #if (defUseFsOnMultipleTasks == cfgTrue)
01363              /* In the case of multiple tasks, the task that is currently writing is in the status */
01364              Tuint08 uiLockTaskNumber =  (uiFsStatus & defFsWriteNumberGetMask) >> defFsTaskNumberShift;
01365             #else
01366              /* otherwise it is a fixed number determined by the preprocessor. */
01367              Tuint08 uiLockTaskNumber =  defUseFsSingleTaskNumber;
01368             #endif
01369             /* we must unblock the task, note that
01370              * this kind of block (burn block) cannot timeout, and thus the function does not return a value
01371              * to indicate this, we must leave the return register as it was. Naturally we keep the lock, for
01372              * there could be more bytes that must be written. It is not possible for a terminated task
01373              * to arrive here, since the taskKill routine will always try to close a lock on the file. */
01374             privUnblockTask(uiLockTaskNumber | defParaLockStateKeep | defParaRetStateNon); } } }
01375     #endif
01376     /* Well, let us first determine what kind of tick engine
01377      * we are running on. Since both types require quite a different approach the code is
01378      * completely separated. */
01379     #if (cfgUseEquidistantTicks == cfgTrue)
01380       /* We are running equidistant ticks. Thus the timer interrupt is set to a fixed value and
01381        * can only be read, but not changed. Also, the timer is of the type interrupt and clear, thereby
01382        * guaranteeing the most regular tick intervals. But if we are in a full cooperative mode
01383        * i.e. all tick interrupts masked either by disabling tick interrupts itself, of by disabling
01384        * global interrupts, we do not see ticks-interrupts any more and have to check manually. */
01385       #if (cfgIntManualTicks == cfgTrue)
01386         /* The check method returns true if a timer interrupt was due. The interrupt was also
01387          * cleaned and therefore we ask for a increase of the tick counter here. */
01388         if (portCheckTimer()) { uiAction |=  defActionTickStateTick; }
01389       #endif
01390       /* If we are checking timing issues or want to keep track of the load of the tasks we
01391        * must read the value timer. */
01392       #if ( (cfgCheckTiming == cfgTrue) || (cfgUseLoadMonitor == cfgTrue) )
01393         Tuint08 uiOsStartTime = portReadTimer();
01394       #endif
01395       /* If we want to monitor the load, some calculations are needed. Please note that this facility
01396        * is only available when cfgIntOsProtected == true. Otherwise there is simply no way get the
01397        * data to the os, since during this calculation it may be interrupted also. */
01398       #if (cfgUseLoadMonitor == cfgTrue)
01399         Tuint08 uiTaskTime;
01400         /* We want to calculate how much time the last task took. The subbyte contains the
01401          * last saved value of portReadTimer.*/
01402         Tuint08 uiSubByte = uxTickCount.SubByte;
01403         /* It is possible that we had an interrupt since we saved the the subByte. We assume,
01404          * that in that case uiSubByte>uiOsStartTime. This may not be true if we had a very
01405          * long interrupt but that cannot be checked. We cannot check one full round of the timer,
01406          * since we do not want to spend bytes to keep track of that.
01407          * If, however the timer has an unhandled interrupt, we expect the portReadTimer() function
01408          * to return a timer value with cfgSysSubTicksPerFullTick added to the actual value. This
01409          * is 'easy' to implement. */
01410         if (uiSubByte<=uiOsStartTime)
01411         /* If we have a very slow timer, we could arrive in the same subtick here, thus the equality
01412          * should result in the addition of zero subticks instead of cfgSysSubTicksPerFullTick. */
01413         { uiTaskTime = uiOsStartTime - uiSubByte; }
01414         /* So here we past the cfgSysSubTicksPerFullTick barrier and must correct for the situation. */
01415         else
01416         { uiTaskTime = cfgSysSubTicksPerFullTick - uiSubByte + uiOsStartTime; }
01417         /* If we have other interrupts at hand, the system may have spend some time there as well. We should
01418          * correct the uiTaskTime accordingly. Note that we cannot distinguish between different interrupts
01419          * of succeeding interrupts.  */
01420         #if (cfgIntUserDefined == cfgTrue)
01421           /* uiIsrLoadTemp holds the time spend in isr */
01422           Tuint08 uiIsrTime = uiIsrLoadTemp;
01423           /* This time however was erroneously incorporated in the task time, so we must correct for that error,
01424            * again, we assume no multiple tick interrupts in between. The test in between it to make sure the
01425            * value is indeed deducible, which may not be the case in rare cases */
01426           if (uiTaskTime > uiIsrTime) { uiTaskTime -= uiIsrTime; }
01427           /* Add it to the LoadCollect */
01428           uiIsrLoadCollect += uiIsrTime;
01429           /* We have read out the value of uiIsrLoadTemp and must reset it for the next interrupt. */
01430           uiIsrLoadTemp = 0;
01431         #endif
01432         /* Which task was running lately? */
01433         TtaskControlBlock * oldTCB = privTcbList(privTaskNumber(defCurrentTaskNumber));
01434         /* OK, this was the last task that did run, but ... did it run the very last slice or was it the
01435          * somewhat longer ago and did the idle time run after that? We can see the difference by looking
01436          * at uiAction variable. We did run a task (or isr) if we did not run the idle state. If we run the
01437          * idle state we must have come from privTickYield,  the RunState and running mode. We only count the time if the task is running and the state
01438          * is still runnable (may be delayed). Of course we miss the time of those tasks which just went
01439          * blocking. Since there is no other way to find out, we have to accept this (small) error. */
01440         if ((uiAction & defActionRunGetMask) == defActionRunStateTask)
01441         { /* The RunState is still set, so indeed the calculated time was due to the running of that task */
01442           oldTCB->uiLoadCollect += uiTaskTime; }
01443         else
01444         { /* Now the last task was the idle task, so add the time to the idle collect. Btw we could
01445            * also have been sleeping that difference cannot be made. However, it does not matter, since the
01446            * time from wakeup until now could count for idle as well. The sleep time itself is lost.*/
01447           uiIdleLoadCollect += uiTaskTime; }
01448       #endif
01449       /* Here we arrive at the first instruction when all checks are off. We increment the tick counter
01450        * if a timer interrupt has taken place. This is indicated the the uiAction parameter. Most times
01451        * we arrive here it is namely due to a yield of delay or so, and we can skip that step. */
01452       if ((uiAction & defActionTickStateTick) == defActionTickStateTick) { privIncrementTick(); }
01453       /* DISCUSSION
01454        * Sometimes the GCC compiler uses a frame pointer when there is not real need. This costs a lot
01455        * of flash. With the measure below we try to convince gcc that a particular register is free.
01456        * This seems portable since it is a request the compiler may ignore.
01457        * See the explanation at the option for more information. */
01458       #if (cfgSysFramePointerCounterMeasures == cfgTrue)
01459         register volatile Tuint08 uiAssignmentStatus asm ("r6");
01460       #else
01461         Tuint08 uiAssignmentStatus;
01462       #endif
01463       /* The two most important steps of the scheduler are, incrementing the tick counter, and
01464        * determining which task has to run next. The former is done above, and this action possible
01465        * waked some tasks, the latter below. The switch context results in a new task to run, or
01466        * the idle task that may or, or the sleep mode that the device must be put in. */
01467       uiAssignmentStatus = privSwitchContext();
01468       /* First, we check if we are put to sleep, which is done in OS space btw. If we are to sleep,
01469        * we are not allowed to prepare a subbyte for timing measurement, since that variable is used
01470        * to pass the number of allowable tick blocks to sleep.*/
01471       #if (cfgUseLowPowerSleep == cfgTrue)
01472         if ((uiAssignmentStatus & defAssignmentSleep) == defAssignmentSleep)
01473         { /* We indeed have to go to low power sleep. Enter the sleep mode, we do not return from this call. */
01474           privEnterSleep(uxTickCount.SubByte); }
01475       #endif
01476       /* If we arrive here, we are left with two possibilities, either we go run a task or go to the
01477        * idle state. First however, we measure how long the OS took, since most tasks of the OS are
01478        * done by this time. */
01479       #if ((cfgCheckTiming == cfgTrue) || (cfgUseLoadMonitor == cfgTrue))
01480         Tuint08 uiOsTime;
01481         /* Read the timer again. */
01482         Tuint08 uiOsStopTime = portReadTimer();
01483         /* From the moment we filled uiOsStartTime until now, that is the time the
01484          * OS took to run. This may not even be one subtick, so the we must test for equality too. (i.e..
01485          * both times being equal probably means not even one subtick, contrary to a full round of the
01486          * subtick timer */
01487         if (uiOsStartTime <= uiOsStopTime)
01488         { uiOsTime = uiOsStopTime - uiOsStartTime; }
01489         else
01490         { /* Again, in this case the timer interrupt fired, but is not handled yet. Hmmm, I am not
01491            * even sure id this situation can happen, since this should be a hanging interrupt right?
01492            * Well anyway, it seems save to leave the calculation in anyway */
01493           uiOsTime = cfgSysSubTicksPerFullTick - uiOsStartTime + uiOsStopTime; }
01494         /* If you already used half of the tick here, there is probably not enough time to complete
01495          *  the following task. Let us calculate that situation. */
01496         #if (cfgCheckTiming == cfgTrue)
01497           /* Since this may incidentally happen, and that does not cause problems
01498            * we calculate the continuous 4 sample average. Depending on how large that number can be
01499            * we need a 8 or 16 bit variable. */
01500           #if (cfgSysSubTicksPerFullTick >= 64)
01501             Tuint16   uiOsLocalTimeAverage = ((Tuint16)uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2);
01502           #else
01503             Tuint08   uiOsLocalTimeAverage = (uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2);
01504           #endif
01505           /* The average value however should not exceed the a one byte value */
01506           uiOsTimeAverage = (Tuint08) (uiOsLocalTimeAverage >> 2);
01507           /* If we have tracing activated, we report the actual timing and the averaged timing.
01508            * when they are close to the border*/
01509           #if (cfgCheckTrace == cfgTrue)
01510             if (uiOsTimeAverage > (3*cfgSysSubTicksPerFullTick/8))
01511             { privTrace(traceOsTime);
01512               privTrace(uiOsTime);
01513               privTrace(uiOsTimeAverage); }
01514             #endif
01515           /* If this value exceeds have the tick time, this should be reported. */
01516           if (uiOsTimeAverage >= (cfgSysSubTicksPerFullTick / 2))
01517           { /* This is a fatal error since it makes no sense to continue as this issue
01518              * will rise again and again. */
01519             privShowError((fatOsTickRateTooHigh | errTaskStateNon), callIdSystem, errNoInfo ); }
01520         #endif
01521         #if (cfgUseLoadMonitor == cfgTrue)
01522           /* Having calculated the time spend in the OS, add it to the Collected times. */
01523           uiOsLoadCollect += uiOsTime;
01524           /* In order to make the next time measurement possible save the last value read
01525            * from the timer in the Subbyte. That will be used to calculate the time spend in
01526            * a particular task. Of course we make a small error because the next few instructions
01527            * are not really part of the task. However, the same applies to the instructions
01528            * after the context save so we assume (hope?) these errors cancel out. */
01529           uxTickCount.SubByte = uiOsStopTime;
01530         #endif
01531       #endif
01532       /* Now check if we must perform an idle task */
01533       if ((uiAssignmentStatus & defAssignmentIdle) == defAssignmentIdle)
01534       { /* if so, call for the EnterIdle, from which we do not return */
01535         privEnterIdle(); }
01536       /* Here we are done, the next thing is to enter the privEnterTask, in order to run
01537        * the task chosen */
01538     #else
01539       /* We are running in the true time slice mode. Good choice, now you are sure that no tasks
01540        * are being starved if more are running full swing in one priority. OK, so far for the
01541        * advertisements. The timer interrupt is variable in this mode, and reset every time a task,
01542        * or the os starts. Note that the interrupts may be (and are) irregular. Ticks, however,
01543        * are on average, in pace with the system clock.  */
01544       Tuint08 uiTaskTime;
01545       /* Determining how long the task took is a snap, since the timer was reset just before
01546        * the task started. And, we must also specify how long we want to wait before the next
01547        * timer interrupt. Now that does not matter much here, since (tick) interrupts are disabled
01548        * anyhow. */
01549       uiTaskTime = portReadAndResetTimer(defSubTicksMax);
01550       /* We assume the timer did not overflow. This may happen if you choose cfgSysSubTicksPerFullTick
01551        * too high. But you can easily check this, see below. In this timer model the subbyte collects
01552        * the subticks, from which the tick counter is driven. */
01553       uxTickCount.SubByte += uiTaskTime;
01554       /* If we keep track of the task times, the only thing to do is to add uiTaskTime to the collector */
01555       #if (cfgUseLoadMonitor == cfgTrue)
01556         /* If we have other interrupts at hand, the system may have spend some time there as well. We should
01557          * correct the uiTaskTime accordingly. Note that we cannot distinguish between different interrupts
01558          * of succeeding interrupts.  */
01559         #if (cfgIntUserDefined == cfgTrue)
01560           /* uiIsrLoadTemp holds the time spend in isr */
01561           Tuint08 uiIsrTime = uiIsrLoadTemp;
01562           /* This time however was erroneously incorporated in the task time, so we must correct for that error,
01563            * again, we assume no multiple tick interrupts in between. The test in between it to make sure the
01564            * value is indeed deducible, which may not be the case in rare cases */
01565           if (uiTaskTime > uiIsrTime) { uiTaskTime -= uiIsrTime; }
01566           /* Add it to the LoadCollect */
01567           uiIsrLoadCollect += uiIsrTime;
01568           /* We have read out the value of uiIsrLoadTemp and must reset it for the next interrupt. */
01569           uiIsrLoadTemp = 0;
01570         #endif
01571         /* Which task was running lately? */
01572         TtaskControlBlock * oldTCB = privTcbList(privTaskNumber(defCurrentTaskNumber));
01573         /* OK, this was the last task that did run, but ... did it run the very last slice or was it the
01574          * somewhat longer ago and did the idle time run after that? We can see the difference by looking
01575          * at uiAction variable. We did run a task (or isr) if we did not run the idle state. If we run the
01576          * idle state we must have come from privTickYield,  the RunState and running mode. We only count the time if the task is running and the state
01577          * is still runnable (may be delayed). Of course we miss the time of those tasks which just went
01578          * blocking. Since there is no other way to find out, we have to accept this (small) error. */
01579         if ((uiAction & defActionRunGetMask) == defActionRunStateTask)
01580         { /* The RunState is still set, so indeed the calculated time was due to the running of that task */
01581           oldTCB->uiLoadCollect += uiTaskTime; }
01582         else
01583         { /* Now the last task was the idle task, so add the time to the idle collect. Btw we could
01584            * also have been sleeping that difference cannot be made. However, it does not matter, since the
01585            * time from wakeup until now could count for idle as well. The sleep time itself is lost.*/
01586           uiIdleLoadCollect += uiTaskTime; }
01587       #endif
01588       /* in this timing model we always call privIncrementTick() since the ticks added to the tick counter
01589        * are deduced from the value of subbyte */
01590       privIncrementTick();
01591       /* DISCUSSION
01592        * Sometimes the GCC compiler uses a frame pointer when there is not real need. This costs a lot
01593        * of flash. With the measure below we try to convince gcc that a particular register is free.
01594        * This seems portable since it is a request the compiler may ignore.
01595        * See the explanation at the option for more information. */
01596       #if (cfgSysFramePointerCounterMeasures == cfgTrue)
01597         register volatile Tuint08 uiAssignmentStatus asm ("r6");
01598       #else
01599         Tuint08 uiAssignmentStatus;
01600       #endif
01601       /* The two most important steps of the scheduler are, incrementing the tick counter, and
01602        * determining which task has to run next. The former is done above, and this action possible
01603        * waked some tasks, the latter below. The switch context results in a new task to run, or
01604        * the idle task that may or, or the sleep mode that the device must be put in. */
01605       uiAssignmentStatus = privSwitchContext();
01606       /* First, we check if we are put to sleep, which is done in OS space btw. If we are to sleep,
01607        * we are not allowed to prepare a subbyte for time measurement, since that variable is used
01608        * to pass the number of allowable tick blocks to sleep.*/
01609       #if (cfgUseLowPowerSleep == cfgTrue)
01610         if ((uiAssignmentStatus & defAssignmentSleep) == defAssignmentSleep)
01611         { /* We indeed have to go to low power sleep. Enter the sleep mode, we do not return from this call. */
01612           privEnterSleep(uxTickCount.SubByte); }
01613       #endif
01614       /* If we arrive here, we are left with two possibilities, either we go run a task or go to the
01615        * idle state, Now check if we must perform an idle task .*/
01616       if ((uiAssignmentStatus & defAssignmentIdle) == defAssignmentIdle)
01617       { /* if so, first set the Timer with the right time for the idle task, which at the same time
01618          * returns the time spend so far in OS */
01619         Tuint08 uiOsTime = portReadAndResetTimer(TimeSliceIdleTime);
01620         /* This is of course time spend, so must be added to the tick counter */
01621         uxTickCount.SubByte += uiOsTime;
01622         /* If we monitor the load */
01623         #if (cfgUseLoadMonitor == cfgTrue)
01624           /* the time spend in OS must be added to the appropriate collector */
01625           uiOsLoadCollect += uiOsTime;
01626         #endif
01627         /* End we may enter the idle task. In this case we do not have the opportunity to check
01628          * upon the OS time. */
01629         privEnterIdle(); }
01630       /* Arriving here means we are going to perform a regular task. So we must prepare the timers
01631        * for that.*/
01632       Tuint08 uiTimeSlice;
01633       /* We have two options. Either we have specified the time slice for each task separately, or
01634        * we use the one fits all model. */
01635       #if (defTimeSliceConstant == cfgFalse)
01636         /* Get the task number we choose to run next */
01637         Tuint08 uiTaskNumber = privTaskNumber(defCurrentTaskNumber);
01638         /* Get the value of the time slice from flash */
01639         uiTimeSlice   = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiTimeSlice);
01640       #else
01641         /* In the standard model, we give all tasks the same value, */
01642         uiTimeSlice = defTimeSliceFixed;
01643       #endif
01644       /* The time has come to determine how long the OS has run, reset the timer and set the new time slice */
01645       Tuint08 uiOsTime = portReadAndResetTimer(uiTimeSlice);
01646       /* The time spend in the OS must be added to the extended tick counter */
01647       uxTickCount.SubByte += uiOsTime;
01648       /* And, if we monitor the load ... */
01649       #if (cfgUseLoadMonitor == cfgTrue)
01650         /* to the collector of the OS load time as well */
01651         uiOsLoadCollect += uiOsTime;
01652       #endif
01653       /* Many things can go wrong with the timing, so the moment has come to check. */
01654       #if (cfgCheckTiming == cfgTrue)
01655         /* First, check if the time spend in the OS does not exceed one tick. Although it is not an error in the
01656          * strictest sense, it can do harm to the collected subticks in the subbyte, which may overflow, since
01657          * it cannot be cleaned on this point. Since this may incidentally happen, and that does not cause
01658          * problems we calculate the continuous 4 sample average.  */
01659         #if (cfgSysSubTicksPerFullTick >= 64)
01660           Tuint16   uiOsLocalTimeAverage = ((Tuint16)uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2);
01661         #else
01662           Tuint08   uiOsLocalTimeAverage = (uiOsTimeAverage + uiOsTimeAverage + uiOsTimeAverage + uiOsTime + 2);
01663         #endif
01664         /* The average value however should not exceed the a one byte value */
01665         uiOsTimeAverage = (Tuint08) (uiOsLocalTimeAverage >> 2);
01666         /* If we have tracing activated, we report the actual timing and the averaged timing.
01667          * when they are close to the border*/
01668         #if (cfgCheckTrace == cfgTrue)
01669           if (uiOsTimeAverage > (3*cfgSysSubTicksPerFullTick/8))
01670           { privTrace(traceOsTime);
01671             privTrace(uiOsTime);
01672             privTrace(uiOsTimeAverage); }
01673         #endif
01674         /* If this value exceeds have the tick time, this should be reported. */
01675         if (uiOsTimeAverage >= (cfgSysSubTicksPerFullTick / 2))
01676         { /* This is a fatal error since it makes no sense to continue as this issue
01677            * will rise again and again. */
01678           privShowError((fatOsTickRateTooHigh | errTaskStateNon), callIdSystem, errNoInfo ); }
01679         /* Second, we test if there is enough room for the current time slice, i.e. if the subtick counter will
01680          * not overflow, even if the task runs its full length. The comparison seams awkward, but makes use
01681          * of the overflow of the unsigned integers arithmetic. */
01682         if (((Tuint08)(uiTimeSlice + uxTickCount.SubByte)) < uiTimeSlice)
01683         { /* if it does not fit report the error, we are in OS space and return automatically.  */
01684           privShowError((errTaskTakesTooLong | errTaskStateCurrent | errOsStateAsIs), callIdSystem, errCurrentTask  );
01685           /* We have a problem, since the task is put in error mode we cannot start it any more, that
01686            * would violate assumptions on running tasks. But we cannot select an other task at this point either.
01687            * The only senseable thing to do seems like launching the idle task, just for one slice.
01688            * Of course we must add the subticks already past */
01689           uxTickCount.SubByte += portReadAndResetTimer(TimeSliceIdleTime);
01690           /* and start the idle task: */
01691           privEnterIdle(); }
01692       #endif
01693       /* Here we are done, the next thing is to enter the privEnterTask, in order to run
01694        * the task chosen */
01695    #endif
01696   }
01697   /* make it so */
01698   privEnterTask(); }
01699 
01700 
01701 static void privIncrementTick(void)
01702 { /* This the place where the tick counter is increased. This may be one or more ticks, depending of
01703    * the timing model in use. */
01704   #if (cfgUseEquidistantTicks == cfgFalse)
01705     /* Only in case we have true time slice model, more than one tick may be needed to add. For every
01706      * tick we fully loop the routine below. This is a little inefficient, but ensures we do not miss
01707      * a tick in the delay/wake section. If we do, we may miss to reactivate a delay task. */
01708     while (uxTickCount.SubByte >= cfgSysSubTicksPerFullTick)
01709   #endif
01710   { /* In case of true time slicing we use the SubByte to collect all subticks, otherwise there is
01711      * no need */
01712     #if (cfgUseEquidistantTicks == cfgFalse)
01713       uxTickCount.SubByte -= cfgSysSubTicksPerFullTick;
01714     #endif
01715     /* Every time we arrive here we must increase the tick counter by one. Or we arrived here because
01716      * subbyte overflowed the cfgSysSubTicksPerFullTick or we arrived here because we were send here
01717      * from a tick interrupt. */
01718     ++uxTickCount.LowByte;
01719     /* Call the tick hook. Please note that at this point the tick counter is incomplete, */
01720     #if (callAppTick00 == cfgTrue)
01721       appTick00();
01722     #endif
01723 
01724     /* if the low byte overflows ... */
01725     if (uxTickCount.LowByte == 0 )
01726     { /* ... Increase the high byte */
01727       ++uxTickCount.HighByte;
01728       /* every 256 ticks we must call the tick08 hook */
01729       #if (callAppTick08 == cfgTrue)
01730         appTick08();
01731       #endif
01732       /* Once ever 256 ticks let us adjust the Os stack watermark */
01733       #if (cfgCheckWatermarks == cfgTrue) && (cfgSysReuseOsStack == cfgFalse)
01734         privCheckOsStackRegion();
01735       #endif
01736       /* If we have tracing activated we must regularly inform the outside world about
01737        * the status of the watermarks and registeruse. */
01738       #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue)
01739         if ((uxTickCount.HighByte & 0x03) == 0) { privTraceWatermarks(); }
01740       #endif
01741       /* every 256*256 ticks we must call the appTick16 hook */
01742       #if (callAppTick16 == cfgTrue)
01743         if (uxTickCount.HighByte == 0) { appTick16(); }
01744       #endif
01745       /* On every cross of the low byte boundary, new tasks may have entered the near wake
01746        * state, thus we must set the corresponding bit. */
01747       uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStatePresent);
01748       /* If we make use of the watchdog facility */
01749       #if (cfgUseTaskWatchdog == cfgTrue)
01750         /* we test if we already arrived at a watchdog decrease boundary. This test simple passes when
01751          * the HighByte ends with the number of zeros given by  cfgNumWatchdogDiv */
01752         if ( (uxTickCount.HighByte & (0xFF >> (8 - cfgNumWatchdogDiv))) == 0)
01753         { /* if so, now loop trough all tasks who might have a watchdog */
01754           Tuint08 uiLoopTask;
01755           for (uiLoopTask=defTaskNumberWatchdogBegin; uiLoopTask<defTaskNumberWatchdogEnd; uiLoopTask++)
01756           { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
01757             /* We may only decrease the watchdog state of running tasks, If a task is delayed, suspended
01758              * or otherwise blocked, it can never feed its watchdog so we should not decrease its counter. */
01759             if (loopTCB->uiTaskStatus >= defBaseRunningTask)
01760             { /* Extract the value of the watchdog counter which can only take four states */
01761               Tuint08 uiWatchdog = loopTCB->defDogField & defDogGetMask;
01762               /* The value of zero (defDogDead) means we do not use the watchdog, the value one (defDogBark) means
01763                * it is already barking, so it should not be decreased further rendering it inactive,*/
01764               if (uiWatchdog > defDogBark)
01765               { /* so only on higher values, decrease its value, which may result in a barking watchdog */
01766                 loopTCB->defDogField = (loopTCB->defDogField & defDogSetMask) | (uiWatchdog - defDogDec); } } } }
01767       #endif
01768       /* If we make use of the load monitor facility */
01769       #if (cfgUseLoadMonitor == cfgTrue)
01770         /* we test if we already arrived at a watchdog monitor boundary. This test simple passes when
01771          * the HighByte ends with the number of zeros given by  cfgNumMonitorDiv, we must
01772          * copy all values from the collectors to the totals. */
01773         if ( (uxTickCount.HighByte & (0xFF >> (8 - cfgNumMonitorDiv))) == 0) { privCopyLoad(); }
01774       #endif
01775     /* This concludes the activity on the high byte (256 ticks) boundary */
01776     }
01777     /* Let us check the OS stack on this place, just to be sure. */
01778     #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
01779       privCheckOsStackLevel();
01780     #endif
01781     #if (defUseDelay == cfgTrue)
01782       /* Now we must check if we must wake some delayed tasks, this only is the case when we
01783        * have near wakes. The mechanism of near wakes is made in order not to have to check all
01784        * tasks on every tick */
01785       if ((uiOsStatus & defNearWakeStateGetMask) == defNearWakeStatePresent)
01786       { Tuint08 uiNearWakeCount = 0;
01787         Tuint08 uiLoopTask;
01788         /* we must find out which tasks are near wake, thus we must check all tasks */
01789         for (uiLoopTask=defTaskNumberDelayBegin; uiLoopTask<defTaskNumberDelayEnd; uiLoopTask++)
01790         { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
01791           /* We check if the task is delayed. Note that it need not to be running. If the task was suspended or
01792            * sleeping while delayed, this delay simply ends, but the task remains suspended/sleeping. The same
01793            * applies for killed tasks. Tasks that are blocked may have a timeout, so this must be checked as well. */
01794           if ((loopTCB->uiTaskStatus & defBaseDelayStateGetMask) == defBaseDelayStateDelayed)
01795           { /* a task from which the high byte does not much is not near wake */
01796             if (loopTCB->uxDelay.HighByte == uxTickCount.HighByte)
01797             { /* if it matches, we assume that the task must be waked, since we do not allow so long delays that the
01798                * end time is, after overflow, just before the current time in the same block. (The need for this has
01799                * several reasons, one of which is that this allows us to skip ticks within a tick block. It is needed
01800                * for recovery from low power sleep too. The former facility is not really used right now */
01801               if (loopTCB->uxDelay.LowByte <= uxTickCount.LowByte)
01802               { /* We have a winner, we may give a wakeup call, but first we check synchronization */
01803                 privWakeupFromDelay(uiLoopTask,loopTCB); }
01804               else
01805               { /* arriving here means the particular task has a wake time in the near future, thus we may
01806                  * increase the near wake counter. */
01807                 ++uiNearWakeCount; } } } }
01808         /* If we had no more near wakes we may clear the near wake bit. If we do not do this, it remains set, as
01809          * it was for certain when arriving here. If we clear the bit will be set again when we enter the
01810          * next tick block. */
01811         if (uiNearWakeCount==0) { uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStateAbsent); } }
01812     #endif
01813   } }
01814 
01815 
01816 static Tselect privSelectTask(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd)
01817 { Tselect result;
01818   Tuint08 uiLoopTask;
01819   /* Since this is on if the deeper calls, we perform an OS stack check here when timing
01820    * is activted, (without timing activated, it is done inside privTcbList. */
01821   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
01822     privCheckOsStackLevel();
01823   #endif
01824   /* Tselect contains of (MaxTask,Prio) MaxTask is variable which will hold the task with the highest
01825    * priority. The Prio is* the important variable. It is set to the highest priority of a non runnable task,
01826    * the default so to say. If it is not increased, we have no runnable task, thus we must run the idle task.
01827    * By using this approach we do not need a real idle task, with stack and so. */
01828   result.MaxStatus = defBaseRunningTask - 1;
01829   /* Now loop trough all tasks and determine which has the highest priority. Since the
01830    * priority information is just before the bit 0, which determines the run state, all
01831    * tasks with equal priority are issued round robin. Tasks with a status below defRunableTask
01832    * will never be started, they are typically the blocked, delayed suspended, error or
01833    * sleeping tasks. */
01834   for (uiLoopTask=uiLoopStart; uiLoopTask<uiLoopEnd; uiLoopTask++)
01835   { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
01836     Tuint08 uiCompareStatus = loopTCB->uiTaskStatus ^ uiFlipMask;
01837     /* The 'smaller sign' is important here, only if the priority exceeds the smallest possible
01838      * runnable task a replacement must take place. */
01839     if (result.MaxStatus < uiCompareStatus)
01840     { /* this is the most important task so far */
01841       result.MaxTask = uiLoopTask;
01842       /* and this is its priority */
01843       result.MaxStatus = uiCompareStatus;  } }
01844   /* Return the result. Note that MaxTask in not initialized when there where not runnable tasks.  */
01845   return result; }
01846 
01847 #if (cfgUseHierarchicalRoundRobin == cfgTrue)
01848   static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiPriority, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip)
01849 #else
01850   static void privMakeTasksRunable(Tuint08 uiFlipMask, Tuint08 uiLoopStart, Tuint08 uiLoopEnd, Tbool bCheckSkip)
01851 #endif
01852 { Tuint08 uiLoopTask;
01853   /* In fact we need only to restore the run state of the current priority. It
01854    * is however more work to check the priority, and it does not matter that we
01855    * reset the run states of all lower priorities as well. Since  it could matter in
01856    * special cases where one particular lower priority task revives higher priority
01857    * tasks, which, when done, get to the same lower priority task every time,
01858    * thereby skipping the other lower priority tasks. If this is the case choose
01859    * for hierarchical round robin. However, we must take care  that only running
01860    * tasks are handled. For the blocking tasks the dressbit is used for other
01861    * purposes. */
01862   for (uiLoopTask=uiLoopStart; uiLoopTask<uiLoopEnd; uiLoopTask++)
01863   { /* So get the task ... */
01864     TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
01865     /* Test if may have a blocking of some kind. */
01866       /* If so make sure the present task is not blocked. */
01867       if ((bCheckSkip) || ((loopTCB->uiTaskStatus ^ uiFlipMask) >= defBaseNoBlocksTask))
01868       { /* ... and set the RunState to Runable If we make use of HierarchicalRoundRobin */
01869         #if (cfgUseHierarchicalRoundRobin == cfgTrue)
01870         /* We may only reset those tasks in the requested priority. (Note we test the shifted priority because it
01871          * is faster on this side and on the callers side.) */
01872         if ((loopTCB->uiTaskStatus & defBasePrioGetMask) == uiPriority)
01873         #endif
01874         { loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBaseDressSetMask) | defBaseDressRunable; } } } }
01875 
01876 
01877 static Tuint08 privSwitchContext(void)
01878 { /* We arrive here when we may want to do a context switch. If we do not want to switch, we are
01879    * always running a task, so that is the default return state. Here we fill in in, so we can
01880    * modify it to our needs. */
01881   Tuint08 uiContextResult = defAssignmentTask;
01882   /* ... determine which task was running */
01883   #if (cfgUseTaskWatchdog == cfgTrue) ||  (includeTaskProtectSwitchTasks == cfgTrue) || (includeTaskProtectSwitchCritical == cfgTrue)
01884     Tuint08 uiOldTaskNumber = privTaskNumber(defCurrentTaskNumber);
01885     TtaskControlBlock * oldTCB = privTcbList(uiOldTaskNumber);
01886   #endif
01887   /* DISCUSSION
01888    * Where to put he watchdog check was a matter of considerable inner debate of mine. Is the
01889    * privSwitchContext the right place? In any case, we must not do it after we determined
01890    * what the next task to run will be. We could have done it in the privInitOS, probably it
01891    * results in smaller code over there. On the other side, we want to keep privInitOS as lean as
01892    * possible by itself. We could have done it in privSwitchContext since there is no need
01893    * to check broken tasks after. In any case, a call to privTaskInit eats a lot of stack so
01894    * we want to do it at a place where the stack is fresh. Anyway i decided that this place
01895    * is not that bad. */
01896   /* If we make use of the watchdog ... */
01897   #if (cfgUseTaskWatchdog == cfgTrue)
01898     /* check if the watchdog barked (in this way we make sure we only check tasks which
01899      * actually did run, so at least had a chance to feed the watchdog. A blocking task
01900      * therefore should not be barking. Exception is, if the task just called for some
01901      * unavailable slot, and went blocking as last action. Likewise, the task could also be
01902      * delayed as last action (the task normally is started with bark activated as last
01903      * action, since bark is only checked upon tasks that have run.)  Furthermore, a
01904      * terminated task can only issue a bark if it was not stuck but running without
01905      * watchdog feeding. This situation is extremely rare, but not impossible. Such a
01906      * task should stay terminated. End of the story, we have to recheck if the task is
01907      * indeed still running. Test if the task is running and if the watchdog barks. */
01908     if ( (oldTCB->uiTaskStatus >= defBaseRunningTask) && ((oldTCB->defDogField & defDogGetMask) == defDogBark))
01909     { /* Report that the watchdog is barking. The task number can be deduced from the following TaskInit report. */
01910       privTrace(traceWatchdog);
01911       /* If so get the pointer to the bark code from flash, and call it, if there is something to call.
01912        * We first call the Bark and reinitialize afterwards in order to process suspend requests,
01913        * from the bark routine. This further has the advantage that the bark routine may inspect
01914        * the malicious task, before it is erased. */
01915       #if (callAppBark == cfgTrue)
01916         portFlashReadWord(fpBarkTask,pxBarklist[uiOldTaskNumber-defTaskNumberWatchdogBegin])();
01917       #endif
01918       /* Re-initialize the task, (but keep its priority). The rest of the state will be restored as if
01919        * the task was created for the first time. All hold locks will be released, blocking and delays
01920        * are removed, with out exception. The task could be in block waiting for the burnbock to release.
01921        * We want to keep the status partially (priority etc) and the watermarks. Shared tasks are put
01922        * back in the pool of waiting shared tasks. */
01923       privTaskInit(uiOldTaskNumber,(defInitContextRenew | defInitStatusCopyDont | defInitSharedPassive | defInitStatusPrioKeep | defInitLockRelease | defInitProcessAll));
01924       /* In case we check timing we must inform the caller that we may have spend considerable time in
01925        * the watchdog routine. Since this is a one time event (or should be rare at least, is is better
01926        * if it not triggers an error. */
01927       #if (cfgCheckTiming == cfgTrue)
01928         uiContextResult |= defAssignmentWatchdog;
01929       #endif
01930     }
01931   #endif
01932   /* ... its state to done. If we previously run the idle task, the old task is already
01933    * done, but that does not matter. Since the dress runnable bit has only the runnable meaning
01934    * when the state is indeed not blocked (running, may be delayed) we may only set it to
01935    * done in that case. In blocking situations the bit is used for different purposes.
01936    * So there tasks must be skipped. We can save some bytes if there is no possibility for
01937    * blocking states. */
01938   /* TODO v0.90 Calls on the heap may block also */
01939   /* If we have shared tasks in our midst, ...  */
01940   #if (defUseSharedStack == cfgTrue)
01941     Tselect uiShareSelect;
01942     /* ... we must check if one of them is actually running. */
01943     if ((uiOsStatus & defShareStateGetMask) == defShareStateAbsent)
01944     { /* If this is not the case, we must select a new task from the shared tasks pool. This
01945        * selection is based on round robin among all shared tasks, where we always select
01946        * the one with the highest priority. This selection equals the regular task selection
01947        * scheme, with only the access bit being different. (The bit being zero on shared
01948        * tasks. By flipping this bit in the selection we get the correct shared task.*/
01949       uiShareSelect = privSelectTask((defBaseSharedGetMask & ~defBaseSharedTask),defTaskNumberSharedBegin,defTaskNumberSharedEnd);
01950       /* Now, we must see if there is indeed one that can be scheduled. Note that shared
01951        * tasks can be delayed as regular tasks, and they are not selected as long as this
01952        * delay is not over. The MaxStatus contains the states of the selected task with (!)
01953        * the flipped bit. So it looks like a stated that is able not blocked and not delayed.
01954        * Therefore compare with the highest state that could not run, if there are unequal,
01955        * we have a truly runnable state. */
01956       if (uiShareSelect.MaxStatus != (defBaseRunningTask - 1))
01957       { /* OK, among the shared tasks we have a winner! This task will be promoted to running
01958          * later on, get the control block of that task. */
01959         TtaskControlBlock * shareTCB = privTcbList(uiShareSelect.MaxTask);
01960         /* But it could be that all shared tasks have completed their round robin, We have
01961          * to test this and that can be easily seen from the dressing. Only if all shared
01962          * states are defBaseDressDone, one of them get selected. */
01963         if ((shareTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressDone)
01964         { /* In fact we need only to restore the run state of the shared states of the current
01965            * priority. We do so if we have cfgUseHierarchicalRoundRobin activated, mostly however
01966            * it does not matter much, so e save the code. See also the discussion below.
01967            * We do have to take care that we only reset shared states however */
01968           #if (cfgUseHierarchicalRoundRobin == cfgTrue)
01969             privMakeTasksRunable((defBaseSharedGetMask & ~defBaseSharedTask),(shareTCB->uiTaskStatus & defBasePrioGetMask),defTaskNumberSharedBegin,defTaskNumberSharedEnd,(defDenseSharedStack == cfgTrue));
01970           #else
01971             privMakeTasksRunable((defBaseSharedGetMask & ~defBaseSharedTask),defTaskNumberSharedBegin,defTaskNumberSharedEnd,(defDenseSharedStack == cfgTrue));
01972           #endif
01973         }
01974         /* Ok, now we must initialize the context for this new state, keeping everything else as is. The
01975          * initialization in the function taskes also case of activation of the shared state in the uiOsStatus */
01976         privTaskInit(uiShareSelect.MaxTask, (defRestartRunning | defInitContextRenew | defInitStatusCopyDo | defInitSharedActive | defInitStatusPrioKeep | defInitLockKeep | defInitProcessAll)); } }
01977   #endif
01978   /* When all tasks are shared the dominat mode makes no sense. */
01979   #if ((includeTaskProtectSwitchTasks == cfgTrue) || (includeTaskProtectSwitchCritical == cfgTrue)) && (defAllSharedStack == cfgFalse)
01980    /* Only if the task has been 'done' we may switch to a new task. Otherwise, we are
01981     * still running this task. Note that all tasks are set to 'done' even before after start
01982     * so there must be a special situation when this has been changed to 'runnable'. Tasks that are
01983     * blocked, sleeping or cannot be continued in any way loose their switch block. Delayed tasks
01984     * may keep it, but the idle state will be called (technically this is not a switch). So first
01985     * test if we have indeed a state that does not block and is runnable*/
01986     if ( (oldTCB->uiTaskStatus & (defBaseNoBlocksGetMask | defBaseDressGetMask)) == (defBaseNoBlocksTask | defBaseDressRunable) )
01987     { /* If so, see if it is delayed */
01988       if ((oldTCB->uiTaskStatus & defBaseDelayStateGetMask) == defBaseDelayStateDelayed)
01989       { /* delayed states go idle, and will persist to do so until the wake up. */
01990         uiContextResult |= defAssignmentIdle; } }
01991     else
01992   #endif
01993   { /* After we have handle the shared tasks we must see if there are only shared tasks. In that case
01994      * we do not need to do an other selection, and know which task is selected, namely the only runable
01995      * one. */
01996     #if (defAllSharedStack == cfgTrue)
01997       /* The task to be executed is the one selected by the shares. */
01998       Tselect uiExecute = uiShareSelect;
01999     #else
02000       /* If there are also standard tasks, we must make a new selection about which task to run. */
02001       Tselect uiExecute = privSelectTask(0x00,0,defNumberOfTasks);
02002     #endif
02003     if (uiExecute.MaxStatus == (defBaseRunningTask - 1))
02004     { /* DISCUSSION
02005        * If we arrive here, all the tasks are terminated, suspended, sleeping, blocked or delayed.
02006        * (Thus, you cannot arrive here if there are running tasks, this is important for a correct
02007        *  understanding of the decisions made below.)
02008        * The first situation can never recover, the next three cannot recover on their own, thus
02009        * the delayed tasks are their only hope. Thus we may go to sleep directly, or, when the
02010        * the minimum delay is long enough, for that time at least. Interrupts can revive
02011        * locked tasks may do so through the sleep anyway. Note that we disallowed sleep in case
02012        * of any write-file system activity or coming activity.
02013        * So see if we allow for low power sleep. If there are no tasks we cannot decide to go to sleep
02014        * by looking at the tasks. In fact, we should not sleep, maybe the idle loop has something
02015        * sensible to do, otherwise we just as well switch off the power. */
02016       #if (cfgUseLowPowerSleep == cfgTrue) && (defNumberOfTasks > 0)
02017         Tuint08 uiLoopTask;
02018         /* Set the minimum delay to 255 block ticks, this is the largest value per default. */
02019         Tuint08 uiMinDelay = defSleepMaximum;
02020         /* If there is any fs activity we do not need to test any further, so sort this out. If any
02021          * writing is going on, we may not fall asleep. Reading is permitted (read tasks cannot block
02022          * because of reading solely, only in combination of an other task writing or an other synchronization
02023          * primitive). Since we know there are no running tasks when arriving here, we only need to check
02024          * if there is not write block present.*/
02025         #if (cfgUseFileSystem  ==  cfgTrue)
02026           /* If the file system is in write block, there are tasks writing to the FS and we cannot put the
02027            * system into a sleep mode. If there is a read block, this is no problem by itself.
02028            * If we have a burn block, there must be a write block too, so we do not need to check
02029            * for burning separately.*/
02030           if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockActive)
02031           { /* Setting uiLoopTask to zero blocks failing to sleep in the test below. */
02032             uiLoopTask = 0; }
02033           else
02034         #endif
02035         { /* Now loop through all tasks to test if a sleep is justified. */
02036           for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++)
02037           { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
02038             #if (cfgUseFileSystem  ==  cfgTrue)
02039               /* Test first if we do not have a FS block, if so, no sleep. For we must first handle any
02040                * task that is waiting for an opportunity to start writing. Task blocking for reading
02041                * operations should not hinder falling asleep. However, there cannot be a situation where
02042                * all tasks are solely blocked on reading. One read block implies an other task blocked
02043                * on writing. And even if that where possible we should not go to sleep since we cannot
02044                * store the blocked state. Tasks actually doing reading (but blocked or delayed) are
02045                * NOT FileBlocked, thus pass here undetected. When some tasks are actually writing
02046                * we never arrive here. End of the story is that any file block should hinder sleep.
02047                * The  defBaseDressGetMask is not important. */
02048               if ( (loopTCB->uiTaskStatus & defBaseFileBlockedGetMask) == defBaseFileBlockedTask ) { break; }
02049             #endif
02050             /* We can only arrive here if all tasks (until now) are terminated, suspended, sleeping of syncblocked.
02051              * (fs blocks are already filtered out). The task could be delayed or syncblocked, the latter with or
02052              * without delay. We cannot reach this place if there is any task that is able to run.
02053              * Here three situations are important. No delay, delay with UseLowPowerOnDelay or delay without UseLowPowerOnDelay.
02054              * First if delayed tasks are not possible then, there is nothing left to do. If we come here, this task
02055              * cannot hinder going to sleep anymore.
02056              * In the second situation, where we allow a sleep on (long) delays, we must determine the shortest delay among
02057              * all, where we only take tasks into account that will be reviving without the help from outside, i.e.
02058              * standard  delayed tasks, or blocked delayed tasks (not on FS).
02059              * Third, if we do not allow a sleep on (long) delays, we must determine if we really do not have any delayed
02060              * tasks, thus  check if there is a delay task, if so, we may not go to sleep. If there is no delayed
02061              * task, we could only arrive here (idle) if ALL tasks are stopped, suspended, sleeping of syncblocked.
02062              * Only an external event can revive any of them. We may go to sleep.
02063              * Note that it does  matter if blocked state is delayed, since a delayed blocked state may revive
02064              * earlier, but only with help from an other task or from the outside. But it will certainly be revived
02065              * when its timer expires. We thus must treat is as if it is a normal delayed task.
02066              * It must however not be a stopped delayed task, since even if the timer expires it will never restart. */
02067             #if (cfgUseDelay == cfgTrue)
02068               /* Test if we are in a delayed situation, that is not terminated or sleeping (fs is not possible any more)
02069                * but may be syncblocked. */
02070               if ( (loopTCB->uiTaskStatus & (defBaseStopStateGetMask | defBaseModeGetMask | defBaseDelayStateGetMask)) == ( defBaseStopStateGo |  defBaseModeSync | defBaseDelayStateDelayed ) )
02071                 #if (cfgUseLowPowerOnDelay == cfgTrue)
02072                 { /* Second situation, we must determine the delay. We do not care for the low byte, this 256 ticks vanish in
02073                    * the rounding. One tick block must at least be the delay.  The calculation is correct, even if
02074                    * loopTCB->uxDelay.HighByte < uxTickCount.HighByte do to the limited size of the type. */
02075                   Tuint08 uiDelayTime = loopTCB->uxDelay.HighByte - uxTickCount.HighByte;
02076                   /* If the high bytes are equal, there will be no delay, in fact that task will be waked
02077                    * [Is this possible at all, it seems not so, cause these tasks should have been waked earlier on ]
02078                    * Delays as long as 0xFF00 or more are forbidden. */
02079                   if (uiDelayTime == 0 ) { break; }
02080                   /* The time the device may be put to sleep is of course one tick block less otherwise, we could
02081                    * be to late for the task to wake, thus we subtract one. */
02082                   uiDelayTime--;
02083                   /* We have an option which specifies a lower bound on the sleep time. Sleep times below that
02084                    * are not thought to be useful, thus if we find such a time, we might as well wait here */
02085                   if (uiDelayTime < defSleepThreshold ) { break; }
02086                   /* We want to keep track of the smallest possible sleep time so far, cause that will be reported
02087                    * to the portSleep method. */
02088                   if (uiMinDelay>uiDelayTime) { uiMinDelay = uiDelayTime; } }
02089                 #else
02090                 { /* Third situation, a present delay must prohibit going to sleep. */
02091                   break; }
02092                 #endif
02093             #endif
02094           }
02095         }
02096         /* No we must see if we did not break out of the loop. If we did not ... */
02097         if (uiLoopTask == defNumberOfTasks)
02098         { /* ... we indeed may go to sleep, the the caller so */
02099           uiContextResult |= defAssignmentSleep;
02100           /* and let the portSleep method know what the longest possible sleep time is, for
02101            * which no tasks will be missed (note the you may sleep longer if you want). The highest
02102            * value that can emerge from a delay is 0xFE (from 0xFExx) so that we know that a value of
02103            * defSleepMaximum (0xFF) must mean that no delay was present at all.
02104            * Now we can use the subbyte to pass this information, since if we are going to sleep, the
02105            * information will be useless anyway. The tick counter can be corrected manually, but
02106            * we cannot expect it to be accurate up to the subtick */
02107            uxTickCount.SubByte = uiMinDelay; }
02108         else
02109       #endif
02110     { /* If you arrive here, then or you did not allow for a sleep mode, or the conditions
02111        * to go go low power sleep are just not met. This implies running the idle task,
02112        * we are done, let the caller know. */
02113       uiContextResult |= defAssignmentIdle; } }
02114     else
02115     { /* If you arrive here, then there is a task to be run. There are two possibilities. If all tasks
02116        * run in the shared mode, then there can only one task be active, so we do not need to select any
02117        * more, the only task must be the one, otherwise the task was selected in the selection process
02118        * thereafter. */
02119       TtaskControlBlock * runTCB = privTcbList(uiExecute.MaxTask);
02120       /* So test if we only have shared tasks. */
02121       #if (defAllSharedStack == cfgFalse)
02122       /* If not, it could be that all tasks in the current priority have already run,
02123        * so that we selected a task in the state defRunStateDone. If this is the case
02124        * then we know vice versa that all tasks in that priority have that state,
02125        * for otherwise it would have been selected due to the higher value of the status byte. */
02126         if ((runTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressDone)
02127         { /* In fact we need only to restore the run state of the shared states of the current
02128            * priority. We do so if we have cfgUseHierarchicalRoundRobin activated, mostly however
02129            * it does not matter much, so we save the code.
02130            * However, we must take care  that only running tasks are handled.
02131            * For the blocking tasks the dressbit is used for other purposes. This is all handled
02132            * within the function privMakeTasksRunable. */
02133           #if (cfgUseHierarchicalRoundRobin == cfgTrue)
02134             privMakeTasksRunable(0x00,(runTCB->uiTaskStatus & defBasePrioGetMask),0,defNumberOfTasks,((cfgUseSynchronization == cfgSyncNon) && (cfgUseFileSystem  ==  cfgFalse) && (defUseSharedStack == cfgFalse)));
02135           #else
02136             privMakeTasksRunable(0x00,0,defNumberOfTasks,((cfgUseSynchronization == cfgSyncNon) && (cfgUseFileSystem  ==  cfgFalse) && (defUseSharedStack == cfgFalse)));
02137           #endif
02138         }
02139       #endif
02140       /* The state immediately needs to be set to the done state, to prohibit indefinite running, since,
02141        * when the dress bit is set this indicates a dominant mode. */
02142       runTCB->uiTaskStatus = (runTCB->uiTaskStatus & defBaseDressSetMask) | defBaseDressDone;
02143       /* Having selected a new state to run, let us put in the status register. From
02144        * now on, that is the current task.  */
02145       uiOsStatus = (uiOsStatus & defTaskNumberSetMask) | (uiExecute.MaxTask << defOsTaskNumberShift);  } }
02146   /* Tell the caller the result, which may be one of: defAssignmentIdle or defAssignmentSleep or
02147    * (and that was the default defAssignmentTask) */
02148   return uiContextResult; }
02149 
02150 
02151 #if (cfgUseLowPowerSleep == cfgTrue)
02152 
02153 static void privEnterSleep(Tuint08 uiTickMinDelay)
02154 { /* If you arrive here, you obviously wanted the device to go to sleep.
02155    * Here we make the last preparations before we can do so.
02156    * The OS stack is not relevant anymore, so to allow for maximal
02157    * depth on in the portSleep function, we want to reset it. This
02158    * however cannot be done with interrupts activated, since that
02159    * would open the possibility of an interrupt on an incomplete
02160    * stack. Thus the first thing to do is to deactivate the interrupts,
02161    * which is only necessary if they are active at all */
02162   #if (cfgIntOsProtected == cfgFalse)
02163     /* We want timer interrupts
02164      * disabled during sleep. The latter is the default situation, thus we do not
02165      * need to change anything to the tick interrupts.
02166      * If we come here, global interrupts where enabled (no OS protection) thus
02167      * we must disable them explicitly. Timer interrupts where already disabled in
02168      * the privInitOs section, thus there is no need to repeat that. */
02169     privDisableGlobalInterrupts();
02170   #endif
02171   /* Let the system know we are about to enter the sleep mode, this information
02172    * is needed when we wakeup and restore the system state, since we always enter
02173    * the OS with privInitOS */
02174   uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateSleep);
02175   /* report we are going to bed */
02176   privTrace(traceSleepStart);
02177   /* Now we may reset the stack, no fear for interrupts. */
02178   privSetStack(&xOS.StackOS[OSstackInit]);
02179   /* Before we go to sleep, there may be some work to do, if a hook is installed */
02180   #if (callAppEnterSleep == cfgTrue)
02181     appEnterSleep();
02182   #endif
02183   /* If we wake from sleep, we want to be sure there is no SleepRequest left. We know that we could
02184    * never arrive here when we where writing, but it could be that the system is in read only mode.
02185    * This must be preserved. */
02186   #if (cfgUseFileSystem == cfgTrue)
02187     uiFsStatus = (uiFsStatus & defFsSleepRequestSetMask) |  defFsSleepRequestClear;
02188   #endif
02189   /* This is where it happens. Call (not jump to!) portSleep. This method
02190    * returns when the sleep is done. Note we enter the portSleep method with
02191    * global interrupts disabled, and we should return that way. In between the
02192    * interrupts may be enabled when needed. */
02193   portSleep(uiTickMinDelay);
02194   /* After we have slept, there may be some work to do, if a hook is installed */
02195   #if (callAppEnterSleep == cfgTrue)
02196     appExitSleep();
02197   #endif
02198   /* If we return, make the jump here */
02199   portJump(privTickYield); }
02200 
02201 #endif
02202 
02203 
02204 static void privEnterIdle(void)
02205 { /* Arriving here, we want to put the device in idle mode.
02206    * Here we make the last preparations before we can do so.
02207    * The OS stack is not relevant anymore, so to allow for maximal
02208    * depth on in the portIdle function, we want to reset it. This
02209    * however cannot be done with interrupts activated, since that
02210    * would open the possibility of an interrupt on an incomplete
02211    * stack. Thus the first thing to do is to deactivate the interrupts,
02212    * which is only necessary if they are activated at all.
02213    * If we have OS protection, the interrupts are already off, and\
02214    * the timer interrupts are permanently on. If not, we must take
02215    * care of that. */
02216   #if (cfgIntOsProtected == cfgFalse)
02217     privDisableGlobalInterrupts();
02218   #endif
02219   /* Timer interrupts are switch on (or must be on) in the idle mode
02220    * because we must return from idle due to a timer interrupt.
02221    * however, that is the responsibility of the portIdle implementation
02222    * Notify the OS that we enter the idle state now. This is needed at
02223    * return, cause we don't need to save any context then. */
02224   uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateIdle);
02225   /* Say that we are ready to start the idle task. */
02226   privTrace(traceIdleStart);
02227   /* Reset the stack, so we maximal space in portIdle. */
02228   privSetStack(&xOS.StackOS[OSstackInit]);
02229   /* Interrupts on of course, otherwise the timer interrupt cannot work, and
02230    * we are ready with the stack switch. */
02231   privEnableGlobalInterrupts();
02232   /* Before we enter the idle state the there may be some work to do */
02233   #if (callAppEnterIdle == cfgTrue)
02234     appEnterIdle();
02235   #endif
02236   /* Done, we goto (not call!) the portIdle. Thus, we do not return here. ;-) */
02237   portJump(portIdle); }
02238 
02239 
02240 #if (defNumberOfTasks == 0)
02241 
02242 /* Use an empty method test purposes if there are no tasks defined, we should never come here. */
02243 static void privEnterTask(void) { while(true); }
02244 
02245 #else
02246 
02247 static void privEnterTask(void)
02248 { /* Arrive here when a task need to be executed. Here we make the last preparations before
02249    * we can do so. The OS stack must be changed to the task stack. This however cannot be
02250    * done with interrupts activated, since that would open the possibility of an interrupt
02251    * on an incomplete stack. Thus the first thing to do is to deactivate the interrupts,
02252    * which is only necessary if they are activated at all.
02253    * Timer interrupts are switch on (or must be on) in the task mode. This however is done
02254    * inside the portRestore method for its state is dependent on the previous interrupt
02255    * state of the task. Therefore, we keep (default situation) tick interrupts disabled
02256    * here. If we have OS protection, the interrupts are already off. If not, we must take
02257    * care of that. */
02258   /* Notify the OS that we enter a task state now. This is needed at
02259    * return, cause we must save the context then. */
02260   uiOsStatus = ((uiOsStatus & defContextSetMask) | defContextStateTask);
02261   /* Get the number of the task we are going to run (a call to privTaskNumber takes more bytes) */
02262   Tuint08 uiTaskNumber = (uiOsStatus & defTaskNumberGetMask) >> defOsTaskNumberShift;
02263   /* Get the task control block of the current task also */
02264   TtaskControlBlock * curTCB = privTcbList(uiTaskNumber);
02265   /* Say that we are ready to start the task, but we do this before we switch to the
02266    * task itself since we do not know if the privTrace action fits on the task stack. */
02267   privTrace(traceTaskStart | uiTaskNumber);
02268   /* If we make use of register compression with constant registers, (remember the facility that saves
02269    * stack space by saving only those registers you use on the context), we must determine which
02270    * registers must be used. We need that information to calculate the space these will take
02271    * on the stack. */
02272   #if (defRegisterUseConstant == cfgTrue)
02273     /* ... we can use the constant value */
02274     Tuint08 uiRegisterUse = defRegisterUseCollect;
02275   #else
02276     /* otherwise we must extract it from flash. */
02277     Tuint08 uiRegisterUse = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint16,uiRegisterUse);
02278   #endif
02279   /* We must determine the number of registers used by this task, in oder to calculate the
02280    * new stack level. Since that may require a function call, we perform that operation first.*/
02281   #if (cfgCheckTaskStack == cfgTrue)
02282     #if (defRegisterUseConstant == cfgTrue)
02283       /* The constant defRegisterCount holds the number of registers used.*/
02284       Tuint08 uiRegCount = defRegisterCount;
02285     #else
02286       /* We have to calculate the number dynamically */
02287       Tuint08 uiRegCount = privRegisterCount(uiRegisterUse);
02288     #endif
02289   #endif
02290   /* Disable interrupts here, for interrupts make use of the stack, at least for the call. But this
02291    * is not allowed any more since we are about to fill the background variables.*/
02292   #if (cfgIntOsProtected == cfgFalse)
02293     privDisableGlobalInterrupts();
02294   #endif
02295   /* Make sure all calls are handled, for after this instruction we start filling the background
02296    * variables. The stack should not be used any more. I am not 100% sure this is sufficient, but
02297    * i think i have no other means at my disposal other than assembly, which we cannot use here. */
02298   portBarrier();
02299   /* If we make use of register compression with constant registers, we now fill the background
02300    * variable with the correct value. */
02301    xOS.pxSave.uiRegisterUse = uiRegisterUse;
02302   /* If we check the use of registers we may not want to check all registers, even if they are used. Here we may
02303    * want to check for all tasks the same registers so ...*/
02304   #if (cfgCheckRegisters == cfgTrue)
02305     #if (defRegisterCheckConstant == cfgTrue)
02306       /* ... we can use the constant value */
02307       xOS.pxSave.uiRegisterCheck = defRegisterCheckCollect;
02308     #else
02309       /* otherwise we must extract it from flash. */
02310       xOS.pxSave.uiRegisterCheck = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint16,uiRegisterCheck);
02311     #endif
02312   #elif  (cfgCheckWatermarks == cfgTrue)
02313     /* If we collect watermarks and do not check the registers, we want to collect this
02314      * information from all registers, thus */
02315       xOS.pxSave.uiRegisterCheck = (registersAll);
02316   #endif
02317   /* If we do some checks on the use of the task stack (which are done on portSaveContext)
02318    * we prepare that information here, and put it in the background variables */
02319   #if (cfgCheckTaskStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02320     /* Stack size is needed on two occasions */
02321     #if (defStackSizeConstant == cfgTrue)
02322       Tstack uiStackSize = defStackSizeFixed;
02323     #else
02324       Tstack uiStackSize = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tstack,uiStackSize);
02325     #endif
02326   #endif
02327   /* At saveContext we need to know where the stack starts, but since getting this info
02328    * may overflow the stack at that time, we get it here and save it on the OS stack which
02329    * is used as background storage for the task. */
02330   xOS.pxSave.pcStackOffset = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Taddress,pcStackOffset);
02331   /* Some methods return a boolean to the application code. This data is put in the
02332    * correct register by the restoreContext. At this point we prepare it */
02333   #if (cfgUseSynchronization != cfgSyncNon) && (defUseBoolReturns == cfgTrue)
02334     /* Put that information in the background variables */
02335     xOS.pxSave.uiReturn = curTCB->defRetField & defRetGetMask ;
02336   #endif
02337   #if (cfgCheckTaskStack == cfgTrue)
02338     /* Calculate the limit for the stack size. We do that calculation also here, so that,
02339      * in the portSaveContext, we can do a simple compare. */
02340     #if (cfgSysStackGrowthUp == cfgTrue)
02341       /* TODO: cfgSysStackGrowthUp oops, add code */
02342       xOS.pxSave.pcStackLimit = 0;
02343     #else
02344       /* Note that the limit is calculated taking StackSavety into account. Thus, it is an error
02345        * to pass this limit, although the stack is not yet corrupted at that point. This is
02346        * intentional, so that we can stop the task rather than experiencing bizarre results,
02347        * when experimenting with the stack size. The +1 represents the status register.*/
02348       xOS.pxSave.pcStackLimit = uiRegCount+xOS.pxSave.pcStackOffset+StackSafety-uiStackSize+1;
02349     #endif
02350   #endif
02351   /* Here we will scan the stack for bytes that differ from the initial bye. This indicates the use of
02352    * the stack also on places where no interrupt is possible (at the context the stack is used heavily,
02353    * but it may not give the absolute top if tick interrupts are disabled during deep calls. */
02354   #if (cfgCheckWatermarks == cfgTrue)
02355     #if (cfgSysStackGrowthUp == cfgTrue)
02356       /* I don't know, probably the offset point to the first writable byte*/
02357       Taddress pStack = xOS.pxSave.pcStackOffset + uiStackSize - 1;
02358       /* We walk downwards */
02359       while ( (*(pStack--) == defStackInitByte) && (uiStackSize--));
02360     #else
02361       /* The watermark measurement starts from the top of the stack (first byte to be read),
02362        * which is below the stack offset */
02363       Taddress pStack = xOS.pxSave.pcStackOffset - uiStackSize + 1;
02364       /* the stack is write, post decrement, thus, when we walk downwards, we must read, pre-increment.
02365        * We walk maximally the stack size downwards. If we encounter a value which differs from the
02366        * initial value, we can stop. */
02367       while ( (*(pStack++) == defStackInitByte) && (uiStackSize--));
02368       /* The level of bytes used is at least the number of bytes below the boundary found.
02369        * We assume the stack has not overflown, first it may produce erroneous results in
02370        * the calculation, and second, watermarking is a technique to detect how much stack space is
02371        * left, not to detect stack overflow.*/
02372     #endif
02373     /* If we happen to find a higher level with this technique opposite to the level found
02374      * due to context save, there must have been a situation of heavy stack use inside a tick
02375      * protected region of the code. Good that we noticed it! */
02376     if ((curTCB->uiStackMax)<(uiStackSize)) { curTCB->uiStackMax = uiStackSize; }
02377   #endif
02378  /* Recall the address stack level, and put it in the background variables. Thus this
02379     * variable gets accessible from the portSave/portContext routines. */
02380    xOS.pxSave.pcStackLevel = (Taddress) &(curTCB->pcStackLevel);
02381    /* Now, switch to the task stack */
02382    #if (cfgSysStackGrowthUp == cfgTrue)
02383      privSetStack( (Taddress) ((Tuint16) xOS.pxSave.pcStackOffset + (Tuint16) curTCB->pcStackLevel) );
02384    #else
02385      privSetStack( (Taddress) ((Tuint16) xOS.pxSave.pcStackOffset - (Tuint16) curTCB->pcStackLevel) );
02386    #endif
02387  /* We need not to activate the interrupts right here, since that is done in the
02388   * portRestoreContext at the end by the 'reti'. Jump to the routine that restores
02389   * the context for the task at hand. */
02390   portJump(portRestoreContext); }
02391 
02392 #endif
02393 
02394 
02395 #if (defUseDelay == cfgTrue)
02396 
02397 static void privWakeupFromDelay(Tuint08 uiTaskNumber, TtaskControlBlock * taskTCB)
02398 { /* Call this if you want to wake up a task due to expiration of the delay timer.
02399    * That can be or a simple delay, or a task that has a time out on a block.
02400    * taskTCB must be the tcb that corresponds to the uiTaskNumber. */
02401    #if ((cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue)) && (cfgUseTimeout == cfgTrue)
02402      /* If the task was in blocked state, it may be released for its timeout has passed. Note that
02403       * blocked tasks without a timeout are not delayed, so they never arrive here. The same holds for terminated
02404       * tasks, so unblocking a terminated task cannot result in a suspended task. Sleeping tasks may still be unblocked.
02405       * Resuming, this are the possibilities
02406       * (1) Terminated task: never delayed, should not arrive here.
02407       * (2) Suspended task: never blocked, cannot pass the test below.
02408       * (3) Sleeping, may be blocked, is always syncblocked, thus may timeout to free-sleeping
02409       * (4) FS block: may deblock to free, we must clear the FsFields
02410       * (5) Sync block: may deblock to free, we must clear the blocking slots
02411       * (6) Simple delay, just clear the delay.
02412       * (7) Shared task, may be scheduled for execution, just clear the delay
02413       * */
02414       if ((taskTCB->uiTaskStatus & defBaseBlockStateGetMask) == defBaseBlockStateBlocked)
02415       { /* We can savely assume we only enter this part of the code if the present task indeed
02416          * has a slotstack. If not, the task could never be blocked. so we dont check. Since
02417          * privRestoreInitialPriority and privUnblockTask assume such uiTaskSlot is present,
02418          * they may crash if the fields do not exist. */
02419         /* If we have priority lifting .. */
02420         #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue)
02421           /* ... and if we release, its priority may be changed (we don't know for sure) in any case
02422            * it must be restored. For the moment however we only support priority lifting on sync blocked
02423            * tasks. Thus we must test if we indeed have syncblocking. We included sleeping in this task
02424            * for all sleeping tasks must come from syncblocks.*/
02425           if ((taskTCB->uiTaskStatus & (defBaseModeGetMask | defBaseDressGetMask)) == (defBaseModeSync | defBaseDressSlot))
02426           { privRestoreInitialPriority(uiTaskNumber); }
02427         #endif
02428         /* Release the task by unblocking and unlocking and indicate that it was release by a timeout
02429          * so the requested lock was not obtained (return false) There is no need to check if other
02430          * tasks must be released since we remove a blocking task, which cannot have consequences
02431          * for other blocked/waiting tasks.*/
02432         privUnblockTask(uiTaskNumber | defParaLockStateUnlock | defParaRetStateFalse); }
02433       /* The unblocking above also wakes the task so we may skip it here. */
02434       else
02435    #endif
02436    /* wake the task */
02437    { taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & defBaseDelayStateSetMask) | defBaseDelayStateWake; } }
02438 
02439 #endif
02440 
02441 
02442 #if (cfgUseLoadMonitor == cfgTrue)
02443 
02444 static void privCopyLoad(void)
02445 { /* Arrive here if we want to monitor the load of all tasks, the Os and the Isr. Every now
02446    * and then we copy the collected subticks to the totals and clear the collectors. Since
02447    * this routine is not interrupted, we get an adequate picture of the loads. */
02448   #if (cfgCheckTrace == cfgTrue)
02449     /* If we have tracing, first sent to the trace channel load info is comming */
02450     privTrace(traceLoadInfo);
02451     /* then send the number of bytes to follow. */
02452     privTrace(2*(defNumberOfTasks+3));
02453   #endif
02454   /* Copy the OS load information into the register total. */
02455   uiOsLoadTotal = uiOsLoadCollect;
02456   #if (cfgCheckTrace == cfgTrue)
02457     /* Send the load of the OS in big endian. */
02458     privTrace((Tbyte)(uiOsLoadTotal>>8));
02459     privTrace((Tbyte)(uiOsLoadTotal));
02460   #endif
02461   /* Reset the collecting register. */
02462   uiOsLoadCollect = 0;
02463   /* Copy the idle load information into the register total. */
02464   uiIdleLoadTotal = uiIdleLoadCollect;
02465   #if (cfgCheckTrace == cfgTrue)
02466     /* Send the load of the idle 'task' in big endian. */
02467     privTrace((Tbyte)(uiIdleLoadTotal>>8));
02468     privTrace((Tbyte)(uiIdleLoadTotal));
02469   #endif
02470   /* Reset the collecting register. */
02471   uiIdleLoadCollect = 0;
02472   /* Only if non empty interrupts we need to copy the isr load. Otherwise, these variables
02473    * are absent. */
02474   #if (cfgIntUserDefined == cfgTrue)
02475     /* Copy the ISR load information into the register total. */
02476     uiIsrLoadTotal = uiIsrLoadCollect;
02477     #if (cfgCheckTrace == cfgTrue)
02478     /* Send the load of the ISR  in big endian. */
02479       privTrace((Tbyte)(uiIsrLoadTotal>>8));
02480       privTrace((Tbyte)(uiIsrLoadTotal));
02481     #endif
02482     /* Reset the collecting register. */
02483     uiIsrLoadCollect = 0;
02484   #else
02485     #if (cfgCheckTrace == cfgTrue)
02486       /* We even send if we have no ISR whatsoever, since we want to emit a fixed number of bytes.*/
02487       privTrace(0);
02488       privTrace(0);
02489     #endif
02490   #endif
02491   /* Depending on the number of tasks, we move the loads, and send the info. */
02492   Tuint08 uiLoopTask;
02493   for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++)
02494   { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
02495     /* Copy the load information of this task into the register total. */
02496     loopTCB->uiLoadTotal = loopTCB->uiLoadCollect;
02497     #if (cfgCheckTrace == cfgTrue)
02498      /* Send the load of the task  in big endian. */
02499       privTrace((Tbyte)(loopTCB->uiLoadTotal>>8));
02500       privTrace((Tbyte)(loopTCB->uiLoadTotal));
02501     #endif
02502     /* Reset the collecting register. */
02503     loopTCB->uiLoadCollect = 0; } }
02504 
02505 #endif
02506 
02507 
02508 #if (cfgCheckWatermarks == cfgTrue) && (cfgCheckTrace == cfgTrue)
02509 
02510 static void privTraceWatermarks(void)
02511 { /* Arrive here too send the water mark and register use information to tracing.
02512    * First send the byte indicating that Watermarks are coming, then send the
02513    * number of bytes to follow. */
02514   privTrace(traceWatermarks);
02515   privTrace(3*defNumberOfTasks+3);
02516   /* Send the watermark info of the OS. */
02517   privTrace(uiOsStackMax);
02518   /* See if we are using an isr */
02519   #if (defUseIsrStack == cfgTrue)
02520     /* Send the watermark of the isr stack, and depending on the size we must send
02521      * an extra zero. */
02522     #if ((StackSizeISR) > 0xFF)
02523       privTrace((Tbyte)(uiIsrStackMax>>8));
02524     #else
02525       privTrace(0);
02526     #endif
02527     privTrace((Tbyte)(uiIsrStackMax));
02528   #else
02529     /* If not send zero's */
02530     privTrace(0);
02531     privTrace(0);
02532   #endif
02533   /* Depending on the number of tasks, we move the loads. */
02534   Tuint08 uiLoopTask;
02535   for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++)
02536   { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
02537     /* Depending on the size of the Stack we must send a zero byte or extract the first byte */
02538     #if (defStackSizeExceedsOneByte == cfgTrue)
02539       privTrace((Tbyte)(loopTCB->uiStackMax>>8));
02540     #else
02541       privTrace(0);
02542     #endif
02543     privTrace((Tbyte)(loopTCB->uiStackMax));
02544     /* Send the use of the registers. */
02545     privTrace((Tbyte)(loopTCB->uiRegisterUse)); } }
02546 
02547 #endif
02548 
02549 
02550 #if (defUseDelay == cfgTrue)
02551 
02552 #if (includeTaskDelayFromWake == cfgTrue)
02553   static void privDelayCalc(Tuint16 uiDelayTime, Tbool bFromNow)
02554 #else
02555   static void privDelayCalcFromNow(Tuint16 uiDelayTime)
02556 #endif
02557 { /* This is the delay core code. Several routines make use of it. Arrive here if you need to
02558    * to set the delay variables of the task control block. */
02559   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02560     /* This is one of the places in the current design of the Femto OS that is somewhat deeper.
02561      * Thus it makes sense to check the OS stack here. */
02562     privCheckOsStackLevel();
02563   #endif
02564   TtaskControlBlock * curTCB = privTcbList(privTaskNumber(defCurrentTaskNumber));
02565   /* There are two different forms of delay. The time measured form the current value of the
02566    * tick counter or from the last value of activation. The latter is stored in the delay
02567    * variables. If we have no need for DelayFromWake we exclude the section. */
02568   #if (includeTaskDelayFromWake == cfgTrue)
02569     if (bFromNow)
02570   #else
02571     if (true)
02572   #endif
02573   { /* Here the new time is calculated form the current time. */
02574     /* Now calculate the new Delaytime. This is a 16 bit operation. Note that the uiDelaytime
02575      * parameter represents the requested delay time whereas the uiDelayTime field in the
02576      * task control block represents the wakeup time of the particular task due to delay. */
02577     uiDelayTime += uxTickCount.Full; }
02578   else
02579   { /* Here the new time is calculated from the last wake time. */
02580     #if (cfgCheckTiming == cfgTrue)
02581       /* We can assume we do not want to set a new wake time that is in he past, although it
02582        * is not an error in absolute sense. However, we no code that protects in this case
02583        * it being interpreted as a long delay. Therefore we report an error. */
02584       Tuint16 uiMinDelay = (uxTickCount.Full - curTCB->uxDelay.Full) ;
02585       if (uiDelayTime < uiMinDelay)
02586         /* Unfortunately we do not know which method called the function, so we cannot issue the
02587          * correct callID with certainty. Since the method declaration is already complicated enough
02588          * we issue a IdSystem error here. We do know however that it must be the current task
02589          * causing the problems, and that we are in pseuso task-space, thus we may switch at error. */
02590       { privShowError((errTaskDelayTooShort | errTaskStateCurrent | errOsStateAsIs), callIdSystem, errCurrentTask ); }
02591     #endif
02592     uiDelayTime += curTCB->uxDelay.Full; }
02593   /* We have a new wake time, so place it in the appropriate fields. */
02594   curTCB->uxDelay.Full =  uiDelayTime ;
02595   /* It may be that this task is in the near wake, in which all tasks are that are in the
02596    * same tick block as the current time. We must check this and set the near wake bit if
02597    * needed. */
02598   if (curTCB->uxDelay.HighByte ==  uxTickCount.HighByte )
02599   { uiOsStatus = ((uiOsStatus & defNearWakeStateSetMask) | defNearWakeStatePresent); }
02600   /* Update the task status of the task, say that the current task is delayed. If we want to
02601    * allow for delays below the minimum, we must add code here to check and keep the state
02602    * from being delayed. However we assume that this situation is normally not what you
02603    * want, because the task can be 'far behind'. We cannot get here for terminated tasks. */
02604   curTCB->uiTaskStatus = (curTCB->uiTaskStatus & defBaseDelayStateSetMask) | defBaseDelayStateDelayed; }
02605 
02606 #endif
02607 
02608 
02609 
02610 #if (cfgUseSynchronization == cfgSyncSingleSlot) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue ))
02611 
02612 static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB)
02613 { taskTCB->uiTaskSlot = defSlotRightFree; }
02614 
02615 #endif
02616 
02617 
02618 #if ((cfgUseSynchronization == cfgSyncSingleBlock) || (cfgUseSynchronization == cfgSyncDoubleBlock)) && ((cfgUseTaskWatchdog == cfgTrue) || ( includeTaskRecreate == cfgTrue ))
02619 
02620 static void privCleanSlotStack(TtaskExtendedControlBlock * taskTCB)
02621 { Tuint08 * pTaskSlot = (Tuint08 *) &taskTCB->uiTaskSlot;
02622   /* We need to know the total size of the stack. Note that the number of
02623    * slots is the double of the depth. */
02624   #if (defSlotDepthConstant == cfgTrue)
02625     /* It may be a constant and than we use that */
02626     Tuint08 uiSDC = defSlotDepthCollect;
02627   #else
02628    /* Or it may be stored in flash*/
02629     Tuint08 uiSDC = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiControlTaskNumber & defTaskNumberGetMask],Tuint08,uiSlotDepth);
02630   #endif
02631   Tuint08 uiLoopSlot;
02632   for (uiLoopSlot=0; uiLoopSlot<uiSDC; uiLoopSlot++) { *pTaskSlot++ = defSlotFree;  } }
02633 
02634 #endif
02635 
02636 #if (cfgUseSynchronization == cfgSyncSingleSlot)
02637 
02638 static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot)
02639 { /* Use this method to add remove one slot in the slot stack, or to search a slot.
02640    * This implementation can handle one slot only, and should be selected in case
02641    * you use only one slot and no nesting is required. The size is thus known.
02642    * The search is the default. The search can be decorated with search free only
02643    * or blocks only. Per default any match is valid. If we add a slot, it will always
02644    * be put at the first position. The slot that was at that position will be placed
02645    * elsewhere. This is to ensure that, if the task blocks, we know the blocking
02646    * slot is in the first position. */
02647   TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask);
02648   /* Just to be sure */
02649   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02650     privCheckOsStackLevel();
02651   #endif
02652   /* Now we must handle all cases separately. In one special case we want only to
02653    * clean the stack, it is easy, done below. The clean case is special. */
02654   /* Load the value of the slot (left four bits are unused.) */
02655   Tuint08 uiVal = taskTCB->uiTaskSlot;
02656   /* Searching to see if the slot stack is free of Mutexes and Queues is need only in the
02657    * case we must restore the priority after lifting. */
02658   #if (cfgUsePriorityLifting == cfgTrue)
02659     /* We check if we want to see if the slot numbers representing Queues and Mutexes are absent */
02660     if ((uiControlTaskNumber & defSlotOperateSQMMask) == defSlotOperateQMAbsent)
02661     {  /* Test if slot represents a Queue or Mutex, if so return false, return true otherwise */
02662         return ((uiVal < defNumberQueuBegin) || (uiVal >= defNumberMutexEnd)); }
02663   #endif
02664   /* We need the result of the equality of the requested slot and the stored slot more
02665    * often, so store this in a separate variable. Also, this is the default result value */
02666   Tbool uiValIsSlot = (uiVal == uiSlotSlot);
02667   /* From this place on we will test the different situations. Please note that the default
02668    * result is return the value of uiValIsSlot. Thus if this is wat we want to return, we do
02669    * not need to do anything. */
02670   /* Test if we were looking for free slots only */
02671   if ((uiControlTaskNumber & defSlotOperateSFreeMask) == defSlotOperateSearchFree)
02672   { /* If we where, and the task was blocking, we must return false, since there
02673      * cannot be a matching slot. If the task was free we need to return the result
02674      * of uiValIsSlot, which is default. */
02675     if ((taskTCB->uiTaskStatus & defSlotLoopBlockMask) == defSlotLoopBlockBlocked) return false;  }
02676   /* Test if we want to add a slot */
02677   if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction)
02678   { /* If we make use of method checking, this slot should not be used at this moment */
02679     #if (cfgCheckMethodUse == cfgTrue)
02680       /* Test if it is*/
02681       if (uiVal != defSlotRightFree)
02682       /* If so return an error. Since we are potentially not stopping the current task, we want to return after the error. */
02683       { privShowError((errSlotIncreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); }
02684     #endif
02685     /* Report the locks */
02686     #if (cfgCheckTrace == cfgTrue)
02687       /* First we report which task is currently running */
02688       privTrace(traceSlotLock | (uiControlTaskNumber & defTaskNumberGetMask) );
02689       /* Subsequently report the lock. */
02690       privTrace(uiSlotSlot);
02691     #endif
02692     /* Set the new slot*/
02693     taskTCB->uiTaskSlot = uiSlotSlot;
02694     /* and what we return is not of importance. */ }
02695   /* Test if we want to remove a slot */
02696   if ((uiControlTaskNumber & defSlotOperateDecreaseMask) == defSlotOperateDecreaseAction)
02697   { /* Test if the slot matches. Now this is only needed if we make use of method checking, since it
02698      * is considered an error to remove a non existing slot.  */
02699     #if (cfgCheckMethodUse == cfgTrue)
02700       /* If it does not match */
02701       if (!uiValIsSlot)
02702       /* report the error. Since we are potentially not stopping the current task, we want to return after the error.  */
02703       { privShowError((errSlotDecreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); }
02704     #endif
02705     /* Report the unlocks */
02706     #if (cfgCheckTrace == cfgTrue)
02707       /* First we report which task is currently running */
02708       privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) );
02709       /* Subsequently report the lock */
02710       privTrace(uiSlotSlot);
02711     #endif
02712     /* Remove the slot, even if it does not match, cause we may asume it does. */
02713     { taskTCB->uiTaskSlot = defSlotRightFree; }
02714     /* We return with false, since this condition may only return true if an other slot of the same number
02715      * is present on the stack. This cannot be the case however, since there is only one slot present. */
02716     return false; }
02717   /* In all remaining cases we where just searching, and since there is only one slot a simple test is enough. */
02718  return uiValIsSlot; }
02719 
02720 #endif
02721 
02722 
02723 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
02724 
02725 static Tuint08 privGetQueuSize(Tuint08 uiQueuNumber)
02726 { /* Used to read the size of a particular queue from flash or to return the
02727    * constant if all sizes are equal. */
02728    Tuint08 uiResult;
02729    /* So first determine if they are equal */
02730   #if (defQueuSizeConstant == cfgTrue)
02731     /* Fixed sizes, return the constant.  */
02732     uiResult = defQueuSizeFixed;
02733   #else
02734     /* Otherwise we must look it up from a table in flash. */
02735     uiResult = portFlashReadWord(Tuint08,uiQueuSize[uiQueuNumber]);
02736  #endif
02737  /* Done, return the size of the queue. */
02738  return uiResult; }
02739 
02740 #endif
02741 
02742 
02743 
02744 #if (cfgUseSynchronization == cfgSyncSingleBlock) || (cfgUseSynchronization == cfgSyncDoubleBlock)
02745 
02746 static Tbool privOperateSlotStack(Tuint08 uiControlTaskNumber, Tuint08 uiSlotSlot)
02747 { /* Use this method to add remove slots in the slot stack, or to search a slot.
02748    * The latter is the default. The search can be decorated with search free only
02749    * or blocks only. Per default any match is valid. If we add a slot, it will always
02750    * be put at the first position. The slot that was at that position will be placed
02751    * elsewhere. This is to ensure that, if the task blocks, we know the blocking
02752    * slot is in the first position. Two slots can be added simultaneously, they always
02753    * take the first bytes. */
02754   /* The default reaction is false, but this is only needed if we have double slots, since
02755    * then it is sometimes handy to have a default reaction. Normally you should not enter
02756    * this method with the free slot. */
02757   TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask);
02758   /* Just to be sure */
02759   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02760     privCheckOsStackLevel();
02761   #endif
02762   /* pTaskSlot holds the pointer to the first two slots in the stack. The rest
02763    * of the stack is outside the TaskControlBlock. */
02764   Tuint08 * pTaskSlot = (Tuint08 *) &taskTCB->uiTaskSlot;
02765   /* We need to know the total size of the stack. Note that the number of
02766    * slots is the double of the depth. */
02767   #if (defSlotDepthConstant == cfgTrue)
02768     /* It may be a constant and than we use that */
02769     Tuint08 uiSDC = defSlotDepthCollect;
02770   #else
02771    /* Or it may be stored in flash*/
02772     Tuint08 uiSDC = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiControlTaskNumber & defTaskNumberGetMask],Tuint08,uiSlotDepth);
02773   #endif
02774   Tuint08 uiLoopSlot;
02775   /* This variable is needed to hold some slots later on. */
02776   Tuint08 uiVal;
02777   /* Searching to see if the slotstack is free of Mutexes and Queues is need only in the
02778    * case we must restore the priority after lifting. */
02779   #if (cfgUsePriorityLifting == cfgTrue)
02780     /* We check if we want to see if the slot numbers representing Queues and Mutexes are absent */
02781     if ((uiControlTaskNumber & defSlotOperateSQMMask) == defSlotOperateQMAbsent)
02782     { /* Loop through all the slots */
02783       for (uiLoopSlot=0; uiLoopSlot<uiSDC; uiLoopSlot++)
02784       { /* Get the first to slots (technically the left slot in the zeroth slot can never be
02785          * a wait, but checking does not harm either.*/
02786         uiVal = *pTaskSlot++;
02787         /* Extract the left slot */
02788         Tuint08 uiLeftSlot  = (uiVal & defSlotLeftGetMask) >> defSlotLeftShift;
02789         /* Test if it represents a Queue or Mutex, if so return false */
02790         if ((uiLeftSlot  >= defNumberQueuBegin) && (uiLeftSlot  < defNumberMutexEnd)) { return false;}
02791         /* Extract the right slot */
02792         Tuint08 uiRightSlot = (uiVal & defSlotRightGetMask) >> defSlotRightShift;
02793         /* Test if it represents a Queue or Mutex, if so return false */
02794         if ((uiRightSlot >= defNumberQueuBegin) && (uiRightSlot < defNumberMutexEnd)) { return false;} }
02795       /* If we did no encounter any Queues ore Mutexes, the stack is empty or contains only Waits, in that
02796        * case we may return true. */
02797       return true; }
02798   #endif
02799   /* uiLoopControl will contains two bits of information. One to indicate that is
02800    * the task is free or blocked. And the second (set further below) to indicate if
02801    * the operation is to handle all slots. The latter is the default  */
02802   Tuint08 uiLoopControl = (taskTCB->uiTaskStatus & defSlotLoopBlockMask);
02803   /* If we are still here we did not search Waits only. Now if we are not searching
02804    * free slots only eigther or increase the stack (i.e. we search all or we decrease the stack,  we must make
02805    * sure the loop below does not skip the first two slots in the search loop below. so override
02806    * the blocked state form the task to enforce checking the first byte.  */
02807   if (((uiControlTaskNumber & defSlotOperateSFreeMask) != defSlotOperateSearchFree) && ((uiControlTaskNumber & defSlotOperateIncreaseMask) != defSlotOperateIncreaseAction))
02808   { uiLoopControl = defSlotLoopBlockNon; }
02809   /* The loop below contains two parts, a test for each slot position and an action. The default
02810    * is to test agains the given slot, and to replace the found value with the freeSlot (0x00)
02811    * Since searching can only be at one slot at a time, we know uiSlotSlot has first four bits zero */
02812   Tuint08 uiTest = uiSlotSlot;
02813   Tuint08 uiFill = defSlotRightFree;
02814   /* Load the value of the fist two slots. */
02815   uiVal = *pTaskSlot;
02816   /* However in case we want to add a slot, we must look for a free slot and substitute the
02817    * value that was placed first, which in his turn must contain the new value. Thus */
02818   if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction)
02819   {/* Place the new slot at the first position, thereby leaving a possible second position in tact */
02820     #if (cfgUseSynchronization == cfgSyncDoubleBlock)
02821       /* In this case the whole byte is reserved for the new lock, which may contain two slots. */
02822       *pTaskSlot = uiSlotSlot;
02823     #else
02824       /* In this case the left nibble can contain a free lock which must be preserved, and uiSlotSlot can only
02825        * contain one slot */
02826       *pTaskSlot = (uiVal & defSlotRightSetMask) | uiSlotSlot;
02827     #endif
02828     /* Report the locks */
02829     #if (cfgCheckTrace == cfgTrue)
02830       /* First we report which task is currently running */
02831       privTrace(traceSlotLock | (uiControlTaskNumber & defTaskNumberGetMask) );
02832       /* Report the slots */
02833       privTrace(uiSlotSlot);
02834     #endif
02835      /* and from now on we look for empty slots */
02836     uiTest = defSlotRightFree;
02837     /* Which must be filled with the value that was on the first place before. */
02838     #if (cfgUseSynchronization == cfgSyncDoubleBlock)
02839       /* For a double block both slots may need replacement */
02840       uiFill = uiVal;
02841     #else
02842       /* For a single block only the right nibble is interesting, the left may contain a free lock */
02843       uiFill = (uiVal & defSlotRightGetMask);
02844     #endif
02845     /* If that slot was empty before, it makes no sense to continue (although it cannot harm)
02846      * and we are done. */
02847     if (uiFill == defSlotFree) return false;
02848     /* If we do continue we may not test the first nibble/byte since that is what we just filled, and we do
02849      * not want to risk a double block. so we simulate a block. */
02850     uiLoopControl = defSlotLoopBlockBlocked; }
02851   /* If we do not want to add a slot, we are about to remove one, or search for one. In case
02852    * we do not want to remove one, we search for one, and indicate so in the uiLoopControl variable.
02853    * This is to make sure we leave when found, before we substitute a value. */
02854   else if ((uiControlTaskNumber & defSlotOperateDecreaseMask) != defSlotOperateDecreaseAction)
02855   { uiLoopControl |= defSlotLoopSearchActive; }
02856   /* If we are decreasing, maybe we must inform the outside world we are doing so */
02857   #if (cfgCheckTrace == cfgTrue)
02858     else
02859     { /* Here we are decreasing, which we want to report the unlocks, first we report which task is currently running */
02860       privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) );
02861       /* Report the slot(s) */
02862       privTrace(uiSlotSlot); }
02863   #endif
02864   /* Start looping at the beginning or skip the first byte depending on the situation of the uiLoopControl. */
02865   if ((uiLoopControl & defSlotLoopBlockMask) == defSlotLoopBlockNon)
02866   { uiLoopSlot = 0; }
02867   else
02868   { /* We must distinguish the double and single block situations */
02869     #if (cfgUseSynchronization == cfgSyncDoubleBlock)
02870       /* In this case we need to skip the first full byte, which contains the new slots */
02871       uiLoopSlot = 2;
02872       pTaskSlot++;
02873     #else
02874       /* In this case we need to skip only the first nibble, which contains the new slot */
02875       uiLoopSlot = 1;
02876     #endif
02877   }
02878   /* Now run through all slots, double the number because we check left and right nibble with the same code */
02879   while (uiLoopSlot<2*uiSDC)
02880   { uiVal = *pTaskSlot;
02881      /* At odd passes we check the other nibble (slot) */
02882     if ((uiLoopSlot & 0x01) == 0x01) { portSwapNibbles(uiVal); }
02883     /* Test if the right slot fulfills the test, and if so, test also if we may test this slot. The latter may
02884      * not be the case for the very first slot, when we are only searching for free slots and this task is blocking. */
02885     if ((uiVal & defSlotRightGetMask) == uiTest )
02886     { /* So we found a match on the right slot. If we were only searching, we may leave with the message we found a hit. */
02887       if ((uiLoopControl & defSlotLoopSearchMask) == defSlotLoopSearchActive) return true;
02888       /* If we were replacing (for increase or decrease), do so now */
02889       #if (cfgUseSynchronization == cfgSyncDoubleBlock)
02890         /* We simply slide in the new value on the right, if the values ware swapped before
02891          * that does not matter */
02892         *pTaskSlot = (uiVal & defSlotRightSetMask) | (uiFill & defSlotRightGetMask);
02893       #else
02894         /* Calculate the new value for the slot stack */
02895         Tuint08 uiNewVal = (uiVal & defSlotRightSetMask) | uiFill;
02896         /* here we must be careful since the sequence of nibbles on the bottom of the slot stack
02897          * does matter, so we must restore if we corrupted it */
02898         if ((uiLoopSlot & 0x01) == 0x01) { portSwapNibbles(uiNewVal); }
02899         /* Now it is safe to store. */
02900         *pTaskSlot = uiNewVal;
02901       #endif
02902       /* If we are decreasing, it is interesting to know if there is a second slot with the same number,
02903        * so we continue otherwise */
02904       if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction)
02905       { /* If there can be a second slot */
02906         #if (cfgUseSynchronization == cfgSyncDoubleBlock)
02907           /* We move to the second slot */
02908           uiFill >>= 4;
02909           /* there may be no second slot to paste  */
02910           if (uiFill == defSlotFree) { return false; }
02911         #else
02912           /* If not we are done, the return value is of no importance */
02913           return false;
02914         #endif
02915       }
02916       else
02917       { /* we are decreasing, so turn the operation into a search */
02918         uiLoopControl |= defSlotLoopSearchActive; } }
02919      /* Only at odd passes we check go to the next byte in the stack */
02920     if ((uiLoopSlot & 0x01) == 0x01) { pTaskSlot++;  }
02921     uiLoopSlot++; }
02922   /* If we found nothing, return false. If we were not searching but increasing we should not arrive here,
02923    * for it indicates we could not add the slot. If we where decreasing arriving here means the there was no
02924    * slot with the given number that could be removed. If this is an error depends on the caller. */
02925   #if (cfgCheckMethodUse == cfgTrue)
02926     /* Test if we arrived here while increasing, if so we could not add the slot for the stack is full.
02927      * Since we are potentially not stopping the current task, we want to return after the error. */
02928     if ((uiControlTaskNumber & defSlotOperateIncreaseMask) == defSlotOperateIncreaseAction)
02929     { privShowError((errSlotIncreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); }
02930     /* Test if we arrived here while decreasing and did not yet succeed in doing so. If so the slot
02931      * was not present and this is considered an error. If we where decreasing and searching, we did
02932      * not find a second slot so we should return false, this is normal operation */
02933     if ( ((uiControlTaskNumber & defSlotOperateDecreaseMask) == defSlotOperateDecreaseAction) &&
02934          ((uiLoopControl & defSlotLoopSearchMask) != defSlotLoopSearchActive) )
02935     { /* Since we are potentially not stopping the current task, we want to return after the error. */
02936       privShowError((errSlotDecreaseFail | errTaskStateInfo | errOsStateForce), callIdSystem, ((uiSlotSlot << errInfoNumberShift) | ((uiControlTaskNumber & defTaskNumberGetMask) << errTaskNumberShift)) ); }
02937   #endif
02938   return false; }
02939 
02940 #endif
02941 
02942 #if (cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))
02943 
02944 static Tuint08 privFreeLockAbsent(Tuint08 uiSlot)
02945 { /* This method is used to see if a particular slot is occupied by an other task as a free lock.
02946    * Thus it returns true when the slot is totally unused, returns true when it is only used
02947    * as blocking slots or if you call it with the free Slot. Note that it returns false if the
02948    * slot has been used in a task as free, even if that task was later blocked. */
02949   /* Just to be sure */
02950   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02951     privCheckOsStackLevel();
02952   #endif
02953   if (uiSlot != defSlotFree)
02954   { Tuint08 uiLoopTask;
02955     /* Run trough all tasks that use slots */
02956     for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++)
02957     { /* Search for the use of this slots as free slots in de the slotstack. privOperateSlotStack returns
02958        * true if the slot is reported free in this task. If so we may stop here, if not we must continue
02959        * to search. */
02960       if ( privOperateSlotStack((uiLoopTask | defSlotOperateSearchFree ), uiSlot ) ) { return false; }  } }
02961   /* If we have not found any free use of the slot, the slot is not used as such. */
02962   return true; }
02963 
02964 #endif
02965 
02966 
02967 #if (cfgUseSynchronization != cfgSyncNon) || (cfgUseFileSystem == cfgTrue) || (cfgUseEvents == cfgTrue)
02968 
02969 static void privUnblockTask(Tuint08 uiControlTaskNumber)
02970 { /* This method unlocks / unblocks a task for you, depending on the bits in uiControlTaskNumber.
02971    * It can handle fs blocks and syncblocks. Only call this on blocking tasks, since it will
02972    * always perform a deblock, there is no test if the task is indeed blocked. Terminated and
02973    * suspended tasks are ignored. It may NOT be called on shared tasks. */
02974   TtaskExtendedControlBlock * taskTCB = (TtaskExtendedControlBlock *) privTcbList(uiControlTaskNumber & defTaskNumberGetMask);
02975   /* Just to be sure */
02976   #if (cfgCheckOsStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue)
02977     privCheckOsStackLevel();
02978   #endif
02979     /* Check if the task is not terminated or suspended, since they may not be unlocked, for all the
02980    * other types (sleeping included) there is no problem  */
02981   if (taskTCB->uiTaskStatus >= defBaseSleepingTask)
02982   { /* First we see if the caller wanted to unlock the task too. */
02983     if ((uiControlTaskNumber & defParaLockMask) == defParaLockStateUnlock)
02984     {
02985       #if (cfgUseSynchronization != cfgSyncNon)
02986         if ((taskTCB->uiTaskStatus & defBaseModeGetMask) == defBaseModeSync)
02987         { /* If we make use of events, the test above also passes for event unlocks, thus we
02988            * must explicitly test in that case that we do not have an event unlock. This could
02989            * lead to the erasure of free slots. */
02990           #if (cfgUseEvents == cfgTrue)
02991             if ((taskTCB->uiTaskStatus & defBaseDressGetMask) == defBaseDressSlot)
02992           #endif
02993           { /* Unlocking means setting the lowest slot number to zero, the 'free lock'. Note we assume that
02994              * slot is the correct one, for we came from a blocking situation. */
02995             #if (cfgCheckTrace == cfgTrue)
02996               /* First we report which task is currently running */
02997               privTrace(traceSlotUnlock | (uiControlTaskNumber & defTaskNumberGetMask) );
02998               /* Report the slots */
02999               privTrace(taskTCB->uiTaskSlot);
03000             #endif
03001           /* Clean the lock, depending on the type  */
03002             #if (cfgUseSynchronization == cfgSyncSingleSlot)
03003               /* We may clean all of it, since the left nibble is not used */
03004               taskTCB->uiTaskSlot = defSlotFree;
03005             #elif (cfgUseSynchronization == cfgSyncSingleBlock)
03006               /* We must be careful for the left nibble may contain a free lock */
03007               taskTCB->uiTaskSlot = (taskTCB->uiTaskSlot & defSlotRightSetMask) | defSlotRightFree;
03008             #elif (cfgUseSynchronization == cfgSyncDoubleBlock)
03009               /* We must be sure to clean all blocking slots. */
03010               taskTCB->uiTaskSlot = defSlotFree;
03011             #else
03012               #error "You should not arrive here."
03013             #endif
03014         } }
03015       #endif
03016       #if (cfgUseFileSystem == cfgTrue)
03017         /* Check if the task is locked on the file system. (Note: shared tasks will pass this
03018          * test, therefore it may not be called on shared tasks.) */
03019         if ((taskTCB->uiTaskStatus & defBaseModeGetMask) == defBaseModeAccess)
03020         { /* If so, we clean the read/write modes. Clean the locks, if present. Note we only do so
03021            * when requested to unlock. This is the case in for example when a delay occurs. */
03022           #if (defUseFsField == cfgTrue)
03023             taskTCB->defFsField = (taskTCB->defFsField & (defFsReadSetMask & defFsWriteSetMask)) | (defFsReadClear | defFsWriteClear);
03024           #endif
03025         }
03026       #endif
03027       /* The last situation, namely, the Events, do not need standard unlocking operations, for we can
03028        * only arrive here if the uiTaskEvents has already be cleared. The only exception being in case
03029        * of timeouts. Also, there is no harm in cleaning the TaskEvent if we come from an unlock due
03030        * to a slot locking. Since if the task was blocking on a slot, it could not have been blocking on
03031        * an event to happen. In that case the TaskEvent variable should already be cleared. Thus */
03032       #if (cfgUseEvents == cfgTrue) && (cfgUseTimeout == cfgTrue)
03033         taskTCB->uiTaskEvents = defAllEventsReset;
03034       #endif
03035     }
03036     privTrace(traceTaskRelease | (uiControlTaskNumber & defTaskNumberGetMask) );
03037     /* ... if so, unblock the task and also remove a possible timeout. If we forget this, the task will
03038      * turn into a delayed state. This is needed since we also unblock tasks which are not the current ones.
03039      * A sleeping state may be deblocked and delay-waked, but must stay sleeping. Shared tasks are not allowed. */
03040     taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & (defBaseModeSetMask & defBaseBlockStateSetMask & defBaseDelayStateSetMask & defBaseDressSetMask)) | (defBaseModeSync | defBaseBlockStateFree | defBaseDelayStateWake | defBaseDressDone);
03041     /* If we must return a value, this can only be the case after an unblock of course. Without an unblock, there
03042      * is no task to return something to. */
03043     #if (defUseBoolReturns == cfgTrue)
03044       /* The return value is stored in the defRetField, and consists of two bits, one for true/false, one to
03045        * indicate if there is something to return. */
03046       taskTCB->defRetField = (taskTCB->defRetField & defRetSetMask) | ((uiControlTaskNumber & defParaRetMask) >> defParaRetToRetShift);
03047     #endif
03048    /* If we want to keep track of accurate wake times, and we have release a task, we must copy the current
03049     * time to the delay fields, since this is the place where the time of last activation is kept.*/
03050     #if (defUseDelay == cfgTrue) && (cfgUseCorrectWakeupTimes == cfgTrue)
03051       taskTCB->uxDelay.Full = uxTickCount.Full;
03052     #endif
03053   } }
03054 
03055 #endif
03056 
03057 
03058 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue)
03059 
03060 static void privLiftLocksOnSlot(Tuint08 uiSlot)
03061 { /* Arrive here when you need to increase the priorities of several tasks in a slot
03062    * of a task about to be blocked. By increasing those priorities to the level of
03063    * the blocking slot, these tasks have a chance to finish, and not keep the current
03064    * blocking task for ever. Note that we do not level all priorities in one slot,
03065    * since due to the fact that tasks may be blocked on two slots simultaneously this
03066    * would be a very costly to sort out. This mechanism tries to keep it simple.*/
03067   TtaskControlBlock * curTCB = privTcbList(privTaskNumber(defCurrentTaskNumber));
03068   /* Get the priority of the current task. Note we do not need to shift the
03069    * priority since we will only compare priorities among each other, we do not
03070    * need the exact value. */
03071   Tuint08 uiCurTaskPriority = curTCB->uiTaskStatus & defBasePrioGetMask;
03072   Tuint08 uiLoopTask;
03073   TtaskControlBlock * loopTCB;
03074   /* Run through all task possible having a slot. */
03075   for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++)
03076   { loopTCB = privTcbList(uiLoopTask);
03077     /* Test if the task indeed holds the slot. We do not distinguish between blocked and
03078      * locked only. Solely possessing the lock is enough to rise its priority. Sometimes
03079      * this may imply we rise tasks without a real need, but it would be to costly to
03080      * analyze on which slot the blocking takes place. */
03081     if (privOperateSlotStack((uiLoopTask | defSlotOperateSearchDefault), uiSlot))
03082     { /* Get the priority of the looped task, again, don't shift */
03083       Tuint08 uiLoopTaskPriority = loopTCB->uiTaskStatus & defBasePrioGetMask;
03084       /* Compare which is larger */
03085       if (uiCurTaskPriority>uiLoopTaskPriority)
03086       { /* and if the priotity of the current task (which will be blocked and thus inactivates
03087          * was larger, lift the priority of the other task to that of the current task. The
03088          * present priority gets lost.  */
03089         loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBasePrioSetMask) | uiCurTaskPriority; } } } }
03090 
03091 #endif
03092 
03093 
03094 #if (cfgUseSynchronization != cfgSyncNon) && (cfgUsePriorityLifting == cfgTrue)
03095 
03096 static void privRestoreInitialPriority(Tuint08 uiTaskNumber)
03097 { /* Arrive here if we need to restore the priority of a lifted task. The priority
03098    * is reset to the original value that is stored in flash. First we test if the
03099    * task does not hold any Queue's or Mutexes any more. */
03100  if ( privOperateSlotStack((uiTaskNumber | defSlotOperateQMAbsent ), defSlotFree ) )
03101  { /* If so it now makes sense to restore the priority. */
03102    TtaskControlBlock * taskTCB = privTcbList(uiTaskNumber);
03103    /* Read the initial priority from flash, or use the constant */
03104    #if (defInitialStatusConstant == cfgTrue)
03105      Tuint08 uiInitialStatus = defInitialStatusFixed;
03106    #else
03107      Tuint08 uiInitialStatus = portFlashReadStruc(TtaskDefinitionBlock,pxTDBlist[uiTaskNumber],Tuint08,uiInitialStatus);
03108    #endif
03109    /* Restore its value. */
03110    taskTCB->uiTaskStatus = (taskTCB->uiTaskStatus & defBasePrioSetMask) | (uiInitialStatus & defBasePrioGetMask); } }
03111 
03112 #endif
03113 
03114 
03115 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
03116 
03117 /* The queue mechanism allows for several tasks writing and reading bytes. The OS
03118  * locks all tasks not able at that moment to write or read the required quantity of bytes.
03119  * Ah, and take care, the queues (and locks for that matter) are numbers from 1 and
03120  * onwards. Thus, slot 0 is forbidden, since it indicates a free slot. But queue's
03121  * are stored in arrays, thus slot 1 corresponds to queue 0 etc. Therefore you see
03122  * [uiSlot-1] everywhere. */
03123 
03124 /* DISCUSSION
03125  * Except in the beginning, the Read (pointer) and Write (pointer) are never equal.
03126  * This is because otherwise you could not see the difference between a
03127  * full and an empty queue. This would imply that one byte is effectively lost,
03128  * for it could never be retrieved. Such a waste! We must solve this
03129  * by storing one bit of information that differentiates between a full and an
03130  * empty queue, when Read and Write are equal. But where?
03131  * It seems reasonable to limit the size of queue's to 127 bytes. Now we have
03132  * two extra bits available (upper bits of Read/Write storage). Second advantage
03133  * is that we can address the whole queue, from the queue Requests. What we don't
03134  * know at this moment if this will cost a lot of code.
03135  * Now, we decided to reserve a bit to indicate that the queue is full, is
03136  * it handy to use the other for indicating an empty queue? Well, it may be,
03137  * but, since queue's are empty at startup, we want to indicate the bit
03138  * empty by zero. Hmmm, that implies a lot of code. It may be shorter to
03139  * just ask for Read==Write and !full. Ok let us see what happens.
03140  * Now we keep the write pointers in uiQueuWrite[] and the read pointers in
03141  * uiQueuRead[]. The write operation is write-post-decrement, and the read
03142  * operation likewise. The pointers wrap when they hit the bottom of the queue.
03143  * Thus we write/read downwards, the wrapping is easier detected that way.
03144  */
03145 
03146 
03147 static Tuint08 privQueuTest(Tuint08 uiSlot, Tsint08 siFreeFilling)
03148 { /* If we want to know the size of a queue use this function, or if we
03149    * want to test if a certain number of bytes will fit.  We use negative numbers
03150    * for writing, and positive numbers for reading. */
03151   /* The size information of the queue is in flash, note the shift from slot numbers
03152    * to queue array numbering. */
03153   Tuint08 uiSize = privGetQueuSize(uiSlot-1);
03154   /* Parameter FreeFilling zero is miss-used to request the size of the queue. */
03155   if (siFreeFilling==0) { return uiSize; }
03156   /* The information about the write and read positions are stored in two arrays, the
03157    * uiQueuWrite and the uiQueuRead. Obtain the write pointer. That pointer points to
03158    * the location that has to be written. */
03159   Tuint08 uiWrite = uiQueuWrite[uiSlot-1];
03160   /* Extract the full bit. We assume it has been set and managed correctly, although it
03161    * only has a meaning when uiWrite==uiRead, for this is the only situation where we
03162    * have ambiguity. */
03163   Tbool bFull = (uiWrite & defQueuFillingGetMask) == defQueuFillingStateFull;
03164   /* If the queue is full ... */
03165   if (bFull)
03166   { if (siFreeFilling>0)
03167     { /* we can read as many bytes from the queue as it is long, */
03168       return uiSize; }
03169     else
03170     { /* but we cannot write anything anymore to it. */
03171       return 0; } }
03172   /* In other cases we must calculate the number, extract the read pointer.  */
03173   Tuint08 uiRead = uiQueuRead[uiSlot-1];
03174   /* Now calculate how many readable characters there are. The situation that the queue
03175    * might be full was already resolved, so in the calculation below it is assumed the
03176    * queue is not full, i.e. uiWrite==uiRead implies it is empty. Make sure the calculation
03177    * never produced negative numbers. */
03178   Tuint08 uiReadable  = uiWrite<=uiRead ? uiRead - uiWrite : uiSize - (uiWrite - uiRead);
03179   /* Every byte that is not readable, must be writable. */
03180   Tuint08 uiWriteable = uiSize - uiReadable;
03181   /* Now decide what to return, for positive siFreeFilling we asks for the readable characters,
03182    * for negative for the writable. */
03183   if (siFreeFilling>0)
03184   { return uiReadable; }
03185   else
03186   { return uiWriteable; } }
03187 
03188 #endif
03189 
03190 
03191 #if (cfgUseSynchronization != cfgSyncNon) && (defUseWaits == cfgTrue)
03192 
03193 static void privReleaseWait(Tuint08 uiSlot)
03194 { /* This is called if we want to clean all locks in the tasks blocked by a wait. Cannot be
03195    * called to clean up Queues or Mutexes. */
03196   Tuint08 uiLoopTask;
03197   /* Loop trough all tasks. */
03198   for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++)
03199   { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask);
03200      /* Check if the task holds a blocking slot with the given number. The slot can never be left,
03201       * since waits cannot be be blocked in pairs. Furthermore, the task must be blocking, wait slots
03202       * are never solely locked. */
03203      if ((loopTCB->uiTaskSlot & defSlotRightGetMask) == uiSlot)
03204      { /* If the slot matches unblock and unlock the task */
03205        privUnblockTask(uiLoopTask | defParaLockStateUnlock | defParaRetStateTrue); } } }
03206 
03207 #endif
03208 
03209 
03210 #if (cfgUseSynchronization != cfgSyncNon) && (defUseQueus == cfgTrue)
03211 
03212 static Tbool privSizeFitsQueu(Tuint08 uiSlot, Tsint08 siFreeFilling)
03213 { /* Call this if you need to test if a queue on a slot may fit the filling condition. First
03214    * test if we indeed have a queue, otherwise there is noting to test. */
03215   if ((uiSlot  >= defNumberQueuBegin) || (uiSlot  < defNumberQueuEnd ))
03216   { Tuint08 uiTest;
03217     /* Now we test the siFreeFilling. This method returns the number of bytes that can read from / written to
03218      * the queue, depending on the sign of siFreeFilling. If siFreeFilling==0 it returns the queue size (which
03219      * value we do not need here) The number returned is always positive. */
03220     uiTest = privQueuTest(uiSlot,siFreeFilling);
03221     /* If we want to read we may enter if we want to read not more bytes as are available */
03222     if (siFreeFilling>0) { return ( ((Tuint08) siFreeFilling) <= uiTest); }
03223     /* If we want to read we may enter if we want to read not more bytes as are available */
03224     if (siFreeFilling<0) { return ( ((Tuint08) (-siFreeFilling)) <= uiTest); } }
03225   /* The default is true, i.e. you get the lock and it remains in effect if the siFreeFilling == 0 or
03226    * we have no queue at all. */
03227   return true; }
03228 
03229 #endif
03230 
03231 
03232 #if ((cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue))) && 0
03233 
03234 static void privReleaseSyncBlockingTasks(void)
03235 { /* Arrive here if you have just completed putting sum data on the queue, or used a Mutex
03236    * and are done and have released your own lock. Or call it when the task is
03237    * initilialized or terminated. It runs through all tasks, and unblocks (not unlock)
03238    * the first task that meets the conditions. This may not be the one with the highest
03239    * priority. It does not operate on waitblocks (since they cannot, and should not,
03240    * be released like this) */
03241   Tuint08 uiLoopTask;
03242   /* Run through all tasks possibly having a slot. */
03243   for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++)
03244   { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask);
03245     /* First check it this is a blocking task (a non blocking task does not need to
03246      * be released) Furthermore, it must be a task that is able to run, otherwise way
03247      * may release a task which is for instance suspended, and that will hold the lock
03248      * for ever. */
03249      if ((loopTCB->uiTaskStatus & ( defBaseStopStateGetMask | defBaseModeGetMask | defBaseBlockStateGetMask | defBaseDressGetMask))  == (defBaseStopStateGo | defBaseModeSync | defBaseBlockStateBlocked | defBaseDressSlot) )
03250      { /* If yes, extract on which slots this task is blocking */
03251        Tuint08 uiRightSlot = (loopTCB->uiTaskSlot & defSlotRightGetMask) >> defSlotRightShift;
03252        /* Now we must test if this is not a wait-slot */
03253        #if (defUseWaits == cfgTrue)
03254          if (uiRightSlot < defNumberWaitBegin)
03255        #endif
03256        { /* If we arive here we are dealing with a queue of mutex. Now, depending on if we are working
03257           * with doublelocks, we may need the left slot too. Waits and mutex/queues can never mix, so we
03258           * do not need to worry */
03259          #if (cfgUseSynchronization == cfgSyncDoubleBlock)
03260            Tuint08 uiLeftSlot  = (loopTCB->uiTaskSlot & defSlotLeftGetMask) >> defSlotLeftShift;
03261            /* Now we must test if these slots are unlocked on some other task */
03262            if (privFreeLockAbsent(uiLeftSlot) && privFreeLockAbsent(uiRightSlot))
03263            { /* If not, we must test if the filling conditions on this task are fulfilled
03264               * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If
03265               * we do not have queues at all testing is not needed. */
03266             #if (defUseQueus == cfgTrue)
03267               if (privSizeFitsQueu(uiLeftSlot,loopTCB->siQueuLeftLock) && privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock))
03268             #endif
03269         #else
03270            /* Now we must test if these slots are unlocked on some other task */
03271            if (privFreeLockAbsent(uiRightSlot))
03272            { /* If not, we must test if the filling conditions on this task are fulfilled
03273               * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If
03274               * we do not have queues at all testing is not needed. */
03275             #if (defUseQueus == cfgTrue)
03276               if (privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock))
03277             #endif
03278         #endif
03279           { /* This task seems to fulfill all requirements to be released. Make it so. */
03280             privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue); } } } } } }
03281 
03282 
03283 #endif
03284 
03285 
03286 #if ((cfgUseSynchronization != cfgSyncNon) && ((defUseMutexes == cfgTrue) || (defUseQueus == cfgTrue)))
03287 
03288 static void privReleaseSyncBlockingTasks(void)
03289 { /* Arrive here if you have just completed putting sum data on the queue, or used a Mutex
03290    * and are done and have released your own lock. Or call it when the task is
03291    * initialized or terminated. It runs through all tasks, and unblocks (not unlock)
03292    * the first task with the highest priority that meets the conditions. When having
03293    * equal priority tasks with a timeout prevail above those without.
03294    * It does not operate on wait blocks (since they cannot, and should not,
03295    * be released like this) */
03296   /* Variable to run through all tasks. */
03297   Tuint08 uiLoopTask;
03298   #if (cfgUsePrioritizedRelease == cfgTrue)
03299     /* Variable store the most likely candidate so far. */
03300     Tuint08 uiSelectedTask;
03301     /* Variable store the modified priority of the most likely candidate so far. */
03302     Tuint08 uiSelectedLevel = 0;
03303   #endif
03304   /* Run through all tasks possibly having a slot. */
03305   for (uiLoopTask=0; uiLoopTask<defNumberOfTasksWithSlot; uiLoopTask++)
03306   { TtaskExtendedControlBlock * loopTCB = (TtaskExtendedControlBlock *) privTcbList(uiLoopTask);
03307     /* First check it this is a blocking task (a non blocking task does not need to
03308      * be released) Furthermore, it must be a task that is able to run, otherwise way
03309      * may release a task which is for instance suspended, and that will hold the lock
03310      * for ever. */
03311      if ((loopTCB->uiTaskStatus & ( defBaseStopStateGetMask | defBaseModeGetMask | defBaseBlockStateGetMask | defBaseDressGetMask))  == (defBaseStopStateGo | defBaseModeSync | defBaseBlockStateBlocked | defBaseDressSlot) )
03312      { /* If yes, extract on which slots this task is blocking */
03313        Tuint08 uiRightSlot = (loopTCB->uiTaskSlot & defSlotRightGetMask) >> defSlotRightShift;
03314        /* Now we must test if this is not a wait-slot */
03315        #if (defUseWaits == cfgTrue)
03316          if (uiRightSlot < defNumberWaitBegin)
03317        #endif
03318        { /* If we arive here we are dealing with a queue of mutex. Now, depending on if we are working
03319           * with doublelocks, we may need the left slot too. Waits and mutex/queues can never mix, so we
03320           * do not need to worry */
03321          #if (cfgUseSynchronization == cfgSyncDoubleBlock)
03322            Tuint08 uiLeftSlot  = (loopTCB->uiTaskSlot & defSlotLeftGetMask) >> defSlotLeftShift;
03323            /* Now we must test if these slots are unlocked on some other task */
03324            if (privFreeLockAbsent(uiLeftSlot) && privFreeLockAbsent(uiRightSlot))
03325            { /* If not, we must test if the filling conditions on this task are fulfilled
03326               * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If
03327               * we do not have queues at all testing is not needed. */
03328             #if (defUseQueus == cfgTrue)
03329               if (privSizeFitsQueu(uiLeftSlot,loopTCB->siQueuLeftLock) && privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock))
03330             #endif
03331         #else
03332            /* Now we must test if these slots are unlocked on some other task */
03333            if (privFreeLockAbsent(uiRightSlot))
03334            { /* If not, we must test if the filling conditions on this task are fulfilled
03335               * by the queue. If it is a mutex, privSizeFitsQueu returns true per default. If
03336               * we do not have queues at all testing is not needed. */
03337             #if (defUseQueus == cfgTrue)
03338               if (privSizeFitsQueu(uiRightSlot,loopTCB->siQueuRightLock))
03339             #endif
03340         #endif
03341           { /* This task seems to fulfill all requirements to be released. */
03342             #if (cfgUsePrioritizedRelease == cfgTrue)
03343               /* See if it is a candidate.
03344                * By swapping the nibbles we get the priority before the delay status so that we compare
03345                * first on priority and subsequently on delay. (the other bits of the pattern 110dppp1
03346                * are fixed */
03347               Tuint08 uiCandidateLevel = loopTCB->uiTaskStatus;
03348               portSwapNibbles(uiCandidateLevel);
03349               /* So the candidate level is of the form ppp1110d. If we do not have a selected level yet,
03350                * or the new task is higher */
03351               if (uiSelectedLevel < uiCandidateLevel)
03352               { /* Copy the new level for further comparison. */
03353                 uiSelectedLevel = uiCandidateLevel;
03354                 /* Copy the task number for unblocking at the end. */
03355                 uiSelectedTask = uiLoopTask; }
03356             #else
03357               /* unblock the task. */
03358               privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue);
03359               /* we are done. Quickly terminate this loop by increasing the loop task number.
03360                * (do NOT use return here, costs 28 bytes extra! */
03361               uiLoopTask = defCurrentTaskNumber;
03362             #endif
03363           } } } } }
03364   #if (cfgUsePrioritizedRelease == cfgTrue)
03365     if (uiSelectedLevel != 0) { privUnblockTask(uiSelectedTask | defParaLockStateKeep | defParaRetStateTrue); }
03366   #endif
03367   }
03368 
03369 #endif
03370 
03371 
03372 #if (cfgUseLowPowerSleep == cfgTrue) && (includeTaskSleepAll == cfgTrue)
03373 
03374 static void privPutAllTasksToSleep(void)
03375 { /* Call this if you want to put all tasks to sleep. It will put all running and
03376    * blocked, delayed tasks to sleep. If will check if files system operations
03377    * are busy, or if tasks are waiting for fs operations. Those tasks may run
03378    * until the fs actvities are over, and are subsequently put to sleep. It will
03379    * leave suspended and terminated tasks alone. */
03380   Tuint08 uiLoopTask;
03381   /* Run through all tasks. */
03382   for (uiLoopTask=0; uiLoopTask<defNumberOfTasks; uiLoopTask++)
03383   { TtaskControlBlock * loopTCB = privTcbList(uiLoopTask);
03384     /* Putting a task to sleep, we do not want to revive a suspended task via the
03385      * sleep. An already sleeping task requires no attention. Thus we must check if
03386      * the task we put to sleep was indeed running. */
03387     if ((loopTCB->uiTaskStatus & defBaseStopStateGetMask) == defBaseStopStateGo )
03388     { /* Putting a task blocked on the fs to sleep may lead to a crash at wakeup,
03389        * so putting these tasks to sleep must be postponed until later. In that case
03390        * we will return here. The reason is we have no way to specially store the kind of
03391        * block in a sleepstate, so at wakeup we cannot correctly reconstruct the state.
03392        * It is no problem if the task is actually reading from the file system
03393        * for such a task is not blocked, but writing files cannot be put
03394        * to sleep. First of all, this may lead to data loss, but, second, a sleep
03395        * instruction cannot be effectuated when a burnlock is active. Thus we must
03396        * check if any task is writing. If there are writing tasks, all tasks that
03397        * want to read are readblocked, but if there are no writing tasks, readers
03398        * cannot be blocked, for they are released immediately after the writing
03399        * has finished. Thus we come to the conclusion there is no real need to store
03400        * a read-block-sleeping state. */
03401       #if (cfgUseFileSystem == cfgTrue)
03402         /* Thus we exclude all those tasks from sleeping if the fs writeblock is activated
03403          * which are in read or write mode. (We cannot test for writeblock itself, since
03404          * there is always one task actually writing and not blocking. */
03405         if ( ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear) ||
03406            ((loopTCB->defFsField & (defFsReadGetMask | defFsWriteGetMask)) == (defFsReadClear | defFsWriteClear)) )
03407       #endif
03408       /* Change the status to sleeping */
03409       { loopTCB->uiTaskStatus = (loopTCB->uiTaskStatus & defBaseSleepingSetMask) | defBaseSleepingTask; } } } }
03410 
03411 #endif
03412 
03413 
03414 #if (cfgUseFileSystem  ==  cfgTrue)
03415 
03416 static void privReleaseFileBlocks(void)
03417 { /* Call this method after you are done with file operations on a task and new tasks
03418    * may be given the opportunity to be released. Usually this is after a fileClose on
03419    * a file write operation, or after all reads are finished. */
03420   /* First check if we have more task on the file system. If not, we can only arrive
03421    * here if the current (and only!) task has completed read or write operations and
03422    * closes the access to the file system. Now, since there are no other tasks that
03423    * may want access we have little to do. */
03424   #if (defUseFsOnMultipleTasks == cfgTrue)
03425     /* It seems we have. We have to see in which state the file system is
03426      * Check if we have a write lock. When we have a write lock there must be at least
03427      * one task actually writing, so it makes no sense to release anything. When we don't
03428      * have a write lock we may have a read lock (i.e. there are tasks actually reading, but
03429      * we still search for tasks that may want to write. If we find these, we should not
03430      * accept new reads, until the tasks wanting to write has had the opportunity to do so. */
03431     if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear)
03432     { /* It seems we don't have a write task, so loop through all tasks and search
03433        * for write blocks. We only search in those tasks making use of the file system. */
03434       Tuint08 uiLoopTask;
03435       for (uiLoopTask=defTaskNumberFileSystemBegin; uiLoopTask<defTaskNumberFileSystemEnd; uiLoopTask++)
03436       { /* Now we must check if this is indeed a task with a file block (it could be an other kind of block) and if this
03437          * block is in the write mode. */
03438         if ((privTcbList(uiLoopTask)->uiTaskStatus & (defBaseFileBlockedGetMask | defBaseDressGetMask)) == (defBaseFileBlockedTask | defBaseDressWrite))
03439         { /* All right, this task is waiting for an opportunity to write to the file system.
03440            * There are two possibilities, or we can directly deblock to a lock, or we must issue
03441            * a write request. In both cases we must activate the write block in the file system
03442            * status (If there is already a read block present, this write block works as a local
03443            * flag signaling that a task is waiting for write access. Note we can never arrive here
03444            * in the other situation where both read and write block are activated, namely a writing
03445            * task waiting for file close. */
03446           uiFsStatus = (uiFsStatus & defFsWriteBlockSetMask) | defFsWriteBlockActive;
03447           /* If there is not single-write-multiple-read synchronizer active, read blocks are not
03448            * possible (we read under the write lock, this is always permitted. */
03449           #if (cfgUseFileSystemConcurrentRead == cfgTrue)
03450             /* Now, if the read block is set we are done, cause the write request is already set,
03451              * nothing more can be done. In case it is not set, we may appoint access to the current
03452              * task, and give it write access. */
03453             if (((uiFsStatus & defFsReadBlockGetMask) == defFsReadBlockClear))
03454           #endif
03455           { /* Here we must activate the task for writing, which implies that its task number is put into
03456              * the first nibble of the FsStatus. Furthermore, we clear the burn lock (it should already be
03457              * cleared btw). */
03458             uiFsStatus = ((uiFsStatus & (defFsBurnBlockSetMask & defFsWriteNumberSetMask)) | (defFsBurnBlockClear | (uiLoopTask << defFsTaskNumberShift) ) );
03459             /* The task was already blocked (we tested that, and besides, every task is blocked at
03460              * file-open per default). So we must unblock, but keep the lock. */
03461             privUnblockTask(uiLoopTask | defParaLockStateKeep | defParaRetStateTrue); }
03462           break; } } }
03463     /* If we have simultaneous read possibility we must do some more research. */
03464     #if (cfgUseFileSystemConcurrentRead == cfgTrue)
03465       /* It may seem strange, but we start again by looking at the WriteBlokc bit in the
03466        * FS status. If the bit is set, there are two possibilities. First, the read bit is
03467        * still cleared, so there is write action, and no further action with respect to reading
03468        * is needed. If the read bit is set also, it must be been set before we entered this method,
03469        * and the write bit must be interpreted as write request. In that case no new read tasks
03470        * may be activated. Same result, nothing to do. */
03471       if ((uiFsStatus & defFsWriteBlockGetMask) == defFsWriteBlockClear)
03472       { /* No write bit, thus there is room for a new read task. Run through all tasks. */
03473         Tuint08 uiLoopTask;
03474         for (uiLoopTask=defTaskNumberFileSystemBegin; uiLoopTask<defTaskNumberFileSystemEnd; uiLoopTask++)
03475         { /* Now we must check if this is indeed a task with a file block (it could be an other kind of block) and if this
03476            * block is in the read mode. */
03477           if ((privTcbList(uiLoopTask)->uiTaskStatus & (defBaseFileBlockedGetMask | defBaseDressGetMask)) == (defBaseFileBlockedTask | defBaseDressRead))
03478           { /* This task want to perform a read action. This is certainly possible, without further restrictions
03479              * since reading may be done in parallel. */
03480             uiFsStatus