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 #if (cfgCheckReset == cfgTrue) || (defCheckReportingError == cfgTrue) 00030 00034 static void portShortDelay(Tuint08 uiLoops); 00035 #endif 00036 00037 00038 #if (defSysGCCstartup != cfgReplace) 00039 00040 void portInit(void) 00041 { /* Switch to the highest internal clock frequency (8MHz) Note that the need of this action 00042 * depends on the setting of the fuse CKDIV8. If that is unprogrammed (i.e. set to 1) these 00043 * instructions are not needed. However, the device is shipped with a programmed fuse. */ 00044 #if (devCLKPCE != cfgUndefined) 00045 /* Not all hardware has prescalers but if it does, set the values here. We set it to 00046 * the highest clock frequency. This may not be what you want because of the power 00047 * consumption. Set an other value if needed. */ 00048 devCLKPR = preBitSet1(0x00,devCLKPCE); 00049 devCLKPR = defClockPrescaleBits; 00050 #endif 00051 /* If we use low power sleep we disable watchdog, only works in safety level 1 00052 * (in safety level 2 fuses need to be reset). Please note that it may be necessary 00053 * to perform this steps on devices that where switched off during sleep, and are 00054 * now used for other purposes. */ 00055 #if (cfgUseLowPowerSleep == cfgTrue) || (includeGenReboot == cfgTrue) 00056 /* Disable the watchdog, since WDRF overrides the WDE, we must clear it first. 00057 * Furthermore, we clear all other flags, since we have no interest in them. 00058 * If you do, alter the code here to preserver the flags. */ 00059 devMCUSR = preBitClr4(0x00,devWDRF,devBORF,devEXTRF,devPORF); 00060 /* Enable configuration change. */ 00061 devWDTCR = preBitSet2(0x00,devWDCE,devWDE); 00062 /* Disable Watchdog, do NOT write any other bits! */ 00063 devWDTCR = preBitClr1(0x00,devWDCE); 00064 #endif 00065 /* If we make use of tracing, we must configure it at the very beginning. Note we use 00066 * bitbanging because this is the most portable. */ 00067 #if (cfgCheckTrace == cfgTrue) 00068 /* Set the data pin to output, we use it to transport the bits. */ 00069 devTraceComDRR = preBitSet1(devTraceComDRR,devTraceDataPin); 00070 /* Set the ready pin to output, we use it to signal the data is available. */ 00071 devTraceComDRR = preBitSet1(devTraceComDRR,devTraceReadyPin); 00072 /* Set the acknowledge pin to output, we use it see if the data has been read. */ 00073 devTraceComDRR = preBitClr1(devTraceComDRR,devTraceAckPin); 00074 #endif 00075 /* Done */ } 00076 00077 #endif 00078 00079 00080 00081 #if (cfgCheckTrace == cfgTrue) 00082 00083 void portTrace(Tuint08 uiEvent) 00084 { /* This routine shifts one byte through an handshaked bitbanging protocol. */ 00085 Tuint08 uiBitLoc = 0x80; 00086 /* uiBitLoc indicates the bit location of the bit from uiEvent that is about to be send. The loop 00087 * runs until all bits have been send. Since this is a full handshake protocol, make sure your 00088 * data collector is quick enough, otherwise timing errors may occur. */ 00089 while (uiBitLoc != 0) 00090 { /* If the bit is set, set the output pin, otherwise clear it. */ 00091 if ((uiEvent & uiBitLoc) != 0x00) 00092 { devTraceComPORT = preBitSet1(devTraceComPORT,devTraceDataPin); } 00093 else 00094 { devTraceComPORT = preBitClr1(devTraceComPORT,devTraceDataPin); } 00095 /* Send the listener that the data is ready. */ 00096 devTraceComPORT = preBitSet1(devTraceComPORT,devTraceReadyPin); 00097 /* Wait until the data has been read and the listener told so. */ 00098 while (preBitIsClr(devTraceComPIN,devTraceAckPin)); 00099 /* Go to the next bit */ 00100 uiBitLoc >>= 1; 00101 /* If the bit is set, set the output pin, otherwise clear it. */ 00102 if ((uiEvent & uiBitLoc) != 0x00) 00103 { devTraceComPORT = preBitSet1(devTraceComPORT,devTraceDataPin); } 00104 else 00105 { devTraceComPORT = preBitClr1(devTraceComPORT,devTraceDataPin); } 00106 /* Send the listener that the data is ready. */ 00107 devTraceComPORT = preBitClr1(devTraceComPORT,devTraceReadyPin); 00108 /* Wait until the data has been read and the listener told so. */ 00109 while (preBitIsSet(devTraceComPIN,devTraceAckPin)); 00110 /* Go to the next bit */ 00111 uiBitLoc>>= 1; } 00112 /* Done. Note that if the transmission of even one bit fails, this protocol hangs for ever, so 00113 * keep your lines short. */ } 00114 00115 #endif 00116 00117 00118 00119 #if (defCheckReportingError == cfgTrue) 00120 00121 void portShowError(Tuint08 uiMessage, Tuint08 uiCallId, Tuint08 uiInfoTask) 00122 { /* We display the error code on devErrorComPORT. If you use the stk500 with eight leds, this can be 00123 * seen quite nicely. Specify that we want the message to be repeated 16 times. We arrive here 00124 * with disabled tick and global interrupts. */ 00125 Tuint08 uiCount = 16; 00126 /* The error Port must be configured as output on all pins. We do not bother to store its original 00127 * value, since we must solve the error first. */ 00128 devErrorComDDR = 0xFF; 00129 /* Send the error message several times. In order to make them catch the eye we have a special 00130 * blinking scheme. */ 00131 while (uiCount--) 00132 { /* Draw the attention of the user by running a led. This also marks the beginning 00133 * of the sequence of information. */ 00134 Tuint08 uiRun = 0x80; 00135 do 00136 { devErrorComPORT = ~uiRun; 00137 portShortDelay(0x02); 00138 uiRun >>= 1; } 00139 while (uiRun); 00140 /* Display the message. */ 00141 devErrorComPORT = ~uiMessage; 00142 /* wait a little while */ 00143 portShortDelay(0x20); 00144 /* Display the caller. */ 00145 devErrorComPORT = ~uiCallId; 00146 /* wait a little while */ 00147 portShortDelay(0x20); 00148 /* Display extra information. */ 00149 devErrorComPORT = ~uiInfoTask; 00150 /* wait a somewhat longer */ 00151 portShortDelay(0x20); } 00152 /* Done, we return, if the error was fatal, this method will be called again automatically. */ } 00153 00154 #endif 00155 00156 00157 #if (cfgCheckReset == cfgTrue) || (defCheckReportingError == cfgTrue) 00158 00159 static void portShortDelay(Tuint08 uiLoops) 00160 { /* Disable all timer interrupts. Since we use this routine to display some (error) information 00161 * we do not want to disturbed by other sources. We only make it explicit for timer 0. 00162 * Note that other timer bits may be destroyed here. */ 00163 devTIMSK = preBitClr2(0x00,devOCIE,devTOIE); 00164 /* Set the timer in normal 8 bit mode and activate the timer with the chosen prescaler. 00165 * Now, on some devices these bits are located on the same register, and on other they 00166 * are not. We must separate. Furthermore, it is assumed it is OK to clear the other 00167 * bits in the register. */ 00168 if (preEqualMacros(devTCCRB,devTCCRA)) 00169 { devTCCRA = preBitClr1(0x00,devWGM) | defDelayPrescaleBits; } 00170 else 00171 { devTCCRA = preBitClr1(0x00,devWGM); 00172 devTCCRB = defDelayPrescaleBits; } 00173 /* operate in the standard 8 bit timer mode (some devices do not have a 16 bit mode. 00174 * Note we use the same timer as the tick interrupt does. */ 00175 /* reset the timer */ 00176 devTCNT = 0x00; 00177 /* clear the overflow flag, so that we start with a clean timer. */ 00178 devTIFR = preBitSet1(0x00,devTOV); 00179 /* If you use the simulator of Atmel, it cannot simulate the timer at this point 00180 * so just skip it. */ 00181 #if (cfgSysDebug == cfgFalse) 00182 /* wait until the time is done. Note we do not use an interrupt, since that routine 00183 * is not at our disposal, but there is no need either. */ 00184 while (uiLoops!=0) 00185 { /* Wait until the timer overflows. */ 00186 while (preBitIsClr(devTIFR,devTOV)); 00187 /* Reset the overflow flag */ 00188 devTIFR = preBitSet1(0x00,devTOV); 00189 /* Every overflow counts as one loop. */ 00190 uiLoops--; } 00191 #endif 00192 /* Done, we have spend some time in this method. */ } 00193 00194 #endif 00195 00196 #if (cfgCheckReset == cfgTrue) 00197 00198 void portShowReset(void) 00199 { /* We blink all leds rapidly on portA. If you use the stk500 with eight leds, this can be 00200 * seen quite nicely. Specify that we want the message to be repeated 5 times. */ 00201 Tuint08 uiCount = 5; 00202 /* Port A must be configured as output on all pins. */ 00203 devErrorComDDR = 0xFF; 00204 /* Show the reset message a number of times. */ 00205 while (uiCount--) 00206 { /* all leds on */ 00207 devErrorComPORT = 0x00; 00208 /* wait a little while */ 00209 portShortDelay(0x01); 00210 /* all leds off */ 00211 devErrorComPORT = 0xFF; 00212 /* wait a little while */ 00213 portShortDelay(0x01); } 00214 /* Done */ } 00215 00216 #endif 00217 00218 00219 #if (cfgUseLowPowerSleep == cfgTrue) && ((cfgUseLowPowerOnDelay == cfgFalse) || !defined(devSigWatchdogTimeout)) 00220 00221 void portSleep(Tuint08 uiTickBlockMinDelay) 00222 { /* We will go to sleep until we are woken by a kiss of the prins (i.e. 00223 * an external interrupt.) The parameter uiTickBlockMinDelay has no 00224 * relevance. Set the sleep mode to power down. This is needed, 00225 * since normally the sleep mode is different during the idle task. 00226 * Make sure we leave the other bits alone. */ 00227 devSMCR = preBitClr2(preBitSet2(devSMCR,devSE,devSM1),devSM0,devSM2); 00228 /* Start sleeping, since we arrive here with interrupts disabled, they must be enabled again 00229 * just before sleeping, to make sure we actually go to sleep before an interrupt comes. 00230 * To make sure we only have one waking up interrupt we must immediately switch off 00231 * interrupts after wakeup. That way we can reliably determine which was the cause 00232 * of the awakening. */ 00233 asm volatile ( 00234 "sei \n\t" /* We reactivate the global interrupt just before we go to sleep so we know for */ 00235 "sleep \n\t" /* sure the sleep will indeed start off. In that way we do not miss an interrupt. */ 00236 "cli \n\t" /* And then we immediately disable them again so there cannot be an extra interrupt */ 00237 ::); /* just after the first interrupt. */ 00238 /* We woke up, first disable the sleep so we do not accidently fall asleep again. */ 00239 devSMCR = preBitClr1(devSMCR,devSE); 00240 /* Cinderella has no idea how long she has slept, so it makes no sense to 00241 * correct the tick counter. We are done sleeping and may return to the OS 00242 * Note we must return with interrupts still switched off! */ } 00243 00244 #endif 00245 00246 00247 #if (cfgUseLowPowerSleep == cfgTrue) && (cfgUseLowPowerOnDelay == cfgTrue) 00248 00249 void portSleep(Tuint08 uiTickBlockMinDelay) 00250 { /* We will go to sleep in periods given by cfgNumSleepPeriod and rounded to an allowed value 00251 * for this device. Global and timer interrupts are disabled at this moment, so we are 00252 * responsible for activating the interrupts on the right moment. Timer interrupts must 00253 * stay disabled. uiTickBlockMinDelay denotes the time we can sleep safely without 00254 * having the risk to wake up any delayed tasks. If uiTickBlockMinDelay is defSleepInfinite 00255 * (0xFF) there are no delayed tasks and we may sleep up to the moment an external interrupt 00256 * arrives. Of course we may sleep longer as uiTickBlockMinDelay but then you must activate 00257 * cfgUseLowPowerDelayRelease or face incorrect wakeup of delayed tasks. For infinite 00258 * sleep this does not matter, since there are no delays in that case and keeping the 00259 * tick counter does not make much sense. */ 00260 /* Calculate how much blocks in terms of the watchdog we may sleep. Please note this generates 00261 * the shortest code when defSleepDivider is a power of two. See the discussion at its 00262 * definition.*/ 00263 Tuint08 uiBlocksToSleep = (uiTickBlockMinDelay / defSleepDivider); 00264 /* The watchdog will be used to wake up, thus enable it (note that is does not seem 00265 * necessary to set the WDCE bit for changing the prescaler bits, contrary to what the manual 00266 * on page 45 states: "Bit 4 – WDCE: Watchdog Change Enable: .... This bit must also be set 00267 * when changing the prescaler bits") It seems this only is required at disabling the watchdog, 00268 * but i never got this to work as described. */ 00269 /* Enable configuration change. */ 00270 devWDTCR = preBitSet2(0x00,devWDCE,devWDE); 00271 devWDTCR = preBitSet4(0x00,devWDIF,devWDIE,devWDP2,devWDP1) | preBitClr4(0x00,devWDP3,devWDCE,devWDE,devWDP0); 00272 /* At this moment we have a fixed mode for sleeping which is the powerdown. Change it to your 00273 * liking if needed. So, set the sleep mode to power down. This is needed, since normally 00274 * the sleep mode is different during the idle task. Make sure we leave the other bits alone. */ 00275 devSMCR = preBitClr2(preBitSet2(devSMCR,devSE,devSM1),devSM0,devSM2); 00276 /* We use a counter to keep track of how many blocks we have slept, this in order to 00277 * correct the clock later on. */ 00278 Tuint08 uiBlocksSleepCount = 0; 00279 /* Start sleeping. Because there are two sources of wakeup, i.e. the watchdog and some other 00280 * interrupt we must distinguish between the two. We do so by setting a sleepbit (we can use 00281 * the devAuxSysRegBit bit since within the sleep - wake loop no other use is made of it.) in 00282 * the devAuxSysReg. This bit is cleared by the watchdog and cleared just before sleep. If at 00283 * wakeup the bit is not set we know an other interrupt source was present and we must 00284 * break of sleeping at once. Otherwise we may continue if we are not done yet. So first 00285 * set the sleepBit to pass the first while loop.*/ 00286 #if (cfgSysSqueezeState == cfgFalse) 00287 devAuxSysReg = preBitSet1(devAuxSysReg,devAuxSysRegBit); 00288 /* You may continue the loop when not all blocks are slept yet and we came from a watchdog interrupt */ 00289 while ( ((uiBlocksSleepCount != uiBlocksToSleep)) && preBitIsSet(devAuxSysReg,devAuxSysRegBit) ) 00290 { /* Clear the sleepbit, so it can be set by the watchdog interrupt */ 00291 devAuxSysReg = preBitClr1(devAuxSysReg,devAuxSysRegBit); 00292 asm volatile ( 00293 "sei \n\t" /* We reactivate the global interrupt just before we go to sleep so we know for */ 00294 "sleep \n\t" /* sure the sleep will indeed start off. In that way we do not miss an interrupt. */ 00295 "cli \n\t" /* And then we immideately disable them again so there cannot be an extra interrupt */ 00296 ::); /* just after the first interrupt. */ 00297 /* If we may sleep for ever we simple forbid sleepcounting so the loop will never reach its limit. Still other 00298 * interrupts may break the loop.*/ 00299 if ((uiTickBlockMinDelay != defSleepInfinite)) uiBlocksSleepCount++; 00300 /* If a sleephook was installed it is called at every wakeup. */ 00301 #if (callAppTickSleep == cfgTrue) 00302 appTickSleep(); 00303 #endif 00304 /* Done, if we leave the loop here, the device has woken up */ } 00305 #else 00306 /* The code below does exactly the same as above, but now for the T bit instead of the AuxRegBit. Since 00307 * i don't know an efficient way to use the T bit in C code directly, i coded it in assembly. Save's two bytes 00308 * compared to what the gcc compiler makes of the code above, and six push/pop instructions, due to efficient 00309 * register use. 00310 * Note that you onw interrupt should not set the T bit on entry. Usually that does not happen since interrupts 00311 * should preserve the status register. And this has T bit cleared. */ 00312 asm volatile ( 00313 "set \n\t" /* The T bit functions as flag to detect if we come from an watchdog interrupt */ 00314 "76: \n\t" /* or from an other interrupt. When it is set, we assume coming from a watchdog */ 00315 "cp %[_Count_],%[_Sleep_] \n\t" /* So start with the flag set. Now we compare the sleep counter with the number */ 00316 "breq 78f \n\t" /* of blocks we must sleep. When equal, we are done, jump to the end. */ 00317 "brtc 78f \n\t" /* Subsequently we check if the T bit is still set. If not, we had an non watch */ 00318 "clt \n\t" /* dog interrupt and we may stop. Then, we start sleeping with T bit reset. */ 00319 "sei \n\t" /* We reactivate the global interrupt just before we go to sleep so we know for */ 00320 "sleep \n\t" /* sure the sleep will indeed start off. In that way we do not miss an interrupt. */ 00321 "cli \n\t" /* And then we immediately disable them again so there cannot be an extra inter- */ 00322 "cpi %[_Delay_],%[_Inf_] \n\t" /* rupt after the first interrupt. Then increase the sleep counter by one if */ 00323 "breq 77f \n\t" /* sleep time is limited and otherwise keeping the counter constant will induce */ 00324 "subi %[_Count_],0xFF \n\t" /* Infinite looping. */ 00325 "77: \n\t" /* */ 00326 #if (callAppTickSleep == cfgTrue) /* If we defined a hook on the heartbeat of sleeping call it. */ 00327 defCALL " appTickSleep \n\t" /* Make use if defCall because we do not know how far we must go */ 00328 #endif /* */ 00329 "rjmp 76b \n\t" /* Go for an other round of sleep. */ 00330 "78: \n\t" 00331 : 00332 [_Count_] "=r" (uiBlocksSleepCount): 00333 "[_Count_]" (uiBlocksSleepCount), 00334 [_Sleep_] "r" (uiBlocksToSleep), 00335 [_Delay_] "r" (uiTickBlockMinDelay), 00336 [_Inf_] "i" (defSleepInfinite)); 00337 00338 #endif 00339 /* To prevent accidentally falling asleep, clear the sleep enable bit. */ 00340 devSMCR = preBitClr1(devSMCR,devSE); 00341 /* The watchdog must be disabled, since it may interrupt the OS with disastrous consequences. 00342 * Enable a configuration change. (There is no need now to clear the WDRF, we do not come here 00343 * after a reset. If we did not set the watchdog, there is nothing to clear here. */ 00344 devWDTCR = preBitSet2(0x00,devWDCE,devWDE); 00345 /* Disable Watchdog, do NOT write any other bits! Setting the prescaler seems not to be allowed 00346 * on this moment. Why, is unclear to me, but we do not need it here. */ 00347 devWDTCR = preBitClr1(0x00,devWDCE); 00348 /* If we want to correct the tick counter ... */ 00349 #if (includeGenAddtoTickCount == cfgTrue) 00350 /* Calculate the time slept by approximation. It does not make sense to adjust the clock 00351 * when we where allowed to sleep indefinitely. */ 00352 if (uiTickBlockMinDelay != defSleepInfinite) 00353 { /* The number of sleepblocks slept have been counted, so it is relatively easy to calculate 00354 * the number of extra ticks. First each sleepblock accounts for defSleepDivider tickblocks, 00355 * and each tickblock is 256 ticks. */ 00356 Tuint16 uiTicksSlept = (uiBlocksSleepCount * defSleepDivider << 8); 00357 /* And pass it on to the Femto OS */ 00358 genAddtoTickCount(uiTicksSlept); } 00359 #endif 00360 /* We are done sleeping and may return to the OS 00361 * Note we must return with interrupts still switched off! */ } 00362 00363 #endif 00364 00365 00366 #if (includeGenReboot == cfgTrue) 00367 00368 void portReboot(void) 00369 { /* These are the device specific instructions to reboot. Unfortunately there is no 00370 * way to reset the device directly. Depending on your situation you could jump to 00371 * __init, but that does not reset the registers, and your __init may even rely upon 00372 * the inital values of them, so the interrupts may not be cleared at the start!! 00373 * Hmm, it seems we must misuse the 00374 * watchdog for this. Make sure you disable the watchdog upon re-entry, set it to 00375 * the reset mode, and the shortest interval. (16ms) 00376 * TODO: test if the reset time is really 16ms, even if we have slept some other time 00377 * before. The point is, we do not set WDCE, although is should have been done 00378 * account. Page 45 of the manual, Bit 4 -WDCE: Watchdog Change Enable. 00379 * Found: for the attiny861 this is works also without, but for other devices it does not. */ 00380 devWDTCR = preBitSet2(0x00,devWDCE,devWDE); 00381 devWDTCR = preBitSet1(0x00,devWDE) | preBitClr5(0x00,devWDIE,devWDP3,devWDP2,devWDP1,devWDP0); 00382 /* Now, wait on the reset, for we may not return. */ 00383 while (true); } 00384 00385 #endif 00386 00387 00388 void portIdle(void) 00389 { /* If we arrive here it is because there are no tasks that can be run. This is the idle 00390 * task. We must stay here until the next timer interrupt. */ 00391 #if cfgSysDebug == cfgTrue 00392 /* If you use the simulator of Atmel, it cannot simulate the timer at this point 00393 * so we manually simulate it. */ 00394 devSigTimerCompare(); 00395 #else 00396 /* It is the responsibility of the portIdle implementation to activate the timer 00397 * interrupts, since there are no references with regard to timer activation/ 00398 * deactivation inside the femto OS core. You may assume you arrive here with 00399 * timer interrupts switched off. */ 00400 devTIMSK = preBitSet1(devTIMSK,devOCIE); 00401 /* the avr tiny has and idle state that we will utilize, this is the sleep idle mode, 00402 * enable it */ 00403 devSMCR = preBitClr3(preBitSet1(devSMCR,devSE),devSM2,devSM1,devSM0); 00404 /* and start sleeping. An (timer) interrupt will wake the device */ 00405 asm volatile ("sleep" :: ); 00406 /* If we are woken by some other interrupt we return here after the interrupt is 00407 * done. However, we may not return, therefore we will just wait for the next 00408 * timer interrupt. */ 00409 while (true); 00410 #endif 00411 } 00412 00413 #ifndef portInitContext 00414 /* Only include the code below if portInitContext is not defined, i.e. if we do not reroute a call to 00415 * portInitContext to privInitContext. The implementation below is identical to the one in the core.c */ 00416 Taddress portInitContext(Taddress pTaskStart, Taddress pStackTop, Tuint08 uiRegisterCount, Tuint08 uiInterruptStart) 00417 { /* In case you cannot make use of the privInitContext defined inside the Femto OS you can define your 00418 * own. This method does not contain any assembler, but the order in which status register and regular registers 00419 * are save is tightly coupled to the organization of portSaveContext and portRestorContext. 00420 * Note that the code below is identical to the code inside Femto OS and is not used in reality. See 00421 * port.h how to activate this (or your substitute) code. */ 00422 Tuint16 uiStartAddress = (Tuint16) pTaskStart; 00423 /* At the bottom of the artificial stack the start address of each task is defined. This is a pointer 00424 * to your Loop code. */ 00425 *(pStackTop--) = (Tuint08) uiStartAddress; 00426 *(pStackTop--) = (Tuint08) (uiStartAddress >> 8); 00427 /* If we have a program counter of more than 16 bit, call instructions push three bytes 00428 * into the stack. We must take that into account. Unfortunately, i believe, gcc is not 00429 * able to handle pointer larger as 64K words in the current setting. Thus this will 00430 * effectively be zero, thus although you need something like 00431 * *(pStackTop--) = (Tuint08) (pTaskStart >> 16); 00432 * we will use: */ 00433 #if (defThreeByteAddress == cfgTrue) 00434 *(pStackTop--) = 0; 00435 #endif 00436 /* DISCUSSION 00437 * You can if you wish fill the register space with other bytes. This is the place to do so, 00438 * otherwise these have the value of the defStackInitByte, which is usually set to zero. */ 00439 #if (0) 00440 /* Clean the stack actively in case that we are reinitializing, or want to fill it with something else as 0. 00441 * Note that this also fill r1 since the option cfgSysClearUnusedR1 operates before the registers are restored. */ 00442 while (uiRegisterCount--) { *(pStackTop--) = defYourRegisterInitByte; } 00443 #else 00444 /* We rely upon register cleaning done by the Femto OS, or the precleaning done by privTaskInit(). */ 00445 pStackTop -= uiRegisterCount; 00446 #endif 00447 /* The way the status register is setup depends on the state of the interrupts. These can be specified per task 00448 * or (easier, and shorter) in general. Note that the optimization is not needed in a strict sense, the variable 00449 * uiInterruptStart constrains the interrupt start information, even if this is constant for all tasks. But, of course, 00450 * the lower part generates shorter code. */ 00451 #if (defInterruptStartConstant == cfgFalse) 00452 /* If we specify the CPU status register per task, we need to prepare them per task. uiInterruptStart contains 00453 * the information about which interrupts must be activated. The other bits of the status register cannot be 00454 * set individually (there is no need in general) */ 00455 Tuint08 uiInitCPUStatusRegister = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc); 00456 /* Test if global interrupts must be activated at the start, if so set the specified bit. */ 00457 #if (cfgIntGlobalOnly == cfgTrue) 00458 /* In case we have a mapping from tick interrupts on global interrupts, we require both interrupts 00459 * to be activated before activating the global interrupt. */ 00460 if ((uiInterruptStart & ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) == ((cfgGlobSet | cfgTickSet) & defInitialInterruptGetMask)) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); } 00461 #else 00462 /* Otherwise we just test the global interrupt setting. */ 00463 if ((uiInterruptStart & (cfgGlobSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitGlobalInterruptLoc); } 00464 #endif 00465 /* Test if tick interrupts must be activated at the start, if so set the specified bit. */ 00466 #if (cfgIntTickTrack == cfgTrue) 00467 if ((uiInterruptStart & (cfgTickSet & defInitialInterruptGetMask)) != defInitialInterruptAbsent) { uiInitCPUStatusRegister |= (1 << portInitTickInterruptLoc); } 00468 #endif 00469 /* The status register is put at the end of the stack. (See portSaveContext for the reason why) */ 00470 *(pStackTop--) = uiInitCPUStatusRegister; 00471 #else 00472 /* Set up the status register with the initial interrupt states: global set and tick set */ 00473 #if (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet) 00474 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc); 00475 /* Set up the status register with the initial interrupt states: global set and tick clear */ 00476 #elif (((defInterruptStartFixed) & cfgGlobSet) == cfgGlobSet) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear) 00477 #if (cfgIntGlobalOnly == cfgTrue) 00478 /* In case we have a mapping from tick interrupts on global interrupts, global interrupts cannot be activated if tick interrupts are not activated */ 00479 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00480 #else 00481 /* Default situation. */ 00482 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (1 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00483 #endif 00484 /* Set up the status register with the initial interrupt states: global clear and tick set */ 00485 #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickSet) == cfgTickSet) 00486 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (1 << portInitTickInterruptLoc); 00487 /* Set up the status register with the initial interrupt states: global clear and tick clear */ 00488 #elif (((defInterruptStartFixed) & cfgGlobClear) == cfgGlobClear) && (((defInterruptStartFixed) & cfgTickClear) == cfgTickClear) 00489 *(pStackTop--) = defInitCPUStatusRegister | (0 << portInitModeInterruptLoc) | (0 << portInitGlobalInterruptLoc) | (0 << portInitTickInterruptLoc); 00490 #elif (defNumberOfTasks == 0) 00491 /* without tasks there is nothing to set. */ 00492 #else 00493 /* well, we never come here. */ 00494 #error "Parameter 'defInterruptStartFixed' is misspeld or incorrect (You should not arrive here)." 00495 #endif 00496 #endif 00497 /* Done, let the caller know where the stack ended. */ 00498 return pStackTop; } 00499 #endif 00500 00501 00502 void portSetupTimerInterrupt(void) 00503 { /* We use a 8 bit timer to generate the tick. Note that it is not really possible to 'calculate' the 00504 * maximum value of the timer for general values of the tick frequency, this depends heavily on the 00505 * possibilities of the hardware. Therefore we work with a limited set of allowed values of the 00506 * prescaler, present in most devices. 00507 * Reset the prescaler, Set the largest divider */ 00508 /* In case of equidistant ticks we make use of the automatic cleaning of the subtick counter 00509 * If your hardware does not have such a mode, it must be coded by hand in the tick interrupt. */ 00510 #if (cfgUseEquidistantTicks == cfgTrue) 00511 /* Use the 8 bit CTC mode */ 00512 if (preEqualMacros(devTCCRB,devTCCRA)) 00513 { devTCCRA = preBitSet1(0x00,devWGM) | defTimerPrescaleBits; } 00514 else 00515 { devTCCRA = preBitSet1(0x00,devWGM); 00516 devTCCRB = defTimerPrescaleBits; } 00517 /* Set the time at which the tick interrupt should take place. */ 00518 devOCR = cfgSysSubTicksPerFullTick; 00519 #else 00520 /* Use a normal counting mode in case of variable time slices, clear is done through the call 00521 * portReadAndResetTimer */ 00522 if (preEqualMacros(devTCCRB,devTCCRA)) 00523 { devTCCRA = preBitClr1(0x00,devWGM) | defTimerPrescaleBits; } 00524 else 00525 { devTCCRA = preBitClr1(0x00,devWGM); 00526 devTCCRB = defTimerPrescaleBits; } 00527 #endif 00528 /* To be able to check for overflowing timers we must enable the overflow interrupt. */ 00529 #if ((cfgUseEquidistantTicks == cfgFalse) && (cfgCheckTiming == cfgTrue)) 00530 /* We must make sure a overflow interrupt is not immediately generated, so reset the timer. */ 00531 devTCNT = 0x00; 00532 /* Set the TOIE0 bit (enables the overflow interrupt). */ 00533 devTIMSK = preBitSet1(devTIMSK,devTOIE); 00534 #endif 00535 /* Do not enable the tick interrupt itself. The OS is started with tick interrupts disabled. */ } 00536 00537 00538 #if ((cfgUseEquidistantTicks == cfgTrue) && ((cfgCheckTiming == cfgTrue) || (cfgUseLoadMonitor == cfgTrue))) || ((cfgUseLoadMonitor == cfgTrue) && (cfgIntUserDefined == cfgTrue)) 00539 00540 Tuint08 portReadTimer(void) 00541 { /* The intent of this method is to read the current value of the subtick timer. */ 00542 #if cfgSysDebug == cfgTrue 00543 /* If you use the simulator of Atmel, it cannot simulate the timer at this point 00544 * so we manually simulate it, let's just return some fictitious value. */ 00545 Tuint08 uiResult = cfgSysSubTicksPerFullTick / 8; 00546 #else 00547 /* We must have deactivated timer interrupts to get an reliable measurement. However, we do not 00548 * need to do that here, since the portReadTimer is only called from the OS (timer interrupts 00549 * are switched off, or from an isr (global interrupts switched off). */ 00550 Tuint08 uiResult = devTCNT; 00551 /* if there is an unhandled interrupt, the read value must be increased with cfgSysSubTicksPerFullTick 00552 * since there is no way we could otherwise obtain this information. The only problem is that the 00553 * interrupt flag could become set in between the instruction above and the one below. That would lead to 00554 * a way to large value of the result. Now, since there is no atomic way to gain the information about the 00555 * subtick timer and the interrupt flag, we must check if adding cfgSysSubTicksPerFullTick makes sense. */ 00556 if ( preBitIsSet(devTIFR,devOCF) && (uiResult < cfgSysSubTicksPerFullTick/2) ) uiResult+=cfgSysSubTicksPerFullTick; 00557 #endif 00558 return uiResult; } 00559 #endif 00560 00561 00562 #if (cfgUseEquidistantTicks == cfgTrue) && (cfgIntManualTicks == cfgTrue) 00563 00564 Tbool portCheckTimer(void) 00565 { /* Check if the timer interrupt is due to fire. If so return true and release the trigger, otherwise 00566 * return false. This is used in situations where all tasks are cooperative and the timer never gets 00567 * a chance to fire. */ 00568 Tbool bResult = false; 00569 /* Test the value of the timer interrupt flag. If set, reset it by writing a one to its position 00570 * and utilize the none zero result as true value. Note that we do NOT change the value of the timer, 00571 * we merely reset the timer flag, which is replaced by a manual tick inside the core code. */ 00572 if (preBitIsSet(devTIFR,devOCF)) { devTIFR = bResult = preBitSet1(0x00,devOCF); } 00573 /* Done, return the result. */ 00574 return bResult; } 00575 00576 #endif 00577 00578 00579 #if (cfgUseEquidistantTicks == cfgFalse) 00580 00581 Tuint08 portReadAndResetTimer(Tuint08 uiSubTickInterrupt) 00582 { /* The intent of this method is to read the current value of the subtick timer and 00583 * reset it at the same time. Unfortunately this cannot be done in one instruction, 00584 * thus we make a systematic timing error here. If needed this timer can be implemented 00585 * as a 16 bit timer which would enhance the accuracy of the timer. In practice we never 00586 * measure real time through this mechanism so approximate values are sufficient here. 00587 * This method is called with tick interrupts disabled. */ 00588 #if cfgSysDebug == cfgTrue 00589 /* If you use the simulator of Atmel, it cannot simulate the timer at this point 00590 * so we manually simulate it, let's just return some fictitious value. */ 00591 Tuint08 uiResult = cfgSysSubTicksPerFullTick / 2; 00592 #else 00593 /* If we have OS protection, global interrupts are disabled, but otherwise ... */ 00594 #if (cfgIntOsProtected == cfgFalse) 00595 /* ... if an other interrupt comes in between these two instructions, that will disturb the 00596 * timing even more. This can be prevented by conditionally disabling the interrupts 00597 * around these instructions. */ 00598 privDisableGlobalInterrupts(); 00599 #endif 00600 /* Read the value of the current sub tick timer (timer 0 on this device) */ 00601 Tuint08 uiResult = devTCNT; 00602 /* reset the timer. This timer could be increased during the time needed to execute 00603 * the the former instruction. This results in a systematic loss of accuracy, for 00604 * which there is no real (short) cure. It is absolutely essential that no interrupts occur 00605 * between there two instructions.*/ 00606 devTCNT = 0x00; 00607 /* Set the new value of the alarm, this determines the time the next task has. */ 00608 devOCR = uiSubTickInterrupt; 00609 /* We clear the interrupt compare flag, to make sure the new task has the full 00610 * slice, if we would skip this, there could be the situation that that a previous 00611 * unhandled tick cases an unintended interrupt. */ 00612 devTIFR = preBitSet1(0x00,devOCF); 00613 #if (cfgIntOsProtected == cfgFalse) 00614 /* From this point on interrupts are allowed again. */ 00615 privEnableGlobalInterrupts(); 00616 #endif 00617 #endif 00618 /* Done, let he caller know the previous time spend. */ 00619 return uiResult; } 00620 00621 #endif 00622 00623 00624 #if defined(devSigWatchdogTimeout) && (defSysGCCstartup != cfgReplace) && (cfgUseLowPowerSleep == cfgTrue) && (cfgUseLowPowerOnDelay == cfgTrue) 00625 00626 void devSigWatchdogTimeout(void) 00627 { /* We must signal the receiver we come from the watchdog interrupt. Unfortunately the device flag 00628 * is cleared because of this handeling. We have to implement some handling however, otherwise 00629 * the interrupt cannot wake up the device. */ 00630 #if (cfgSysSqueezeState == cfgFalse) 00631 00632 asm volatile ( 00633 "sbi %[_ASR_],%[_ARB_] \n\t" /* Set the WD bit to indicate a watchdog timeout occurred. */ 00634 "ret \n\t" /* Get back to the sleepmethod, there is no need to set the interrupts. */ 00635 "" :: 00636 [_ARB_] "i" (devAuxSysRegBit), 00637 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg)) 00638 ); 00639 00640 #else 00641 /* Here we use the T bit to transmit we hit the watchdog. The H bit is less suitable since its state 00642 * may be destroyed (set) in the handling of alternative interrupts. It is easier to restrict the 00643 * use of the T-bit. */ 00644 asm volatile ( 00645 "set \n\t" /* Set the T bit to indicate a watchdog timeout occurred. */ 00646 "ret \n\t" /* Get back to the sleep method, there is no need to set the interrupts. */ 00647 "" :: ); 00648 #endif 00649 } 00650 00651 #endif 00652 00653 00654 #if (defSysGCCstartup != cfgReplace) && (cfgUseEquidistantTicks == cfgFalse) && (cfgCheckTiming == cfgTrue) 00655 00656 void devSigTimerOverflow(void) 00657 { /* Let the OS know the subtick timer overflowed. This only makes sense when cfgUseEquidistantTicks == cfgFalse 00658 * since in the other case the timer cannot overflow. */ 00659 portJump(privSubtickOverflow); } 00660 00661 #endif 00662 00663 00664 #if (defSysGCCstartup != cfgReplace) 00665 00666 void devSigTimerCompare(void) 00667 { /* If your hardware does not support automatic reset of the sub tick counter, you must do so here, 00668 * but only if (cfgUseEquidistantTicks == cfgTrue). */ 00669 /* Let the OS know the sub tick timer reached its interrupt level and it is 00670 * time to switch the task. */ 00671 portJump(privTickYield); } 00672 00673 #endif 00674 00675 /* Define the most optimal handling for the TIMSK register, depending on its location. */ 00676 #if (defTIMSKinIO == cfgTrue) 00677 #define LoadTIMSK(rx) "in " #rx ", %[_TIMSKio_] \n\t" 00678 #define StoreTIMSK(rx) "out %[_TIMSKio_], " #rx " \n\t" 00679 #else 00680 #define LoadTIMSK(rx) "lds " #rx ", %[_TIMSKmem_] \n\t" 00681 #define StoreTIMSK(rx) "sts %[_TIMSKmem_], " #rx " \n\t" 00682 #endif 00683 00684 00685 #if (cfgUseFileSystem == cfgTrue) && (cfgUseFileSystemEconomyMode == cfgFalse) 00686 /* If you arrive here the OS did already check writing is allowed via portFSWriteBusy, there 00687 * is not need to check it again. Make sure you erase the bytes if needed Global interrupts may 00688 * be activated, so save the state.*/ 00689 void portFSWriteByte(Taddress pAddress, Tbyte bValue) 00690 { /* We perfrom the actual writing in assembly code for optimization reasons. */ 00691 asm volatile ( 00692 "com %[_VAL_] \n\t" /* The byte that we write is actually the inverse, */ 00693 #if (devEEPROMsize > 0x0100U) 00694 "out %[_EEARH_],%B[_ADR_] \n\t" /* Fill the registers EEARH and EEARL with the location of the byte to be written. Since we are */ 00695 #endif 00696 "out %[_EEARL_],%A[_ADR_] \n\t" /* the only task that may write (and thus use these registers), we don't need to disable irq's yet. */ 00697 "clr r26 \n\t" /* Make one register 0 (we could have used r1, but i am not absolutely certain it is zero always. */ 00698 "out %[_EECR_], r26 \n\t" /* Set the erase/Write mode (0<<EEPM1)|(0<<EEPM0), clear interrupt mode, */ 00699 "out %[_EEDR_], %[_VAL_] \n\t" /* Put the data at the location for writing. */ 00700 "in r26, __SREG__ \n\t" /* At this point we must disable interrupts, since the two instructions that follow, must be */ 00701 "cli \n\t" /* executed within 4 clockcyles of each other. */ 00702 "sbi %[_EECR_],%[_EEMPE_] \n\t" /* Enable writing, and (in next instruction ) commence writing. Note the writing is still busy when */ 00703 "sbi %[_EECR_],%[_EEPE_] \n\t" /* this function completes. That is OK, we call portFSWriteReady() before next writing. */ 00704 "out __SREG__, r26 \n\t" /* Restore the interrupt state and we are done. */ 00705 "" :: 00706 [_ADR_] "r" (pAddress), 00707 [_VAL_] "r" (bValue), 00708 [_EECR_] "i" (_SFR_IO_ADDR(devEECR)), 00709 [_EEDR_] "i" (_SFR_IO_ADDR(devEEDR)), 00710 [_EEARL_] "i" (_SFR_IO_ADDR(devEEARL)), 00711 #if (devEEPROMsize > 0x0100U) 00712 [_EEARH_] "i" (_SFR_IO_ADDR(devEEARH)), 00713 #endif 00714 [_EERE_] "i" (devEERE), 00715 [_EEPE_] "i" (devEEPE), 00716 [_EEMPE_] "i" (devEEMPE): 00717 "r26" ); } 00718 00719 #endif 00720 00721 /* DISCUSSION 00722 * 00723 * Because we make use of -mint8 we cannot make use of the instruction cbr Rd,K. This 00724 * is because gcc emits signed integers as operands and thus something like 00725 * cbr r24, 0xF0 00726 * generates 00727 * cbr r24, -128 00728 * this can then not be translated into opcodes, for internally the operation 00729 * (0xFF - -128) cannot be performed. Imho this is a bug in the compiler. 00730 * The workaround is: 00731 * andi r24, ~0xFO 00732 */ 00733 00734 #if (cfgUseFileSystem == cfgTrue) && (cfgUseFileSystemEconomyMode == cfgTrue) 00735 /* First make sure the device has features for split-byte programming, if not, 00736 * you should not call this (test is done in check.h) 00737 * If you arrive here the OS did already check writing is allowed via portFSWriteBusy, there 00738 * is not need to check it again. Make sure you erase the bytes if needed Global interrupts may 00739 * be activated, so save the state. This method implements a more careful way of writing bytes, 00740 * making use of the split erase / write mode of the device. In fact, we only need to erase if 00741 * there are some bits to be set and only need to write if there are some bits to be cleared 00742 * in the byte to be written. We read first to inspect the situation. Off course this introduces 00743 * overhead, but extends the lifetime of the eeprom. Also, on average, the call completes more 00744 * quickly compared to the standard implementation. */ 00745 void portFSWriteByte(Taddress pAddress, Tbyte bValue) 00746 { asm volatile ( 00747 #if (devEEPROMsize > 0x0100U) 00748 "out %[_EEARH_],%B[_ADR_] \n\t" /* Fill the registers EEARH and EEARL with the location of the byte to be written. Since we are */ 00749 #endif 00750 "out %[_EEARL_],%A[_ADR_] \n\t" /* the only task that may write (and thus use these registers), we don't need to disable irqs yet. */ 00751 "sbi %[_EECR_],%[_EERE_] \n\t" /* Start reading, we need the information of the byte present to sort out what to do. */ 00752 "in r24, %[_EEDR_] \n\t" /* Fetch the data. */ 00753 "ldi r23, %[_EECOMb_] \n\t" /* EEPM1,EEPM0, EEMPE set 0x34 */ 00754 "mov r25,24 \n\t" /* Copy the data (data is retained for later use) */ 00755 "or r25,%[_VAL_] \n\t" /* r25=d|m (determines which positions must be set */ 00756 "cpi r25,0xFF \n\t" /* d|m ?= 0xFF If that results equals 'all set' we don't need to erase */ 00757 "breq 2f \n\t" /* Skip to the next test */ 00758 "andi r23, %[_nEEPM1b_] \n\t" /* Activate the erase bit in the action register r23 (bit _EEPM1_) */ 00759 "ldi r24,0xFF \n\t" /* d=0xFF replace the data byte by 0xFF */ 00760 "2: \n\t" /* Continue operations here is there is nothing to erase. */ 00761 "and r24,%[_VAL_] \n\t" /* r24=d&m (determines which positions remain cleared. */ 00762 "cpi r24,0x00 \n\t" /* d&m ?= 0 If all positions remain cleared, there is noting to write */ 00763 "breq 3f \n\t" /* Skip to the next action */ 00764 "andi r23, %[_nEEPM0b_] \n\t" /* Activate the write bit in the action register r23 (bit _EEPM0_) */ 00765 "3: \n\t" /* Continue operations here is there is nothing to write. */ 00766 "cpi r23, %[_EECOMb_] \n\t" /* Now, check if there are any actions to take. */ 00767 "breq 4f \n\t" /* If not, there is nothing to do and we are done. */ 00768 "com %[_VAL_] \n\t" /* The byte that we write is actually the inverse, this is to save write operations. */ 00769 "out %[_EEDR_],%[_VAL_] \n\t" /* Put the byte in the correct location for writing. */ 00770 "in r24, __SREG__ \n\t" /* Writing (and/or erasing) must be prepared whitout the risk of an interrupt, so copy the status */ 00771 "cli \n\t" /* register, and clear the interrupts. */ 00772 "out %[_EECR_],r23 \n\t" /* Enable writing (and set write/erase mode according to action register at the same time!), and */ 00773 "sbi %[_EECR_],%[_EEPE_] \n\t" /* commence writing. Note the writing is still busy when this function completes. That is ok, we */ 00774 "out __SREG__, r24 \n\t" /* call portFSWriteReady() before next writing. Restore the interrupt state and we are done. */ 00775 "4: \n\t" 00776 "" :: 00777 [_ADR_] "r" (pAddress), 00778 [_VAL_] "r" (bValue), 00779 [_EECR_] "i" (_SFR_IO_ADDR(devEECR)), 00780 [_EEDR_] "i" (_SFR_IO_ADDR(devEEDR)), 00781 [_EEARL_] "i" (_SFR_IO_ADDR(devEEARL)), 00782 #if (devEEPROMsize > 0x0100U) 00783 [_EEARH_] "i" (_SFR_IO_ADDR(devEEARH)), 00784 #endif 00785 [_EERE_] "i" (devEERE), 00786 [_EEPE_] "i" (devEEPE), 00787 [_EEMPE_] "i" (devEEMPE), 00788 [_EEPM0_] "i" (devEEPM0), 00789 [_EEPM1_] "i" (devEEPM1), 00790 [_nEEPM0b_] "i" (preBitClr1(0xFF,devEEPM0)), 00791 [_nEEPM1b_] "i" (preBitClr1(0xFF,devEEPM1)), 00792 [_EECOMb_] "i" (preBitSet3(0x00,devEEPM1,devEEPM0,devEEMPE)): 00793 "r23"); 00794 } 00795 00796 #endif 00797 00798 #if (cfgUseFileSystem == cfgTrue) 00799 /* Reading is allowed with interrupts on. */ 00800 Tbyte portFSReadByte(Taddress pAddress) 00801 { Tuint08 result; 00802 /* We perform the actual reading in assembly code for optimization reasons. */ 00803 asm volatile ( 00804 "in r26, __SREG__ \n\t" /* Copy the status register as quickly as possible and disable interrupts, even if it was cleared */ 00805 "cli \n\t" /* already. Although the read operation in itself is allowed to be interrupted, we cannot allow */ 00806 #if (devEEPROMsize > 0x0100U) 00807 "out %[_EEARH_],%B[_ADR_] \n\t" /* the registers EEARH and EEARL to be destroyed by an other task. Load the EEARH and EEARL */ 00808 #endif 00809 "out %[_EEARL_],%A[_ADR_] \n\t" /* registers. */ 00810 "sbi %[_EECR_],%[_EERE_] \n\t" /* Start reading */ 00811 "in %A[_RES_],%[_EEDR_] \n\t" /* Fetch the data read. */ 00812 "out __SREG__, r26 \n\t" /* Done. */ 00813 "" : 00814 [_RES_] "=r" (result): 00815 [_ADR_] "0" (pAddress), 00816 [_EECR_] "i" (_SFR_IO_ADDR(devEECR)), 00817 [_EEDR_] "i" (_SFR_IO_ADDR(devEEDR)), 00818 [_EEARL_] "i" (_SFR_IO_ADDR(devEEARL)), 00819 #if (devEEPROMsize > 0x0100U) 00820 [_EEARH_] "i" (_SFR_IO_ADDR(devEEARH)), 00821 #endif 00822 [_EERE_] "i" (devEERE), 00823 [_EEPE_] "i" (devEEPE), 00824 [_EEMPE_] "i" (devEEMPE), 00825 [_EEPM0_] "i" (devEEPM0), 00826 [_EEPM1_] "i" (devEEPM1): 00827 "r26" ); 00828 /* Note we invert the result. All bytes are stored inverted, so that a completely erased eerprom 00829 * appears to be filled with zero's */ 00830 return ~result; } 00831 00832 #endif 00833 00834 00835 /* DISCUSSION 00836 * The port(Enter/Exit)(Global/Tick)Interrupts methods must temporarily store 00837 * the value of the interrupt state and restore it when needed. These methods 00838 * are only called from within the Femto OS and are never nested, and you should not 00839 * call them yourself. (other functions are available). Since we did not want to make 00840 * inserts (although you can still do so, by turning them into defines) 00841 * because that takes a lot of extra bytes (a call is shorter) we cannot store 00842 * the status on the stack. Thus we decided to use, for example a general purpose 00843 * I/O register (devAuxSysReg) of the AVR tiny as temporary variable. This is used in 00844 * portSave- and portRestore Context also, but since we know we will never call a context 00845 * switch from within one of these Enter/Exit constructions, there is no risk 00846 * of double use. (Remember, you should not use these functions, the results 00847 * can be bizarre!) 00848 * Furthermore we make use of one bit of devAuxSysReg to catch the state of the current 00849 * global interrupt when a context switch occurs. Therefore, when running a task that 00850 * bit must always be zero; it is set if global interrupts are set on context switch. 00851 * Note that the functions portEnterGlobalInterrupts/portExitGlobalInterrupts 00852 * cannot rely on bit 7 being zero since these functions are used in isr's also. 00853 * This has been corrected by clearing this bit in the PortSaveContext. 00854 * From this it follows that it is forbidden to activate global or tick interrupts 00855 * in their respective critical sections by hand. It is not a very logical thing 00856 * to do either. 00857 * An other point of attention is the interrupt nesting due to external interrupts. 00858 * We do not allow interrupt nesting in principle, so you may not activate interrupts 00859 * inside an isr. If we have not protected the OS from outside interrupts, you may 00860 * not call genXXX methods, and if we do EnterSysCritical translates to global 00861 * interrupt management. Thus we can use for both portEnterGlobalInterrupts/portExit- 00862 * GlobalInterrupts and portEnterTickInterrupts/portExitTickInterrupts the same 00863 * storage room for the interrupt state since they can never be nested. 00864 * Remember for user critical nesting there are other facilities. The former are 00865 * solely for the OS and are meant to be as brief as possible. 00866 */ 00867 00868 #if (defNonSwitchingCallPresent == cfgTrue) 00869 00870 #if (cfgSysSqueezeState == cfgFalse) 00871 00872 void portEnterGlobalInterrupts(void) 00873 { /* We make use of the fact that devAuxSysRegBit must equal zero upon entry. */ 00874 asm volatile ( 00875 "brid 41f \n\t" /* We rely upon devAuxSysRegBit being zero. Thus we test if global interrupts are activated. */ 00876 "cli \n\t" /* If not we skip, since there is nothing to do. If so, directly clear them, and set the bit */ 00877 "sbi %[_ASR_],%[_ARB_] \n\t" /* in devAuxSysRegBit to one, indicating the interrupts where activated. If we skip, the */ 00878 "41: \n\t" /* value in devAuxSysRegBit is already correct. */ 00879 "ret " :: /* Return to caller. */ 00880 [_ARB_] "i" (devAuxSysRegBit), 00881 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg))); } 00882 00883 #else 00884 00885 void portEnterGlobalInterrupts(void) 00886 { /* We keep the state of the interrupt in the Tbit. This is perfectly save since we know code in between 00887 * portEnterGlobalInterrupts and portExitGlobalInterrupts is Femto OS code only, which is verified to 00888 * use no T bit. */ 00889 asm volatile ( 00890 "clt \n\t" /* We cannot rely upon T bit being zero, so clean that first. We do not have to preserve T */ 00891 "brid 41f \n\t" /* since we came here manually. Now test if global interrupts are activated. */ 00892 "cli \n\t" /* If not we skip, since there is nothing to do. If so, directly clear them, and set the T bit */ 00893 "set \n\t" /* to one, indicating the interrupts where activated. If we skip, the T bit remains zero */ 00894 "41: \n\t" /* which is correct. */ 00895 "ret " :: 00896 [_Tb_] "i" (preBitSet1(0x00,SREG_T)) ); } 00897 00898 #endif 00899 00900 #endif 00901 00902 00903 #if (defNonSwitchingCallPresent == cfgTrue) 00904 00905 #if (cfgSysSqueezeState == cfgFalse) 00906 00907 void portExitGlobalInterrupts(void) 00908 { asm volatile ( 00909 "sbis %[_ASR_],%[_ARB_] \n\t" /* If devAuxSysRegBit is zero global interrupts where disabled on enter, nothing to do */ 00910 "ret \n\t" /* So we can just return */ 00911 "cbi %[_ASR_],%[_ARB_] \n\t" /* Otherwise, devAuxSysRegBit of must be zero before e.g. a portSaveContext occurs, so clear it */ 00912 "reti \n\t" /* And since it was set we must activate interrupts upon return */ 00913 "" :: 00914 [_ARB_] "i" (devAuxSysRegBit) , 00915 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg)) 00916 ); } 00917 00918 #else 00919 00920 void portExitGlobalInterrupts(void) 00921 { /* We keep the state of the interrupt in the Tbit. This is perfectly save since we know code in between 00922 * portEnterGlobalInterrupts and portExitGlobalInterrupts is Femto OS code only, which is verified to 00923 * use no T bit. */ 00924 asm volatile ( 00925 "brts 40f \n\t" /* The previous state of the interrupt was stored in the T bit, test if it was set, if so return */ 00926 "ret \n\t" /* with a reti, otherwise no interrupt was set and return with a ret. */ 00927 "40: \n\t" /* */ 00928 "reti \n\t" /* */ 00929 "" :: 00930 [_Tb_] "i" (preBitSet1(0x00,SREG_T)) ); } 00931 00932 #endif 00933 00934 #endif 00935 00936 00937 /* DISCUSSION 00938 * We don not need the portEnterTickInterrupts when we only allow for global interrupts. Switched on 00939 * tick interrupts are then replaced by global interrupts. If not these calls are used by Femto OS 00940 * to protect the internals of the API methods. They are only used when cfgIntOsProtected == cfgFalse. 00941 * In that case interrupts from outside may interrupt these methods. Note that, because of this, 00942 * it is never possible that the Enter/Exit Tick Interrupts get nested, for when they are allowed, 00943 * user defined interrupts may not yield, but must return to the point of interrupt. Therefore we 00944 * do not need to save the the state of tick interrupt itself, we can simply switch it off and on, 00945 * if there is no tick interrupt tracking. In the latter case we save the previous state of the 00946 * tick interrupt on some save place, to restore it later on. 00947 * Of course, we must keep the other bits in the TIMSK, so when changing the bit, global interrupts 00948 * must be switched off. 00949 */ 00950 00951 00952 #if (defNonSwitchingCallPresent == cfgTrue) && (cfgIntGlobalOnly == cfgFalse) && (cfgIntOsProtected == cfgFalse) 00953 00954 void portEnterTickInterrupts(void) 00955 { /* Since these calls are not nested, we can simply switch off / switch on the tick interrupts. Only if 00956 * we make use of tick tracking we must store the previous value and restore it upon return. */ 00957 asm volatile ( 00958 "in r25, __SREG__ \n\t" /* Copy the status register as quickly as possible and disable interrupts, even if it was cleared */ 00959 "cli \n\t" /* already. This is needed for a tick may intervene and alter other bits of the TIMSK register. */ 00960 LoadTIMSK(r24) /* Load the TIMSK register, holding the state of the tick interrupt on bit OCIE0A, */ 00961 #if (cfgIntTickTrack == cfgTrue) /* If we make use of tick tracking we must store the value of the tick interrupt somewhere */ 00962 #if (cfgSysSqueezeState == cfgTrue) /* At squeezing, we want spare our registers, so we utilize the T bit for storage, as with */ 00963 "bst r24, %[_OCIE0A_] \n\t" /* the global interrupts, the T bit is not used, neither is it combined with the global enter */ 00964 #else /* Thus, copy the value of the tick interrupt bit into the T bit. In case we do not want to use */ 00965 "sbrc r24, %[_OCIE0A_] \n\t" /* the status register at all, we use our devAuxSysRegBit to store the value. This is always */ 00966 "sbi %[_ASR_],%[_ARB_] \n\t" /* cleared upon entry, so we only need to set it when the tick interrupt is set. */ 00967 #endif 00968 #endif 00969 "andi r24, %[_nOCIE0Ab_] \n\t" /* Reset the timer0 interrupt enable bit */ 00970 StoreTIMSK(r24) /* Rewrite the TIMSK register, so interrupts are disabled after this instruction */ 00971 "out __SREG__, r25 \n\t" /* Restore the state of the global interrupts. */ 00972 "ret \n\t" /* Done. */ 00973 "" :: 00974 [_ARB_] "i" (devAuxSysRegBit), 00975 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg)), 00976 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 00977 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 00978 [_OCIE0A_] "i" (devOCIE), 00979 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)): 00980 "r24","r25" ); } 00981 00982 #endif 00983 00984 00985 #if (defNonSwitchingCallPresent == cfgTrue) && (cfgIntGlobalOnly == cfgFalse) && (cfgIntOsProtected == cfgFalse) 00986 00987 void portExitTickInterrupts(void) 00988 { /* Since these calls are not nested, we can simply switch off / switch on the tick interrupts. At entrance the tick interrupts 00989 * are switched of, so if we make use of tick tracking, and the restore state is switched off interrupts, there is 00990 * nothing to do */ 00991 asm volatile ( 00992 #if (cfgIntTickTrack == cfgTrue) /* If we make use of tick tracking we must restore the value of the tick interrupt */ 00993 #if (cfgSysSqueezeState == cfgTrue) /* At squeezing, it was stored in the T bit, so we ask if the T bit is set, if not we are */ 00994 "brtc 1f \n\t" /* done since the value of the tick interrupt bit is already zero which it must stay. */ 00995 #else /* Otherwise, the last value was stored in devAuxSysRegBit, and we must test if that bit was set */ 00996 "sbis %[_ASR_],%[_ARB_] \n\t" /* If not, we are done too, and we do not even need to clean the devAuxSysRegBit bit because it */ 00997 "rjmp 1f \n\t" /* is already zero. (Any interrupt that might become after this is not allowed to change that) */ 00998 "cbi %[_ASR_],%[_ARB_] \n\t" /* Since we know now that devAuxSysRegBit is set, we must clear it for use after this routine */ 00999 #endif /* And there are, at this point, not interrupts possible that may rely on the fact of */ 01000 #endif /* devAuxSysRegBit being zero [no context switch, no enter global, no switching isr etc] */ 01001 "in r25, __SREG__ \n\t" /* Copy the status register as quickly as possible and disable interrupts, even if it was cleared */ 01002 "cli \n\t" /* already. This is needed for an other interrupt may intervene and alter other bits of the TIMSK */ 01003 LoadTIMSK(r24) /* Load the TIMSK register, holding the state of the tick interrupt on bit OCIE0A, */ 01004 "sbr r24, %[_OCIE0Ab_] \n\t" /* Set the timer0 interrupt enable bit */ 01005 StoreTIMSK(r24) /* Rewrite the TIMSK register, so interrupts are enabled after this instruction */ 01006 "out __SREG__, r25 \n\t" /* Restore the state of the global interrupts. */ 01007 "1: \n\t" /* We jump to this point if there is nothing to do. */ 01008 "ret \n\t" /* Done. */ 01009 "" :: 01010 [_ARB_] "i" (devAuxSysRegBit), 01011 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg)), 01012 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 01013 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 01014 [_OCIE0A_] "i" (devOCIE), 01015 [_OCIE0Ab_] "i" (preBitSet1(0x00,devOCIE)): 01016 "r24","r25" ); } 01017 01018 #endif 01019 01020 01021 #if ((cfgIntGlobalOnly == cfgFalse) && (cfgIntTickTrack == cfgTrue)) || (defCheckReportingError == cfgTrue) 01022 01023 void portDisableTickInterrupts(void) 01024 { /* This simply disables the tick interrupts. Nothing more. The call is not used by the OS itself, (portEnterTickInterrupts / 01025 * portExitTickInterrupts is used to that end), with the exception of being called from the error handling. */ 01026 asm volatile ( 01027 "in r25, __SREG__ \n\t" /* Copy the status register as quickly as possible and disable interrupts, even if it was cleared */ 01028 "cli \n\t" /* already. This is needed for a tick may intervene and alter other bits of the TIMSK register. */ 01029 LoadTIMSK(r24) /* Load the TIMSK register, holding the state of the tick interrupt on bit OCIE0A, */ 01030 "andi r24, %[_nOCIE0Ab_] \n\t" /* Reset the timer0 interrupt enable bit */ 01031 StoreTIMSK(r24) /* Rewrite the TIMSK register, so interrupts are disabled after this instruction */ 01032 "out __SREG__, r25 \n\t" /* Restore the state of the global interrupts. */ 01033 "ret \n\t" /* Done. */ 01034 "" :: 01035 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 01036 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 01037 [_OCIE0A_] "i" (devOCIE), 01038 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)): 01039 "r24","r25" ); } 01040 01041 #endif 01042 01043 01044 #if (cfgIntGlobalOnly == cfgFalse) && (cfgIntTickTrack == cfgTrue) 01045 01046 void portEnableTickInterrupts(void) 01047 { /* This simply enables the tick interrupts. Nothing more. The call is not used by the OS itself, (portEnterTickInterrupts / 01048 * portExitTickInterrupts is used to that end). */ 01049 asm volatile ( 01050 "in r25, __SREG__ \n\t" /* Copy the status register as quickly as possible and disable interrupts, even if it was cleared */ 01051 "cli \n\t" /* already. This is needed for an other interrupt may intervene and alter other bits of the TIMSK */ 01052 LoadTIMSK(r24) /* Load the TIMSK register, holding the state of the tick interrupt on bit OCIE0A, */ 01053 "sbr r24, %[_OCIE0Ab_] \n\t" /* Set the timer0 interrupt enable bit */ 01054 StoreTIMSK(r24) /* Rewrite the TIMSK register, so interrupts are enabled after this instruction */ 01055 "out __SREG__, r25 \n\t" /* Restore the state of the global interrupts. */ 01056 "ret \n\t" /* Done. */ 01057 "" :: 01058 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 01059 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 01060 [_OCIE0A_] "i" (devOCIE), 01061 [_OCIE0Ab_] "i" (preBitSet1(0x00,devOCIE)): 01062 "r24","r25" ); } 01063 01064 #endif 01065 01066 01067 #if (cfgSysClearUnusedR1 == cfgTrue) 01068 /* Checking on r1 is special since gcc always assumes the register to be zero. */ 01069 #define CheckRegisterR1 "cpse r1,r30 \n\t" /* Thus we compare not to r2 but to r31 which contains zero at the place this is inserted */ 01070 #define ClearRegisterR1 "clr r1 \n\t" /* If we make use of the gcc we are save by always clearing r1 */ 01071 #define FillRegisterR1 "" /* The register is not filled with r2 which contains some magic byte to check with. */ 01072 #else 01073 /* Not using gcc r1 is just like other registers */ 01074 #define CheckRegisterR1 "cpse r1,r29 \n\t" /* r1 is now compared with the magic check byte just like the other registers */ 01075 #define ClearRegisterR1 "" /* r1 is not cleared. */ 01076 #define FillRegisterR1 "mov r1,r2 \n\t" /* r1 is now filled with the magic check byte just like the other registers */ 01077 #endif 01078 01079 01080 /* The return value is placed in r24, the standard register used by gcc. */ 01081 #if (cfgUseSynchronization != cfgSyncNon) && (defUseBoolReturns == cfgTrue) 01082 #define ReturnState(arg) \ 01083 "lds r16," #arg " \n\t" /* Now load the return value in r16, which is still free for use */\ 01084 "sbrs r16,%[_RET1_] \n\t" /* If the bit 1 is set, we don't copy the return value ... */\ 01085 "mov r24, r16 \n\t" /* load the result. */ 01086 /* This contains the value that must be returned (false, non-false) */ 01087 #define ReturnField &xOS.pxSave.uiReturn 01088 #else 01089 #define ReturnState(arg) "" 01090 #define ReturnField 0 01091 #endif 01092 01093 01094 /* If we protect the OS from interruptions, we allow for call to the operating system 01095 * from isr. We must have some mechanism to detect that we are calling from an isr. 01096 * Thus we replace the uiOSstatus by a value which indicates that we are in an 01097 * isr, but we must restore the original status as we leave the isr. Note that we 01098 * may replace since we may never call functions from within the OS which need the 01099 * other bits. Note, this code may not corrupt the status register. The uiOSstatus is 01100 * temporarily saved in an unused background variable, and (thus) we may never call this 01101 * when we want to execute an isr in OS space with an OS stack. 01102 */ 01103 #if (cfgIntOsProtected == cfgTrue) 01104 /* Replace the uiOSstatus with the key that indicates we are in the isr context */ 01105 #define SaveOsStatus(stat,save,key) \ 01106 "lds r31," #stat " \n\t" /* Load the uiOSstatus */\ 01107 "sts " #save ", r31 \n\t" /* Save that value in r31 background variable. */\ 01108 "ldi r31," #key " \n\t" /* Load the defContextStateIsr key */\ 01109 "sts " #stat ", r31 \n\t" /* Store that key in the uiOSstatus. Destruction of other bits is allowed. */ 01110 /* Repair the uiOSstatus */ 01111 #define RestoreOsStatus(stat,save) \ 01112 "lds r31," #save " \n\t" /* Recall the original value of the uiOSstatus */\ 01113 "sts " #stat ", r31 \n\t" /* Restore that Status */ 01114 #else 01115 #define SaveOsStatus(stat,save,key) "" 01116 #define RestoreOsStatus(stat,save) "" 01117 #endif 01118 01119 01120 #if (cfgIntUserDefined == cfgTrue) && (cfgIntOsProtected == cfgTrue) && (includeIsrEnter == cfgTrue) && (cfgIntSwitchUsesOSstack == cfgFalse) 01121 01122 void portEnterISR(void) 01123 /* Enters the isr with the isrStack, thus performs a stack switch and an update of the uiOSstatus. The return address is 01124 * remembered so we will return to the code implementing the isr when the stack switch is over. Since we replace the 01125 * stack pointer, global interrupts must be switched off. (Note that defContextStateIsr must be 0xb11000000.) 01126 * Note that for up-growing stacks this code is quite different, and not implemented yet 01127 */ 01128 { asm volatile ( 01129 "pop r24 \n\t" /* Get the first byte of the return address, which is on top of the current stack. */ 01130 "sts %[_ISR1_], r24 \n\t" /* Place it on the new isr stack in the correct position. */ 01131 "pop r24 \n\t" /* Get the second byte of the return address, which is now on top of the current stack. */ 01132 "sts %[_ISR2_], r24 \n\t" /* Place it on the new isr stack in the correct position. */ 01133 #if (defThreeByteAddress == cfgTrue) 01134 "pop r24 \n\t" /* Get the third byte of the return address, which is now on top of the current stack. */ 01135 "sts %[_ISR3_], r24 \n\t" /* Place it on the new isr stack in the correct position. */ 01136 #endif 01137 "lds r24,%[_Status_] \n\t" /* Load the uiOSstatus, now indicating we are running something else. */ 01138 "ori r24,%[_ConIsr_] \n\t" /* Place the key bits in place (do not overwrite the other bits, we do not save the uiOSstatus!) */ 01139 "sts %[_Status_], r24 \n\t" /* Replace the uiOSstatus register with bits adapted. */ 01140 "ldi r24,lo8(%[_ISRst_]) \n\t" /* Now load the low byte of the new (isr) stack */ 01141 "out __SP_L__,r24 \n\t" /* Replace the stack pointer */ 01142 #if (defStackSpaceExceedsOneByte == cfgTrue) 01143 "ldi r24,hi8(%[_ISRst_]) \n\t" /* Now load the high byte of the new (isr) stack */ 01144 "out __SP_H__,r24 \n\t" /* Replace the stack pointer */ 01145 #endif 01146 "ret \n\t" /* Done, return to the isr with a brand new stack. */ 01147 "" :: 01148 [_ISR1_] "i" (&StackISR[ISRstackFirstByte]), 01149 [_ISR2_] "i" (&StackISR[ISRstackSecondByte]), 01150 [_ISR3_] "i" (&StackISR[ISRstackThirdByte]), 01151 [_ISRst_] "i" (&StackISR[ISRstackStart]), 01152 [_ConIsr_] "i" (defContextStateIsr), 01153 [_Status_] "i" (&uiOsStatus) ) 01154 ; } 01155 01156 #endif 01157 01158 01159 #if (cfgIntUserDefined == cfgTrue) && (cfgIntOsProtected == cfgTrue) && (includeIsrEnter == cfgTrue) && (cfgIntSwitchUsesOSstack == cfgTrue) 01160 01161 void portEnterISR(void) 01162 /* Enters the isr with the OsStack. Since this is performed after a context switch we are already in 01163 * Os Space. so no stack transplant needed. What remains is changing the OsStatus. (Note that defContextStateIsr 01164 * must be 0xb11000000.) 01165 */ 01166 { asm volatile ( 01167 "lds r24,%[_Status_] \n\t" /* Load the uiOSstatus, now indicating we are running something else. */ 01168 "ori r24,%[_ConIsr_] \n\t" /* Place the key bits in place (do not overwrite the other bits, we do not save the uiOSstatus!) */ 01169 "sts %[_Status_], r24 \n\t" /* Replace the uiOSstatus register with bits adapted. */ 01170 "ret \n\t" /* Done, return to the isr with a brand new stack. */ 01171 "" :: 01172 [_ConIsr_] "i" (defContextStateIsr), 01173 [_Status_] "i" (&uiOsStatus) ) 01174 ; } 01175 01176 #endif 01177 01178 01179 01180 #if (defUseIsrStack == cfgTrue) && (includeIsrBegin == cfgTrue) 01181 01182 void portBeginISR(void) 01183 /* Call this if you want to start an isr directly, thus without a context switch first. It may even interrupt 01184 * the OS. The stack must be switched but the registers and the status must not be effected. It is up to the 01185 * user to save these. Of course, this routine may only be called with interrupts switched off. */ 01186 { asm volatile ( 01187 "sts %[_ISRlst_], r31 \n\t" /* Save the current value of register r31 (Note, not in the background r31, this is not free!) */ 01188 "pop r31 \n\t" /* Get the first byte of the return address, which is on top of the current stack. */ 01189 "sts %[_ISR1_], r31 \n\t" /* Place it on the new isr stack in the correct position. */ 01190 "pop r31 \n\t" /* Get the second byte of the return address, which is now on top of the current stack. */ 01191 "sts %[_ISR2_], r31 \n\t" /* Place it on the new isr stack in the correct position. */ 01192 #if (defThreeByteAddress == cfgTrue) 01193 "pop r31 \n\t" /* Get the third byte of the return address, which is now on top of the current stack. */ 01194 "sts %[_ISR3_], r31 \n\t" /* Place it on the new isr stack in the correct position. */ 01195 #endif 01196 "in r31,__SP_L__ \n\t" /* The current stack pointer must be rescued, its value is put below(!) the return address of the */ 01197 "sts %[_ISRspl_], r31 \n\t" /* isr stack. (Normally these values are not used for anything else) */ 01198 #if (defStackSpaceExceedsOneByte == cfgTrue) 01199 "in r31,__SP_H__ \n\t" /* Get the high byte of the current stack pointer */ 01200 "sts %[_ISRsph_], r31 \n\t" /* Save it. */ 01201 #endif 01202 "ldi r31,lo8(%[_ISRst_]) \n\t" /* Now make the stack switch, Load the pointer at which we want to start (above the return */ 01203 "out __SP_L__,r31 \n\t" /* address) and store it in the low and high byte respectively. */ 01204 #if (defStackSpaceExceedsOneByte == cfgTrue) 01205 "ldi r31,hi8(%[_ISRst_]) \n\t" /* Get the high byte of the new */ 01206 "out __SP_H__,r31 \n\t" /* Save it. */ 01207 #endif 01208 SaveOsStatus(%[_Status_],%[_s31_],%[_ConISR_]) /* If we have OS protection, we may perform genXXX calls, inform the system */ 01209 "lds r31,%[_ISRlst_] \n\t" /* Restore the value of r31 */ 01210 "ret \n\t" /* Return with the registers and stack intact */ 01211 "" :: 01212 [_ISRspl_] "i" (&StackISR[ISRstackSPL]), 01213 [_ISRsph_] "i" (&StackISR[ISRstackSPH]), 01214 [_ISR1_] "i" (&StackISR[ISRstackFirstByte]), 01215 [_ISR2_] "i" (&StackISR[ISRstackSecondByte]), 01216 [_ISR3_] "i" (&StackISR[ISRstackThirdByte]), 01217 [_ISRlst_] "i" (&StackISR[ISRstackLastByte]), 01218 [_s31_] "i" (&xOS.pxSave.r31), 01219 [_ISRst_] "i" (&StackISR[ISRstackStart]), 01220 [_ConISR_] "i" (defContextStateIsr), 01221 [_Status_] "i" (&uiOsStatus) ) 01222 ; } 01223 01224 #endif 01225 01226 01227 #if (defUseIsrStack == cfgTrue) && (includeIsrEndReturn == cfgTrue) 01228 01229 void portReturnISR(void) 01230 /* Call this to end you isr. Do not call reti from your isr, the return is performed by this method. It restores the 01231 * stack and, if needed the OS stack. You are responsible for the registers and the status. Interrupts must (still) 01232 * be disabled when calling this routine. */ 01233 { asm volatile ( 01234 "sts %[_ISRlst_], r31 \n\t" /* Save the current value of register r31 (Note, not in the background r31, this is not free!) */ 01235 RestoreOsStatus(%[_Status_],%[_s31_]) /* If we have OS protection, we have replaced the uiOSstatus, which must be repaired */ 01236 "lds r31,%[_ISRspl_] \n\t" /* Load the old low byte stack pointer */ 01237 "out __SP_L__,r31 \n\t" /* Restore it */ 01238 #if (defStackSpaceExceedsOneByte == cfgTrue) 01239 "lds r31,%[_ISRsph_] \n\t" /* Load the old high byte stack pointer */ 01240 "out __SP_H__,r31 \n\t" /* Restore it */ 01241 #endif 01242 "lds r31,%[_ISRlst_] \n\t" /* Restore the value of r31 */ 01243 "reti \n\t" /* Return to the interrupted code with interrupts activated. */ 01244 "" :: 01245 [_ISRspl_] "i" (&StackISR[ISRstackSPL]), 01246 [_ISRsph_] "i" (&StackISR[ISRstackSPH]), 01247 [_s31_] "i" (&xOS.pxSave.r31), 01248 [_ISRlst_] "i" (&StackISR[ISRstackLastByte]), 01249 [_Status_] "i" (&uiOsStatus) ) 01250 ; } 01251 01252 #endif 01253 01254 01255 #if (defUseIsrStack == cfgTrue) && (cfgIntOsProtected == cfgTrue) && (includeIsrEndYield == cfgTrue) && (includeTaskYield == cfgTrue) 01256 01257 void portYieldISR(void) 01258 /* Identical functionality as portReturnISR but does not return to the original point of interrupt, but directly 01259 * performs a context switch, possibly to activate a resumed task. Of course this functionality is only available 01260 * when a task is interrupted thus when we have OS protection on. */ 01261 { asm volatile ( 01262 "sts %[_ISRlst_], r31 \n\t" /* Save the current value of register r31 (Note, not in the background r31, this is not free!) */ 01263 RestoreOsStatus(%[_Status_],%[_s31_]) /* If we have OS protection, we have replaced the uiOSstatus, which must be repaired */ 01264 "lds r31,%[_ISRspl_] \n\t" /* Load the old low byte stack pointer */ 01265 "out __SP_L__,r31 \n\t" /* Restore it */ 01266 #if (defStackSpaceExceedsOneByte == cfgTrue) 01267 "lds r31,%[_ISRsph_] \n\t" /* Load the old high byte stack pointer */ 01268 "out __SP_H__,r31 \n\t" /* Restore it */ 01269 #endif 01270 "lds r31,%[_ISRlst_] \n\t" /* Restore the value of r31 */ 01271 "" :: 01272 [_ISRspl_] "i" (&StackISR[ISRstackSPL]), 01273 [_ISRsph_] "i" (&StackISR[ISRstackSPH]), 01274 [_s31_] "i" (&xOS.pxSave.r31), 01275 [_ISRlst_] "i" (&StackISR[ISRstackLastByte]), 01276 [_Status_] "i" (&uiOsStatus) ); 01277 /* Don't return but directly perform a context switch, use a portJump because the jump may 01278 * not be possible with the rjmp. */ 01279 portJump(taskYield); } 01280 01281 #endif 01282 01283 01284 /* DISCUSSION 01285 * Below you will find the portSave and portRestore context. The code in these routines is build on the settings 01286 * of the configuration parameters of the different tasks. First, we distinguish to main blocks, of which only 01287 * one is compiled depending on the setting of cfgCheckRegisters, cfgCheckTaskStack, cfgCheckWatermarks. If one of 01288 * these is cfgTrue, checks are performed on the use of the registers or stack, i.e. this is protected context switching. 01289 * For every group of registers it is 01290 * decided separately if check or save/restore actions are needed. In general this is the approach: 01291 * (1) Registers that are used in all tasks are always saved/restored, and no code is added to test if 01292 * these actions should be skipped, since there is no need. 01293 * (2) Registers that are used in some tasks are conditionally saved/restored. 01294 * (3) Registers that are used in none of the tasks are completely left out of save/restore. 01295 * The same kind of scheme holds with respect to register use checking, since this may be specified separately, 01296 * but now for filling with the check byte and checking against the check byte. 01297 * Of course this code is all AVR specific. 01298 */ 01299 01300 /* Save registers r28r29r30r31 if they are used, in any of the tasks. */ 01301 #if ((defRegisterUseCollect & r28r29r30r31) == r28r29r30r31) 01302 /* This define is for protected context switching. */ 01303 #define SaveGroup7b \ 01304 "lds r28,%[_r28_] \n\t" /* Load original value of register r28 */ \ 01305 "push r28 \n\t" /* Push it on the right place on the stack */ \ 01306 "lds r28,%[_r29_] \n\t" /* Load original value of register r29 */ \ 01307 "push r28 \n\t" /* Push it on the right place on the stack */ \ 01308 "lds r28,%[_r30_] \n\t" /* Load original value of register r30 */ \ 01309 "push r28 \n\t" /* Push it on the right place on the stack */ \ 01310 "lds r28,%[_r31_] \n\t" /* Load original value of register r31 */ \ 01311 "push r28 \n\t" /* Push it on the right place on the stack */ 01312 01313 /* This define is for standard context switching. */ 01314 #define SaveGroup7a \ 01315 "lds r31,%[_r31_] \n\t" \ 01316 "push r28 \n\t" \ 01317 "push r29 \n\t" \ 01318 "push r30 \n\t" \ 01319 "push r31 \n\t" 01320 #else 01321 #define SaveGroup7a "" 01322 #define SaveGroup7b "" 01323 #endif 01324 01325 /* Save registers r24r25r26r27 if they are used, in any of the tasks. */ 01326 #if ((defRegisterUseCollect & r24r25r26r27) == r24r25r26r27) 01327 #define SaveGroup6 \ 01328 "push r24 \n\t" \ 01329 "push r25 \n\t" \ 01330 "push r26 \n\t" \ 01331 "push r27 \n\t" 01332 #else 01333 #define SaveGroup6 "" 01334 #endif 01335 01336 /* Save registers r20r21r22r23 if they are used, in any of the tasks. */ 01337 #if ((defRegisterUseCollect & r20r21r22r23) == r20r21r22r23) 01338 #define SaveGroup5 \ 01339 "push r20 \n\t" \ 01340 "push r21 \n\t" \ 01341 "push r22 \n\t" \ 01342 "push r23 \n\t" 01343 #else 01344 #define SaveGroup5 "" 01345 #endif 01346 01347 /* Save registers r16r17r18r19 if they are used, in any of the tasks. */ 01348 #if ((defRegisterUseCollect & r16r17r18r19) == r16r17r18r19) 01349 #define SaveGroup4 \ 01350 "push r16 \n\t" \ 01351 "push r17 \n\t" \ 01352 "push r18 \n\t" \ 01353 "push r19 \n\t" 01354 #else 01355 #define SaveGroup4 "" 01356 #endif 01357 01358 /* Save registers r12r13r14r15 if they are used, in any of the tasks. */ 01359 #if ((defRegisterUseCollect & r12r13r14r15) == r12r13r14r15) 01360 #define SaveGroup3 \ 01361 "push r12 \n\t" \ 01362 "push r13 \n\t" \ 01363 "push r14 \n\t" \ 01364 "push r15 \n\t" 01365 #else 01366 #define SaveGroup3 "" 01367 #endif 01368 01369 /* Save registers r08r09r10r11 if they are used, in any of the tasks. */ 01370 #if ((defRegisterUseCollect & r08r09r10r11) == r08r09r10r11) 01371 #define SaveGroup2 \ 01372 "push r8 \n\t" \ 01373 "push r9 \n\t" \ 01374 "push r10 \n\t" \ 01375 "push r11 \n\t" 01376 #else 01377 #define SaveGroup2 "" 01378 #endif 01379 01380 /* Save registers r04r05r06r07 if they are used, in any of the tasks. */ 01381 #if ((defRegisterUseCollect & r04r05r06r07) == r04r05r06r07) 01382 #define SaveGroup1 \ 01383 "push r4 \n\t" \ 01384 "push r5 \n\t" \ 01385 "push r6 \n\t" \ 01386 "push r7 \n\t" 01387 #else 01388 #define SaveGroup1 "" 01389 #endif 01390 01391 /* Save registers r00r01r02r03 if they are used, in any of the tasks. */ 01392 #if ((defRegisterUseCollect & r00r01r02r03) == r00r01r02r03) 01393 #define SaveGroup0 \ 01394 "push r0 \n\t" \ 01395 "push r1 \n\t" \ 01396 "push r2 \n\t" \ 01397 "push r3 \n\t" 01398 #else 01399 #define SaveGroup0 "" 01400 #endif 01401 01402 01403 /* Test if the registergroup r28r29r30r31 must be saved for the current task */ 01404 #if ((defRegisterUseVariable & r28r29r30r31) == r28r29r30r31) 01405 #define TestSaveGroup7 \ 01406 "sbrs r31, 7 \n\t" \ 01407 "rjmp 7f \n\t" 01408 #else 01409 #define TestSaveGroup7 "" 01410 #endif 01411 01412 /* Test if the registergroup r24r25r26r27 must be saved for the current task */ 01413 #if ((defRegisterUseVariable & r24r25r26r27) == r24r25r26r27) 01414 #define TestSaveGroup6 \ 01415 "sbrs r31, 6 \n\t" \ 01416 "rjmp 6f \n\t" 01417 #else 01418 #define TestSaveGroup6 "" 01419 #endif 01420 01421 /* Test if the registergroup r00r01r02r03 must be saved for the current task */ 01422 #if ((defRegisterUseVariable & r20r21r22r23) == r20r21r22r23) 01423 #define TestSaveGroup5 \ 01424 "sbrs r31, 5 \n\t" \ 01425 "rjmp 5f \n\t" 01426 #else 01427 #define TestSaveGroup5 "" 01428 #endif 01429 01430 /* Test if the registergroup r16r17r18r19 must be saved for the current task */ 01431 #if ((defRegisterUseVariable & r16r17r18r19) == r16r17r18r19) 01432 #define TestSaveGroup4 \ 01433 "sbrs r31, 4 \n\t" \ 01434 "rjmp 4f \n\t" 01435 #else 01436 #define TestSaveGroup4 "" 01437 #endif 01438 01439 /* Test if the registergroup r00r01r02r03 must be saved for the current task */ 01440 #if ((defRegisterUseVariable & r12r13r14r15) == r12r13r14r15) 01441 #define TestSaveGroup3 \ 01442 "sbrs r31, 3 \n\t" \ 01443 "rjmp 3f \n\t" 01444 #else 01445 #define TestSaveGroup3 "" 01446 #endif 01447 01448 /* Test if the registergroup r08r09r10r11 must be saved for the current task */ 01449 #if ((defRegisterUseVariable & r08r09r10r11) == r08r09r10r11) 01450 #define TestSaveGroup2 \ 01451 "sbrs r31, 2 \n\t" \ 01452 "rjmp 2f \n\t" 01453 #else 01454 #define TestSaveGroup2 "" 01455 #endif 01456 01457 /* Test if the registergroup r04r05r06r07 must be saved for the current task */ 01458 #if ((defRegisterUseVariable & r04r05r06r07) == r04r05r06r07) 01459 #define TestSaveGroup1 \ 01460 "sbrs r31, 1 \n\t" \ 01461 "rjmp 1f \n\t" 01462 #else 01463 #define TestSaveGroup1 "" 01464 #endif 01465 01466 /* Test if the registergroup r00r01r02r03 must be saved for the current task */ 01467 #if ((defRegisterUseVariable & r00r01r02r03) == r00r01r02r03) 01468 #define TestSaveGroup0 \ 01469 "sbrs r31, 0 \n\t" \ 01470 "rjmp 0f \n\t" 01471 #else 01472 #define TestSaveGroup0 "" 01473 #endif 01474 01475 01476 /* Check the content of registergroup r28r29r30r31. */ 01477 #if ((defRegisterCheckCollect & r28r29r30r31) == r28r29r30r31) 01478 #define CheckGroup7 \ 01479 "lds r28,%[_r28_] \n\t" \ 01480 "cpse r28,r29 \n\t" \ 01481 "sbr r30,0x80 \n\t" \ 01482 "lds r28,%[_r29_] \n\t" \ 01483 "cpse r28,r29 \n\t" \ 01484 "sbr r30,0x80 \n\t" \ 01485 "lds r28,%[_r30_] \n\t" \ 01486 "cpse r28,r29 \n\t" \ 01487 "sbr r30,0x80 \n\t" \ 01488 "lds r28,%[_r31_] \n\t" \ 01489 "cpse r28,r29 \n\t" \ 01490 "sbr r30,0x80 \n\t" 01491 #else 01492 #define CheckGroup7 "" 01493 #endif 01494 01495 /* Check the content of registergroup r24r25r26r27. */ 01496 #if ((defRegisterCheckCollect & r24r25r26r27) == r24r25r26r27) 01497 #define CheckGroup6 \ 01498 "cpse r24,r29 \n\t" \ 01499 "sbr r30,0x40 \n\t" \ 01500 "cpse r25,r29 \n\t" \ 01501 "sbr r30,0x40 \n\t" \ 01502 "cpse r26,r29 \n\t" \ 01503 "sbr r30,0x40 \n\t" \ 01504 "cpse r27,r29 \n\t" \ 01505 "sbr r30,0x40 \n\t" 01506 #else 01507 #define CheckGroup6 "" 01508 #endif 01509 01510 /* Check the content of registergroup r20r21r22r23. */ 01511 #if ((defRegisterCheckCollect & r20r21r22r23) == r20r21r22r23) 01512 #define CheckGroup5 \ 01513 "cpse r20,r29 \n\t" \ 01514 "sbr r30,0x20 \n\t" \ 01515 "cpse r21,r29 \n\t" \ 01516 "sbr r30,0x20 \n\t" \ 01517 "cpse r22,r29 \n\t" \ 01518 "sbr r30,0x20 \n\t" \ 01519 "cpse r23,r29 \n\t" \ 01520 "sbr r30,0x20 \n\t" 01521 #else 01522 #define CheckGroup5 "" 01523 #endif 01524 01525 /* Check the content of registergroup r16r17r18r19. */ 01526 #if ((defRegisterCheckCollect & r16r17r18r19) == r16r17r18r19) 01527 #define CheckGroup4 \ 01528 "cpse r16,r29 \n\t" \ 01529 "sbr r30,0x10 \n\t" \ 01530 "cpse r17,r29 \n\t" \ 01531 "sbr r30,0x10 \n\t" \ 01532 "cpse r18,r29 \n\t" \ 01533 "sbr r30,0x10 \n\t" \ 01534 "cpse r19,r29 \n\t" \ 01535 "sbr r30,0x10 \n\t" 01536 #else 01537 #define CheckGroup4 "" 01538 #endif 01539 01540 /* Check the content of registergroup r12r13r14r15. */ 01541 #if ((defRegisterCheckCollect & r12r13r14r15) == r12r13r14r15) 01542 #define CheckGroup3 \ 01543 "cpse r12,r29 \n\t" \ 01544 "sbr r30,0x08 \n\t" \ 01545 "cpse r13,r29 \n\t" \ 01546 "sbr r30,0x08 \n\t" \ 01547 "cpse r14,r29 \n\t" \ 01548 "sbr r30,0x08 \n\t" \ 01549 "cpse r15,r29 \n\t" \ 01550 "sbr r30,0x08 \n\t" 01551 #else 01552 #define CheckGroup3 "" 01553 #endif 01554 01555 /* Check the content of registergroup r08r09r10r11. */ 01556 #if ((defRegisterCheckCollect & r08r09r10r11) == r08r09r10r11) 01557 #define CheckGroup2 \ 01558 "cpse r8,r29 \n\t" \ 01559 "sbr r30,0x04 \n\t" \ 01560 "cpse r9,r29 \n\t" \ 01561 "sbr r30,0x04 \n\t" \ 01562 "cpse r10,r29 \n\t" \ 01563 "sbr r30,0x04 \n\t" \ 01564 "cpse r11,r29 \n\t" \ 01565 "sbr r30,0x04 \n\t" 01566 #else 01567 #define CheckGroup2 "" 01568 #endif 01569 01570 /* Check the content of registergroup r04r05r06r07. */ 01571 #if ((defRegisterCheckCollect & r04r05r06r07) == r04r05r06r07) 01572 #define CheckGroup1 \ 01573 "cpse r4,r29 \n\t" \ 01574 "sbr r30,0x02 \n\t" \ 01575 "cpse r5,r29 \n\t" \ 01576 "sbr r30,0x02 \n\t" \ 01577 "cpse r6,r29 \n\t" \ 01578 "sbr r30,0x02 \n\t" \ 01579 "cpse r7,r29 \n\t" \ 01580 "sbr r30,0x02 \n\t" 01581 #else 01582 #define CheckGroup1 "" 01583 #endif 01584 01585 /* Check the content of registergroup r00r01r02r03. */ 01586 #if ((defRegisterCheckCollect & r00r01r02r03) == r00r01r02r03) 01587 #define CheckGroup0 \ 01588 CheckRegisterR1 \ 01589 "sbr r30,0x01 \n\t" \ 01590 "cpse r0,r29 \n\t" \ 01591 "sbr r30,0x01 \n\t" \ 01592 "cpse r2,r29 \n\t" \ 01593 "sbr r30,0x01 \n\t" \ 01594 "cpse r3,r29 \n\t" \ 01595 "sbr r30,0x01 \n\t" 01596 #else 01597 #define CheckGroup0 "" 01598 #endif 01599 01600 01601 void portSaveContext(void) 01602 { /* PortSaveContext must do what it promises, thus save all used registers for the current task 01603 * on the stack, and save the device status register too. Furthermore, it must switch from task 01604 * stack to OS stack, store if global interrupt was enabled on the moment 01605 * we left the task, disable the tick interrupt, and if requested, check if the stack will 01606 * not overflow (it must not save the context if the stack will overflow) and check if 01607 * no registers are changed which the task promised not to use. And all this must be done while 01608 * leaving ALL registers unchanged, since they may contain parameters for some OS function that 01609 * was called. The device status register must be left unchanged, until it is saved, which is 01610 * at the end of the the routine, i.e. the status is saved last. The latter seems highly unpractical 01611 * but the reason is that, if we save the status first, we must restore it last, after all registers 01612 * are restored. At that time we do have any registers to work with left. This can be circumvented 01613 * off course, but this costs extra code, and the current implementation does not. 01614 * Note that, since the stack changes in this method global interrupts must be switched off. 01615 */ 01616 01617 #if (cfgCheckRegisters == cfgTrue) || (cfgCheckTaskStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 01618 01619 /* This is the code for protected context saving. */ 01620 asm volatile ( 01621 "sts %[_r31_], r31 \n\t" /* We need quite some space to work, so we start of by saving the registers r31,r30,r29,r28. */ 01622 "sts %[_r30_], r30 \n\t" /* These are placed at a special reserved place on the OS stack, since that space is not used at */ 01623 "sts %[_r29_], r29 \n\t" /* the moment. */ 01624 "sts %[_r28_], r28 \n\t" /* */ 01625 "pop r31 \n\t" /* Now we must save the return address. The top address of the stack is the address which called */ 01626 "sts %[_OS1_], r31 \n\t" /* portSaveContext, and this is the place where we must return to. Since we change stack at the */ 01627 "pop r31 \n\t" /* end we displace this address to the OS stack. After this, the top address on the current stack */ 01628 "sts %[_OS2_], r31 \n\t" /* is the address at which the task must be restarted after the portRestoreContext. */ 01629 #if (defThreeByteAddress == cfgTrue) 01630 "pop r31 \n\t" /* Get the third byte of the return address, which is now on top of the current stack. */ 01631 "sts %[_OS3_], r31 \n\t" /* Place it on the OS stack in the correct position. */ 01632 #endif 01633 "lds r31,%[_Status_] \n\t" /* Now we check from which place we are coming. If this is not a task, but the idle task (info */ 01634 "sbrc r31,%[_ConBit_] \n\t" /* is in the uiOSstatus) we can skip this, since there is nothing to be saved or checked. Jump in */ 01635 "rjmp 41f \n\t" /* that case directly to the place where timer interrupt is disabled and the stack is changed. */ 01636 "in r28, __SREG__ \n\t" /* Make a copy of the device status register, cause we must do some calculus below, preserve r28 */ 01637 "in r30,__SP_L__ \n\t" /* Now we will check if the stack call sustain all registers that must be pushed onto it. If not, */ 01638 "lds r29,(%[_StLim_]) \n\t" /* we will not even try, thereby rescuing the system from a crash, but we will simply inform the */ 01639 "sub r30,r29 \n\t" /* OS that we did not succeed. The OS will stop the current task, so it is not rescheduled. */ 01640 "ldi r30,0x00 \n\t" /* Load the StackPointer and the precalculated stack limit, contained in %[_StLim_],and calculate */ 01641 #if (defStackSpaceExceedsOneByte == cfgTrue) /* difference. We are not interested in the actual value, only the carry. We need r30*/ 01642 "in r31,__SP_H__ \n\t" /* to be zero below. If we make use of large stacks, We must repeat the subtraction for the */ 01643 "lds r29,(%[_StLim_])+1 \n\t" /* high byte. Continue the subtraction with sbc. If we have a small stack, we need not to take */ 01644 "sbc r31,r29 \n\t" /* into account the high byte SP_H (which still may be present though) */ 01645 #endif 01646 "brcc 20f \n\t" /* and skip if the stack will fit the space */ 01647 "sbr r30,%[_STKBITb_] \n\t" /* if not, indicate the error in uiStackTCheck (this is why r30 needed to be clean) */ 01648 "20: \n\t" 01649 "sts %[_StChk_],r30 \n\t" /* save the result in the uiStackTCheck */ 01650 "ldi r31,0x00 \n\t" /* Clear r31 to compare with and to collect the register test if we may continue, only if the */ 01651 "cpse r30,r31 \n\t" /* value of uiStackTCheck == 0, if not zero (r31!=r30), we do not save the stack (but do calcu- */ 01652 "rjmp 8f \n\t" /* late the new stack level), the task will be terminated, registers saved and not checked. */ 01653 "21: \n\t" /* Now we are ready to save and check the registers. (btw we know r30==0 since r30==r31) */ 01654 "lds r31,%[_RegUse_] \n\t" /* Load the parameter that describes which registers must be saved. */ 01655 "ldi r29,%[_RegByt_] \n\t" /* Load the parameter that describes which registers must be checked. */ 01656 CheckGroup0 /* Check if register group r00r01r03r03 must be checked (if included), collect result in r30 */ 01657 TestSaveGroup0 /* Check if register group r00r01r03r03 must be saved (if included), use r31 for that info */ 01658 SaveGroup0 /* Save register group r00r01r03r03 (if included). */ 01659 "0: \n\t" /* Copy the contents of the status register to a save place, freeing r28 for other use. We can */ 01660 "mov r1,r28 \n\t" /* use r1 for this purpose, since it is cleared afterwards and never used for parameters. */ 01661 CheckGroup1 /* Same as handling of Group0 */ 01662 TestSaveGroup1 /* */ 01663 SaveGroup1 /* */ 01664 "1: \n\t" /* */ 01665 CheckGroup2 /* Same as handling of Group0 */ 01666 TestSaveGroup2 /* */ 01667 SaveGroup2 /* */ 01668 "2: \n\t" /* */ 01669 CheckGroup3 /* Same as handling of Group0 */ 01670 TestSaveGroup3 /* */ 01671 SaveGroup3 /* */ 01672 "3: \n\t" /* */ 01673 CheckGroup4 /* Same as handling of Group0 */ 01674 TestSaveGroup4 /* */ 01675 SaveGroup4 /* */ 01676 "4: \n\t" /* */ 01677 CheckGroup5 /* Same as handling of Group0 */ 01678 TestSaveGroup5 /* */ 01679 SaveGroup5 /* */ 01680 "5: \n\t" /* */ 01681 CheckGroup6 /* Same as handling of Group0 */ 01682 TestSaveGroup6 /* */ 01683 SaveGroup6 /* */ 01684 "6: \n\t" /* */ 01685 CheckGroup7 /* The last instructions are a bit different since we must now check the registers we are using */ 01686 TestSaveGroup7 /* */ 01687 SaveGroup7b /* */ 01688 "7: \n\t" /* All registers are saved, r30 contains the result of the check. */ 01689 "mov r31, r1 \n\t" /* Now we start storing the status register. Move it to r31 for bit maintenance and storage ... */ 01690 #if (cfgSysSqueezeState == cfgTrue) /* We must distinguish between several situations. First in case we have a squeezed state */ 01691 #if (cfgIntTickTrack == cfgTrue)/* If we have tracking of the timer interrupts we must store the actual value. We are ordered to */ 01692 LoadTIMSK(r29) /* squeeze it into the status register. The only bit not used by gcc except for the H flag are I */ 01693 "bst r29, %[_OCIE0A_] \n\t" /* and V flag (overflow), thus copy the value of the tick bit unto the I flag (which is not used) */ 01694 "bld r31, %[_I_] \n\t" /* at the moment loading it in T and replacing it on the correct position. */ 01695 #endif /* The previous interrupt state is contained in the H bit, and that has already been set. */ 01696 #else /* If we do not squeeze, we have the state of the global interrupt at the moment the context */ 01697 #if (cfgIntTickTrack == cfgTrue)/* switch stored in devAuxSysRegBit. Now we must see if we must store the tick interrupts as well */ 01698 "sbr r31,%[_Ib_] \n\t" /* This is very hard since we lack the space. The trick is to store the mode (preemptive, equals */ 01699 LoadTIMSK(r29) /* both interrupts active as bit set in I, and cooperative as bit zero in I. In the coop mode the */ 01700 "bst r29, %[_OCIE0A_] \n\t" /* the status register is of no importance, thus may be used to store whatever you like. Thus */ 01701 "clh \n\t" /* first copy the tick interrupt state to the T-flag and the global interrupt state to the H-flag */ 01702 "sbic %[_ASR_],%[_ARB_] \n\t" /* Then we must determine in what mode we were when we arrived here. If one of the interrupts is */ 01703 "seh \n\t" /* reset we must be in the cooperative mode (tick interrupt not possible, and the secret route */ 01704 "brtc 50f \n\t" /* via an user interrupt and context switching is not possible when TickTrack is active. Other- */ 01705 "brhs 51f \n\t" /* wise we could be (but not need to be) in the preemptive mode. Former, we save both interrupts. */ 01706 "50: \n\t" /* We arrive here if one of the interrupts is switched off, thus we store the current manipulated */ 01707 "in r31, __SREG__ \n\t" /* status register where T and H represent the Tick and Global interrupt states, otherwise we */ 01708 "51: \n\t" /* store the original status register with I location bit set, indicating preemptive mode. */ 01709 #else /* If not we only need to store the global interrupt, which can best be done on its place holder */ 01710 "sbic %[_ASR_],%[_ARB_] \n\t" /* Get the state of the interrupt at the moment the context switch occurred and set the I bit */ 01711 "sbr r31,%[_Ib_] \n\t" /* to one if the interrupts where set. Per default that bit is zero */ 01712 #endif 01713 #endif 01714 "push r31 \n\t" /* The status register is the last register that is pushed (for the reason, see above) */ 01715 "lds r28,%[_RegChk_] \n\t" /* uiRegisterCheck contains the registers we want to check, others may be used but are ignored */ 01716 "and r30,r28 \n\t" /* deliberately, thus we calculate an and between the registers found to be used (in r30) and the */ 01717 "sts %[_RegChk_],r30 \n\t" /* ones we are interested in. Save the result at the same place. */ 01718 "8: \n\t" /* We calculate the new stack level below. */ 01719 "lds r1,%[_StOff_] \n\t" /* First load the stack offset, then load the low stack pointer. The difference is the new */ 01720 "in r31,__SP_L__ \n\t" /* stack level. This even holds true over an page border. Standard: 0x31-0x21 = 0x10 and over a */ 01721 "sub r1,r31 \n\t" /* page border: 0x0101 - 0x00F1 = 0x0010 becomes 0x01 - 0xF1 = 0x10 */ 01722 "lds r30,%[_StLev_] \n\t" /* Load the address of the stack level. */ 01723 "lds r31,(%[_StLev_])+1 \n\t" /* in the Z register for indirect storage. */ 01724 "st Z,r1 \n\t" /* Store the stack level, directly the right place. */ 01725 #if (defStackSizeExceedsOneByte == cfgTrue) 01726 "lds r1,(%[_StOff_])+1 \n\t" /* In case we have two byte stack level, we must also process the high byte. Load the stack high */ 01727 "in r31,__SP_H__ \n\t" /* byte of the offset, and the high byte of the stack pointer. The difference is the high byte of */ 01728 "sbc r1,r31 \n\t" /* the stack level, provided we take the carry of the previous subtraction into account. Reload */ 01729 "lds r31,(%[_StLev_])+1 \n\t" /* r31 (r30 is preserved) and take care to store the high byte stack level result at the correct */ 01730 "std Z+1,r1 \n\t" /* location, which is one further as the base address. (We may only work with r1,r30,r31 here) */ 01731 #endif /* Btw, if the StackSize exceeds one byte so must the StackSpace do, so we do not test for it */ 01732 "41: \n\t" /* Below we switch of the tick interrupts. We do that here since we may skip the part at label 7. */ 01733 LoadTIMSK(r30) /* Load the TIMSK in a register */ 01734 "andi r30, %[_nOCIE0Ab_] \n\t" /* Clear the tick interrupt bit (timer 0) */ 01735 StoreTIMSK(r30) /* write the new state to the timer register, disabling tick interrupts */ 01736 "ldi r31,lo8(%[_OSst_]) \n\t" /* Load the low byte of the OS stack. */ 01737 "out __SP_L__,r31 \n\t" /* Save it. */ 01738 #if (defStackSpaceExceedsOneByte == cfgTrue) 01739 "ldi r31,hi8(%[_OSst_]) \n\t" /* Load the high byte of the OS stack. */ 01740 "out __SP_H__,r31 \n\t" /* Save it. */ 01741 #endif 01742 "lds r30,%[_r30_] \n\t" /* Now we restore r30 and r31. Registers r28 and r29 do not need to be restored since if they we */ 01743 "lds r31,%[_r31_] \n\t" /* used by the task they where restored in SaveGroup7b, and if not they where already checked and */ 01744 #if (cfgSysSqueezeState == cfgFalse) 01745 "cbi %[_ASR_],%[_ARB_] \n\t" /* Make sure the portSaveContext is left with zero devAuxSysRegBit for interrupt routines that */ 01746 #endif /* may make use of genXXX routines. */ 01747 "clr r1 \n\t" /* the contents should not be important any more. Clear r1 since gcc generated code expects it. */ 01748 "ret \n\t" /* Done, return without enabling the global interrupt, that is done inside the OS if needed. */ 01749 "" ::\ 01750 [_OS1_] "i" (&xOS.StackOS[OSstackFirstByte]), 01751 [_OS2_] "i" (&xOS.StackOS[OSstackSecondByte]), 01752 [_OS3_] "i" (&xOS.StackOS[OSstackThirdByte]), 01753 [_OSst_] "i" (&xOS.StackOS[OSstackStart]), 01754 [_r31_] "i" (&xOS.pxSave.r31), 01755 [_r30_] "i" (&xOS.pxSave.r30), 01756 [_r29_] "i" (&xOS.pxSave.r29), 01757 [_r28_] "i" (&xOS.pxSave.r28), 01758 [_RegByt_] "i" (cfgSysRegisterCheckByte), 01759 [_RegUse_] "i" (&xOS.pxSave.uiRegisterUse), 01760 [_RegChk_] "i" (&xOS.pxSave.uiRegisterCheck), 01761 [_StOff_] "i" (&xOS.pxSave.pcStackOffset), 01762 [_StLev_] "i" (&xOS.pxSave.pcStackLevel), 01763 [_StLim_] "i" (&xOS.pxSave.pcStackLimit), 01764 [_StChk_] "i" (&xOS.pxSave.uiStackTCheck), 01765 [_ConBit_] "i" (defContextStateSaveBit), 01766 [_Status_] "i" (&uiOsStatus), 01767 [_I_] "i" (SREG_I), 01768 [_Ib_] "i" (preBitSet1(0x00,SREG_I)), 01769 [_Tb_] "i" (preBitSet1(0x00,SREG_T)), 01770 [_STKBIT_] "i" (defCheckStackBit), 01771 [_STKBITb_] "i" (preBitSet1(0x00,defCheckStackBit)), 01772 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 01773 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 01774 [_OCIE0A_] "i" (devOCIE), 01775 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)), 01776 [_ARB_] "i" (devAuxSysRegBit), 01777 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg))); 01778 01779 #else 01780 01781 /* This is the code standard context saving. */ 01782 asm volatile ( 01783 "sts %[_r31_], r31 \n\t" /* We need some space to work, so we start of by saving the registers r31,r30. */ 01784 "sts %[_r30_], r30 \n\t" /* These are placed at a special reserved place on the OS stack, not used at this moment */ 01785 "pop r31 \n\t" /* Now we must save the return address. The top address of the stack is the address which called */ 01786 "sts %[_OS1_], r31 \n\t" /* portSaveContext, and this is the place where we must return to. Since we change stack at the */ 01787 "pop r31 \n\t" /* end we displace this address to the OS stack. After this, the top address on the current stack */ 01788 "sts %[_OS2_], r31 \n\t" /* is the address at which the task must be restarted after the portRestoreContext. */ 01789 #if (defThreeByteAddress == cfgTrue) 01790 "pop r31 \n\t" /* Get the third byte of the return address, which is now on top of the current stack. */ 01791 "sts %[_OS3_], r31 \n\t" /* Place it on the OS stack in the correct position. */ 01792 #endif 01793 "lds r31,%[_Status_] \n\t" /* Now we check from which place we are coming. If this is not a task, but the idle task (info */ 01794 "sbrc r31,%[_ConBit_] \n\t" /* is in the uiOSstatus) we can skip this, since there is nothing to be saved or checked. Jump in */ 01795 "rjmp 41f \n\t" /* that case directly to the place where timer interrupt is disabled and the stack is changed. */ 01796 "lds r31,%[_RegUse_] \n\t" /* Load the parameter that describes which registers must be saved. */ 01797 TestSaveGroup0 /* Check if registergroup r00r01r03r03 must be saved (if included). */ 01798 SaveGroup0 /* Save registergroup r00r01r03r03 (if included). */ 01799 "0: \n\t" /* */ 01800 TestSaveGroup1 /* Same as handling of Group0 */ 01801 SaveGroup1 /* */ 01802 "1: \n\t" /* */ 01803 TestSaveGroup2 /* Same as handling of Group0 */ 01804 SaveGroup2 /* */ 01805 "2: \n\t" /* */ 01806 TestSaveGroup3 /* Same as handling of Group0 */ 01807 SaveGroup3 /* */ 01808 "3: \n\t" /* */ 01809 TestSaveGroup4 /* Same as handling of Group0 */ 01810 SaveGroup4 /* */ 01811 "4: \n\t" /* */ 01812 TestSaveGroup5 /* Same as handling of Group0 */ 01813 SaveGroup5 /* */ 01814 "5: \n\t" /* */ 01815 TestSaveGroup6 /* Same as handling of Group0 */ 01816 SaveGroup6 /* */ 01817 "6: \n\t" /* */ 01818 TestSaveGroup7 /* Same as handling of Group0 */ 01819 SaveGroup7a /* */ 01820 "7: \n\t" /* */ 01821 "in r31, __SREG__ \n\t" /* Until this point the status register must be unchanged, now is the time to save it. */ 01822 #if (cfgSysSqueezeState == cfgTrue) /* We must distinguish between several situations. First in case we have a squeezed state */ 01823 #if (cfgIntTickTrack == cfgTrue)/* If we have tracking of the timer interrupts we must store the actual value. We are ordered to */ 01824 LoadTIMSK(r30) /* squeeze it into the status register. The only bit not used by gcc except for the H flag are I */ 01825 "bst r30, %[_OCIE0A_] \n\t" /* and V flag (overflow), thus copy the value of the tick bit unto the I flag (which is not used) */ 01826 "bld r31, %[_I_] \n\t" /* at the moment loading it in T and replacing it on the correct position. */ 01827 #endif /* The previous interrupt state is contained in the H bit, and that has already been set. */ 01828 #else /* If we do not squeeze, we have the state of the global interrupt at the moment the context */ 01829 #if (cfgIntTickTrack == cfgTrue)/* switch stored in devAuxSysRegBit. Now we must see if we must store the tick interrupts as well */ 01830 "sbr r31,%[_Ib_] \n\t" /* This is very hard since we lack the space. The trick is to store the mode (preemptive, equals */ 01831 LoadTIMSK(r30) /* both interrupts active as bit set in I, and cooperative as bit zero in I. In the coop mode the */ 01832 "bst r30, %[_OCIE0A_] \n\t" /* the status register is of no importance, thus may be used to store whatever you like. Thus */ 01833 "clh \n\t" /* first copy the tick interrupt state to the T-flag and the global interrupt state to the H-flag */ 01834 "sbic %[_ASR_],%[_ARB_] \n\t" /* Then we must determine in what mode we were when we arrived here. If one of the interrupts is */ 01835 "seh \n\t" /* reset we must be in the cooperative mode (tick interrupt not possible, and the secret route */ 01836 "brtc 50f \n\t" /* via an user interrupt and context switching is not possible when TickTrack is active. Other- */ 01837 "brhs 51f \n\t" /* wise we could be (but not need to be) in the preemptive mode. Former, we save both interrupts. */ 01838 "50: \n\t" /* We arrive here if one of the interrupts is switched off, thus we store the current manipulated */ 01839 "in r31, __SREG__ \n\t" /* status register where T and H represent the Tick and Global interrupt states, otherwise we */ 01840 "51: \n\t" /* store the original status register with I location bit set, indicating preemptive mode. */ 01841 #else /* If not we only need to store the global interrupt, which can best be done on its place holder */ 01842 "sbic %[_ASR_],%[_ARB_] \n\t" /* Get the state of the interrupt at the moment the context switch occurred and set the I bit */ 01843 "sbr r31,%[_Ib_] \n\t" /* to one if the interrupts where set. Per default that bit is zero */ 01844 #endif 01845 #endif 01846 "push r31 \n\t" /* The status register is the last register that is pushed (for the reason, see above) */ 01847 "lds r1,%[_StOff_] \n\t" /* First load the stack offset, then load the low stack pointer. The difference is the new */ 01848 "in r31,__SP_L__ \n\t" /* stack level. This even holds true over an page border. Standard: 0x31-0x21 = 0x10 and over a */ 01849 "sub r1,r31 \n\t" /* page border: 0x0101 - 0x00F1 = 0x0010 becomes 0x01 - 0xF1 = 0x10 */ 01850 "lds r30,%[_StLev_] \n\t" /* Load the address of the stack level. */ 01851 "lds r31,(%[_StLev_])+1 \n\t" /* in the Z register for indirect storage. */ 01852 "st Z,r1 \n\t" /* Store the stack level, directly the right place. */ 01853 #if (defStackSizeExceedsOneByte == cfgTrue) 01854 "lds r1,(%[_StOff_])+1 \n\t" /* In case we have two byte stack level, we must also process the high byte. Load the stack high */ 01855 "in r31,__SP_H__ \n\t" /* byte of the offset, and the high byte of the stack pointer. The difference is the high byte of */ 01856 "sbc r1,r31 \n\t" /* the stack level, provided we take the carry of the previous subtraction into account. Reload */ 01857 "lds r31,(%[_StLev_])+1 \n\t" /* r31 (r30 is preserved) and take care to store the high byte stack level result at the correct */ 01858 "std Z+1,r1 \n\t" /* location, which is one further as the base address. (We may only work with r1,r30,r31 here) */ 01859 #endif /* Btw, if the StackSize exceeds one byte so must the StackSpace do, so we do not test for it */ 01860 "41: \n\t" /* Below we switch off the tick interrupts. We do that here since we may skip the part at label 7.*/ 01861 LoadTIMSK(r30) /* Load the TIMSK in a register */ 01862 "andi r30, %[_nOCIE0Ab_]\n\t" /* Clear the tick interrupt bit (timer 0) */ 01863 StoreTIMSK(r30) /* write the new state to the timer register, disabling tick interrupts */ 01864 "ldi r31,lo8(%[_OSst_]) \n\t" /* Load the low byte of the OS stack. */ 01865 "out __SP_L__,r31 \n\t" /* Save it. */ 01866 #if (defStackSpaceExceedsOneByte == cfgTrue) 01867 "ldi r31,hi8(%[_OSst_]) \n\t" /* Load the high byte of the OS stack. */ 01868 "out __SP_H__,r31 \n\t" /* Save it. */ 01869 #endif 01870 "lds r30,%[_r30_] \n\t" /* Now we restore r30 and r31. */ 01871 "lds r31,%[_r31_] \n\t" /* */ 01872 #if (cfgSysSqueezeState == cfgFalse) 01873 "cbi %[_ASR_],%[_ARB_] \n\t" /* Make sure the portSaveContext is left with zero devAuxSysRegBit for interrupt routines that */ 01874 #endif /* may make use of genXXX routines. */ 01875 "clr r1 \n\t" /* Clear r1 since gcc generated code expects it. */ 01876 "ret \n\t" /* Done, return without enabling the global interrupt, that is done inside the OS if needed. */ 01877 "" :: 01878 [_OS1_] "i" (&xOS.StackOS[OSstackFirstByte]), 01879 [_OS2_] "i" (&xOS.StackOS[OSstackSecondByte]), 01880 [_OS3_] "i" (&xOS.StackOS[OSstackThirdByte]), 01881 [_OSst_] "i" (&xOS.StackOS[OSstackStart]), 01882 [_r31_] "i" (&xOS.pxSave.r31), 01883 [_r30_] "i" (&xOS.pxSave.r30), 01884 [_RegUse_] "i" (&xOS.pxSave.uiRegisterUse), 01885 [_StOff_] "i" (&xOS.pxSave.pcStackOffset), 01886 [_StLev_] "i" (&xOS.pxSave.pcStackLevel), 01887 [_ConBit_] "i" (defContextStateSaveBit), 01888 [_Status_] "i" (&uiOsStatus), 01889 [_I_] "i" (SREG_I), 01890 [_Ib_] "i" (preBitSet1(0x00,SREG_I)), 01891 [_Tb_] "i" (preBitSet1(0x00,SREG_T)), 01892 [_Vb_] "i" (preBitSet1(0x00,SREG_V)), 01893 [_STKBIT_] "i" (defCheckStackBit), 01894 [_STKBITb_] "i" (preBitSet1(0x00,defCheckStackBit)), 01895 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 01896 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 01897 [_OCIE0A_] "i" (devOCIE), 01898 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)), 01899 [_ARB_] "i" (devAuxSysRegBit), 01900 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg))); 01901 01902 #endif 01903 } 01904 01905 /* DISCUSSION 01906 * One might wonder why there is so much work on the Save Context. Would porting not be 01907 * a lot simpler if we just saved the registers and the status, and left stack switching 01908 * and interrupt capture to the OS? Well, yes but there is a problem too. Some of the 01909 * OS function calls are switching, which means that as the application calls the function 01910 * a task switch must be performed also. Standard OS implementation perform function handling 01911 * in task space, issuing a copy of all OS handling on every stack. We use the OS stack so 01912 * we must switch stack and save the interrupts while leaving ALL registers in unchanged since 01913 * they might contain parameters of the calling function. GCC is not capable of generating 01914 * such code, so it must be done by hand. 01915 */ 01916 01917 /* Restore registers r28r29r30r31 if they are used, in any of the tasks. */ 01918 #if ((defRegisterUseCollect & r28r29r30r31) == r28r29r30r31) 01919 #define RestoreGroup7 \ 01920 "pop r31 \n\t" \ 01921 "pop r30 \n\t" \ 01922 "pop r29 \n\t" \ 01923 "pop r28 \n\t" 01924 #else 01925 #define RestoreGroup7 "" 01926 #endif 01927 01928 /* Restore registers r24r25r26r27 if they are used, in any of the tasks. */ 01929 #if ((defRegisterUseCollect & r24r25r26r27) == r24r25r26r27) 01930 #define RestoreGroup6 \ 01931 "pop r27 \n\t" \ 01932 "pop r26 \n\t" \ 01933 "pop r25 \n\t" \ 01934 "pop r24 \n\t" 01935 #else 01936 #define RestoreGroup6 "" 01937 #endif 01938 01939 /* Restore registers r20r21r22r23 if they are used, in any of the tasks. */ 01940 #if ((defRegisterUseCollect & r20r21r22r23) == r20r21r22r23) 01941 #define RestoreGroup5 \ 01942 "pop r23 \n\t" \ 01943 "pop r22 \n\t" \ 01944 "pop r21 \n\t" \ 01945 "pop r20 \n\t" 01946 #else 01947 #define RestoreGroup5 "" 01948 #endif 01949 01950 /* Restore registers r16r17r18r19 if they are used, in any of the tasks. */ 01951 #if ((defRegisterUseCollect & r16r17r18r19) == r16r17r18r19) 01952 #define RestoreGroup4 \ 01953 "pop r19 \n\t" \ 01954 "pop r18 \n\t" \ 01955 "pop r17 \n\t" \ 01956 "pop r16 \n\t" 01957 #else 01958 #define RestoreGroup4 "" 01959 #endif 01960 01961 /* Restore registers r12r13r14r15 if they are used, in any of the tasks. */ 01962 #if ((defRegisterUseCollect & r12r13r14r15) == r12r13r14r15) 01963 #define RestoreGroup3 \ 01964 "pop r15 \n\t" \ 01965 "pop r14 \n\t" \ 01966 "pop r13 \n\t" \ 01967 "pop r12 \n\t" 01968 #else 01969 #define RestoreGroup3 "" 01970 #endif 01971 01972 /* Restore registers r08r09r10r11 if they are used, in any of the tasks. */ 01973 #if ((defRegisterUseCollect & r08r09r10r11) == r08r09r10r11) 01974 #define RestoreGroup2 \ 01975 "pop r11 \n\t" \ 01976 "pop r10 \n\t" \ 01977 "pop r9 \n\t" \ 01978 "pop r8 \n\t" 01979 #else 01980 #define RestoreGroup2 "" 01981 #endif 01982 01983 /* Restore registers r04r05r06r07 if they are used, in any of the tasks. */ 01984 #if ((defRegisterUseCollect & r04r05r06r07) == r04r05r06r07) 01985 #define RestoreGroup1 \ 01986 "pop r7 \n\t" \ 01987 "pop r6 \n\t" \ 01988 "pop r5 \n\t" \ 01989 "pop r4 \n\t" 01990 #else 01991 #define RestoreGroup1 "" 01992 #endif 01993 01994 /* Restore registers r00r01r02r03 if they are used, in any of the tasks. */ 01995 #if ((defRegisterUseCollect & r00r01r02r03) == r00r01r02r03) 01996 #define RestoreGroup0 \ 01997 "pop r3 \n\t" \ 01998 "pop r2 \n\t" \ 01999 "pop r1 \n\t" \ 02000 "pop r0 \n\t" 02001 #else 02002 #define RestoreGroup0 "" 02003 #endif 02004 02005 /* Test if the registergroup r28r29r30r31 must be restored for the current task */ 02006 #if ((defRegisterUseVariable & r28r29r30r31) == r28r29r30r31) 02007 #define TestRestoreGroup7 \ 02008 "sbrs r0, 7 \n\t" \ 02009 "rjmp 7f \n\t" 02010 #else 02011 #define TestRestoreGroup7 "" 02012 #endif 02013 02014 /* Test if the registergroup r24r25r26r27 must be restored for the current task */ 02015 #if ((defRegisterUseVariable & r24r25r26r27) == r24r25r26r27) 02016 #define TestRestoreGroup6 \ 02017 "sbrs r0, 6 \n\t" \ 02018 "rjmp 6f \n\t" 02019 #else 02020 #define TestRestoreGroup6 "" 02021 #endif 02022 02023 /* Test if the registergroup r20r21r22r23 must be restored for the current task */ 02024 #if ((defRegisterUseVariable & r20r21r22r23) == r20r21r22r23) 02025 #define TestRestoreGroup5 \ 02026 "sbrs r0, 5 \n\t" \ 02027 "rjmp 5f \n\t" 02028 #else 02029 #define TestRestoreGroup5 "" 02030 #endif 02031 02032 /* Test if the registergroup r16r17r18r19 must be restored for the current task */ 02033 #if ((defRegisterUseVariable & r16r17r18r19) == r16r17r18r19) 02034 #define TestRestoreGroup4 \ 02035 "sbrs r0, 4 \n\t" \ 02036 "rjmp 4f \n\t" 02037 #else 02038 #define TestRestoreGroup4 "" 02039 #endif 02040 02041 /* Test if the registergroup r12r13r14r15 must be restored for the current task */ 02042 #if ((defRegisterUseVariable & r12r13r14r15) == r12r13r14r15) 02043 #define TestRestoreGroup3 \ 02044 "sbrs r0, 3 \n\t" \ 02045 "rjmp 3f \n\t" 02046 #else 02047 #define TestRestoreGroup3 "" 02048 #endif 02049 02050 /* Test if the registergroup r08r09r10r11 must be restored for the current task */ 02051 #if ((defRegisterUseVariable & r08r09r10r11) == r08r09r10r11) 02052 #define TestRestoreGroup2 \ 02053 "sbrs r0, 2 \n\t" \ 02054 "rjmp 2f \n\t" 02055 #else 02056 #define TestRestoreGroup2 "" 02057 #endif 02058 02059 /* Test if the registergroup r04r05r06r07 must be restored for the current task */ 02060 #if ((defRegisterUseVariable & r04r05r06r07) == r04r05r06r07) 02061 #define TestRestoreGroup1 \ 02062 "sbrs r0, 1 \n\t" \ 02063 "rjmp 1f \n\t" 02064 #else 02065 #define TestRestoreGroup1 "" 02066 #endif 02067 02068 /* Test if the registergroup r00r01r02r03 must be restored for the current task */ 02069 #if ((defRegisterUseVariable & r00r01r02r03) == r00r01r02r03) 02070 #define TestRestoreGroup0 \ 02071 "sbrs r0, 0 \n\t" \ 02072 "rjmp 0f \n\t" 02073 #define SkipFillGroup0 \ 02074 "rjmp 10f \n\t" 02075 #else 02076 #define TestRestoreGroup0 "" 02077 #define SkipFillGroup0 "" 02078 #endif 02079 02080 /* Test if the registergroup r00r01r02r03 must be filled for the current task */ 02081 #if ((defRegisterUseVariable & r00r01r02r03) == r00r01r02r03) 02082 #define TestFillGroup0 \ 02083 "sbrc r0, 0 \n\t" \ 02084 "rjmp 0f \n\t" 02085 #define SkipRestoreGroup0 \ 02086 "rjmp 10f \n\t" 02087 #else 02088 #define TestFillGroup0 "" 02089 #define SkipRestoreGroup0 "" 02090 #endif 02091 02092 /* Fill registergroup r28r29r30r31 with the check byte */ 02093 #if ((defRegisterCheckCollect & r28r29r30r31) == r28r29r30r31) 02094 #define FillGroup7 \ 02095 "mov r30,r2 \n\t" \ 02096 "mov r29,r2 \n\t" \ 02097 "mov r28,r2 \n\t" 02098 #else 02099 #define FillGroup7 "" 02100 #endif 02101 02102 /* Fill registergroup r24r25r26r27 with the check byte */ 02103 #if ((defRegisterCheckCollect & r24r25r26r27) == r24r25r26r27) 02104 #define FillGroup6 \ 02105 "mov r27,r2 \n\t" \ 02106 "mov r26,r2 \n\t" \ 02107 "mov r25,r2 \n\t" \ 02108 "mov r24,r2 \n\t" 02109 #else 02110 #define FillGroup6 "" 02111 #endif 02112 02113 /* Fill registergroup r20r21r22r23 with the check byte */ 02114 #if ((defRegisterCheckCollect & r20r21r22r23) == r20r21r22r23) 02115 #define FillGroup5 \ 02116 "mov r23,r2 \n\t" \ 02117 "mov r22,r2 \n\t" \ 02118 "mov r21,r2 \n\t" \ 02119 "mov r20,r2 \n\t" 02120 #else 02121 #define FillGroup5 "" 02122 #endif 02123 02124 /* Fill registergroup r16r17r18r19 with the check byte */ 02125 #if ((defRegisterCheckCollect & r16r17r18r19) == r16r17r18r19) 02126 #define FillGroup4 \ 02127 "mov r19,r2 \n\t" \ 02128 "mov r18,r2 \n\t" \ 02129 "mov r17,r2 \n\t" \ 02130 "mov r16,r2 \n\t" 02131 #else 02132 #define FillGroup4 "" 02133 #endif 02134 02135 /* Fill registergroup r12r13r14r15 with the check byte */ 02136 #if ((defRegisterCheckCollect & r12r13r14r15) == r12r13r14r15) 02137 #define FillGroup3 \ 02138 "mov r15,r2 \n\t" \ 02139 "mov r14,r2 \n\t" \ 02140 "mov r13,r2 \n\t" \ 02141 "mov r12,r2 \n\t" 02142 #else 02143 #define FillGroup3 "" 02144 #endif 02145 02146 /* Fill registergroup r20r21r22r23 with the check byte */ 02147 #if ((defRegisterCheckCollect & r08r09r10r11) == r08r09r10r11) 02148 #define FillGroup2 \ 02149 "mov r11,r2 \n\t" \ 02150 "mov r10,r2 \n\t" \ 02151 "mov r9,r2 \n\t" \ 02152 "mov r8,r2 \n\t" 02153 #else 02154 #define FillGroup2 "" 02155 #endif 02156 02157 /* Fill registergroup r04r05r06r07 with the check byte */ 02158 #if ((defRegisterCheckCollect & r04r05r06r07) == r04r05r06r07) 02159 #define FillGroup1 \ 02160 "mov r7,r2 \n\t" \ 02161 "mov r6,r2 \n\t" \ 02162 "mov r5,r2 \n\t" \ 02163 "mov r4,r2 \n\t" 02164 #else 02165 #define FillGroup1 "" 02166 #endif 02167 02168 /* Fill registergroup r00r01r02r03 with the check byte */ 02169 #if ((defRegisterCheckCollect & r00r01r02r03) == r00r01r02r03) 02170 #define FillGroup0 \ 02171 "mov r3,r2 \n\t" \ 02172 FillRegisterR1 \ 02173 "mov r0,r2 \n\t" 02174 #else 02175 #define FillGroup0 "" 02176 #endif 02177 02178 02179 void portRestoreContext(void) 02180 { 02181 02182 #if (cfgCheckRegisters == cfgTrue) || (cfgCheckTaskStack == cfgTrue) || (cfgCheckWatermarks == cfgTrue) 02183 02184 /* This is the code for protected context restoring. */ 02185 asm volatile ( 02186 ClearRegisterR1 /* GCC generated code expects r1 to be zero, clear it if the option cfgSysClearUnusedR1 is set */ 02187 LoadTIMSK(r30) /* We must restore the status register as well as set the tick interrupt correctly. Load the */ 02188 "pop r31 \n\t" /* latter into r30 and the first into r31 for manipulation. Then sort out if we squeeze or not. */ 02189 #if (cfgSysSqueezeState == cfgTrue) /* If we have a squeezed state, the global interrupt was stored at the H bit location. Done */ 02190 #if (cfgIntTickTrack == cfgTrue)/* If we keep track of tick interrupts, the information was stored at the I bit location. */ 02191 "bst r31, %[_I_] \n\t" /* Copy the value of the I bit location to the the place where the timer interrupt is situated */ 02192 "bld r30, %[_OCIE0A_] \n\t" /* by moving it through the T bit (shortest method). */ 02193 "andi r31,%[_nIb_] \n\t" /* Don't forget to clear its location, we do not want to activate global interrupts. */ 02194 #else /* If we do not track tick interrupts the tick interrupt is not saved, restore global only */ 02195 "sbr r30,%[_OCIE0Ab_] \n\t" /* If we do not keep track of tick interrupts we must set the timer interrupt per default */ 02196 #endif 02197 #else /* If we not squeeze, the information is packed differently, (see save context also). */ 02198 #if (cfgIntTickTrack == cfgTrue) /* If we keep track of the tick interrupt state, we must sort out if we were in the preemptive */ 02199 "sbr r30,%[_OCIE0Ab_] \n\t" /* mode or not. If so we need to activate both global and tick interrupts, do so here by default */ 02200 "sbi %[_ASR_],%[_ARB_] \n\t" /* by setting the bit locations on their registers. */ 02201 "sbrc r31,%[_I_] \n\t" /* Now look if the preemptive mode was set (I bit location set) */ 02202 "rjmp 52f \n\t" /* If so, we are almost done skip the following */ 02203 "sbrs r31,%[_T_] \n\t" /* If not we must reset the tick interrupt if the T-flag was not set, test if it was set */ 02204 "andi r30,%[_nOCIE0Ab_] \n\t" /* if not, reset that bit otherwise just skip, since it was set already. */ 02205 "sbrs r31,%[_H_] \n\t" /* Also test the H-flag indicating the new state of the global interrupt, test if it was set */ 02206 "cbi %[_ASR_],%[_ARB_] \n\t" /* if not, reset that bit otherwise just skip, since it was set already. */ 02207 "52: \n\t" /* Arrive here when place holders of tick and global interrupt are setup correctly */ 02208 "andi r31,%[_nIb_] \n\t" /* Make sure we do not activate the global interrupt just yet. */ 02209 #else /* If we do not track tick interrupts the tick interrupt is not saved, restore global only */ 02210 "cbi %[_ASR_],%[_ARB_] \n\t" /* We want to temporarily save the status of the global interrupt in the devAuxSysRegBit, clear */ 02211 "sbrc r31,%[_I_] \n\t" /* that bit first, then check the value of the position of the I bit, if it is set, then */ 02212 "sbi %[_ASR_],%[_ARB_] \n\t" /* set the devAuxSysRegBit, if not we can leave it cleared. */ 02213 "andi r31,%[_nIb_] \n\t" /* Make sure the I bit is cleared. (we do not want to activate global interrupts here). */ 02214 "sbr r30,%[_OCIE0Ab_] \n\t" /* If we do not keep track of tick interrupts we must set the timer interrupt per default */ 02215 #endif 02216 #endif 02217 "out __SREG__, r31 \n\t" /* Restore the status register. */ 02218 StoreTIMSK(r30) /* Restore the TIMSK register holding the tick interrupt */ 02219 "lds r0,%[_RegUse_] \n\t" /* Load the parameter describing which register are used, in order to restore them. */ 02220 "ldi r31,%[_RegByt_] \n\t" /* Load the byte we use to fill unused register in under to check if they are misused. */ 02221 "mov r2,r31 \n\t" /* Since we need to restore higher number register first, copy that value to r2 */ 02222 FillGroup7 /* Fill the register group with checkbytes (if used) */ 02223 TestRestoreGroup7 /* Check if the register group must be restored (if used) */ 02224 RestoreGroup7 /* Restore the register group (if used) */ 02225 "7: \n\t" /* */ 02226 FillGroup6 /* Same handling as group 7 */ 02227 TestRestoreGroup6 /* */ 02228 RestoreGroup6 /* */ 02229 "6: \n\t" /* */ 02230 ReturnState(%[_RetPar_]) /* Now r24 is restored we can fill it with a return value when needed, plenty of registers free */ 02231 FillGroup5 /* Same handling as group 7 */ 02232 TestRestoreGroup5 /* */ 02233 RestoreGroup5 /* */ 02234 "5: \n\t" /* */ 02235 FillGroup4 /* Same handling as group 7 */ 02236 TestRestoreGroup4 /* */ 02237 RestoreGroup4 /* */ 02238 "4: \n\t" 02239 FillGroup3 /* Same handling as group 7 */ 02240 TestRestoreGroup3 /* */ 02241 RestoreGroup3 /* */ 02242 "3: \n\t" /* */ 02243 FillGroup2 /* Same handling as group 7 */ 02244 TestRestoreGroup2 /* */ 02245 RestoreGroup2 /* */ 02246 "2: \n\t" /* */ 02247 FillGroup1 /* Same handling as group 7 */ 02248 TestRestoreGroup1 /* */ 02249 RestoreGroup1 /* */ 02250 "1: \n\t" /* */ 02251 TestFillGroup0 /* Since we must test if we need to restore (and this uses register r0, and at the same time fill */ 02252 FillGroup0 /* r0, we have a problem. Since we cannot fill and overwrite, thus we must skip, if the Test was */ 02253 SkipRestoreGroup0 /* was present. Otherwise there is nothing to skip. */ 02254 "0: \n\t" /* */ 02255 RestoreGroup0 /* */ 02256 "10: \n\t" /* */ 02257 #if (cfgSysSqueezeState == cfgFalse) 02258 "sbis %[_ASR_],%[_ARB_] \n\t" /* devAuxSysReg contains a copy of the status register, test for the global interrupt bit */ 02259 "ret \n\t" /* if it is not set return normally (interrupt keeps deactivated) otherwise reti */ 02260 "cbi %[_ASR_],%[_ARB_] \n\t" /* bit ARB of must be zero before a portSaveContext occurs. */ 02261 "reti \n\t" /* */ 02262 #else 02263 "brhs 13f \n\t" /* The H bit contains the interrupt state, so if it is not set */ 02264 "ret \n\t" /* return without interrupts activated, and otherwise */ 02265 "13: \n\t" /* */ 02266 "reti \n\t" /* set the interrupts while returning. */ 02267 #endif 02268 "" :: 02269 [_RegUse_] "i" (&xOS.pxSave.uiRegisterUse), 02270 [_RegByt_] "i" (cfgSysRegisterCheckByte), 02271 [_RetPar_] "i" (ReturnField), 02272 [_I_] "i" (SREG_I), 02273 [_H_] "i" (SREG_H), 02274 [_T_] "i" (SREG_T), 02275 [_Ib_] "i" (preBitSet1(0x00,SREG_I)), 02276 [_Tb_] "i" (preBitSet1(0x00,SREG_T)), 02277 [_nITb_] "i" (preBitClr2(0xFF,SREG_I,SREG_T)), 02278 [_nIb_] "i" (preBitClr1(0xFF,SREG_I)), 02279 [_RET0_] "i" (defRet0), 02280 [_RET1_] "i" (defRet1), 02281 [_STKBIT_] "i" (defCheckStackBit), 02282 [_STKBITb_] "i" (preBitSet1(0x00,defCheckStackBit)), 02283 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 02284 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 02285 [_OCIE0A_] "i" (devOCIE), 02286 [_OCIE0Ab_] "i" (preBitSet1(0x00,devOCIE)), 02287 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)), 02288 [_ARB_] "i" (devAuxSysRegBit), 02289 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg))); 02290 02291 #else 02292 02293 /* This is the code standard context restoring. */ 02294 asm volatile ( 02295 ClearRegisterR1 /* GCC generated code expects r1 to be zero, clear it if the option cfgSysClearUnusedR1 is set */ 02296 LoadTIMSK(r30) /* We must restore the status register as well as set the tick interrupt correctly. Load the */ 02297 "pop r31 \n\t" /* latter into r30 and the first into r31 for manipulation. Then sort out if we squeeze or not. */ 02298 #if (cfgSysSqueezeState == cfgTrue) /* If we have a squeezed state, the global interrupt was stored at the H bit location. Done */ 02299 #if (cfgIntTickTrack == cfgTrue)/* If we keep track of tick interrupts, the information was stored at the I bit location. */ 02300 "bst r31, %[_I_] \n\t" /* Copy the value of the I bit location to the the place where the timer interrupt is situated */ 02301 "bld r30, %[_OCIE0A_] \n\t" /* by moving it through the T bit (shortest method). */ 02302 "andi r31,%[_nIb_] \n\t" /* Don't forget to clear its location, we do not want to activate global interrupts. */ 02303 #else /* If we do not track tick interrupts the tick interrupt is not saved, restore global only */ 02304 "sbr r30,%[_OCIE0Ab_] \n\t" /* If we do not keep track of tick interrupts we must set the timer interrupt per default */ 02305 #endif 02306 #else /* If we not squeeze, the information is packed differently, (see save context also). */ 02307 #if (cfgIntTickTrack == cfgTrue) /* If we keep track of the tick interrupt state, we must sort out if we were in the preemptive */ 02308 "sbr r30,%[_OCIE0Ab_] \n\t" /* mode or not. If so we need to activate both global and tick interrupts, do so here by default */ 02309 "sbi %[_ASR_],%[_ARB_] \n\t" /* by setting the bit locations on their registers. */ 02310 "sbrc r31,%[_I_] \n\t" /* Now look if the preemptive mode was set (I bit location set) */ 02311 "rjmp 52f \n\t" /* If so, we are almost done skip the following */ 02312 "sbrs r31,%[_T_] \n\t" /* If not we must reset the tick interrupt if the T-flag was not set, test if it was set */ 02313 "andi r30,%[_nOCIE0Ab_] \n\t" /* if not, reset that bit otherwise just skip, since it was set already. */ 02314 "sbrs r31,%[_H_] \n\t" /* Also test the H-flag indicating the new state of the global interrupt, test if it was set */ 02315 "cbi %[_ASR_],%[_ARB_] \n\t" /* if not, reset that bit otherwise just skip, since it was set already. */ 02316 "52: \n\t" /* Arrive here when place holders of tick and global interrupt are setup correctly */ 02317 "andi r31,%[_nIb_] \n\t" /* Make sure we do not activate the global interrupt just yet. */ 02318 #else /* If we do not track tick interrupts the tick interrupt is not saved, restore global only */ 02319 "cbi %[_ASR_],%[_ARB_] \n\t" /* We want to temporarily save the status of the global interrupt in the devAuxSysRegBit, clear */ 02320 "sbrc r31,%[_I_] \n\t" /* that bit first, then check the value of the position of the I bit, if it is set, then */ 02321 "sbi %[_ASR_],%[_ARB_] \n\t" /* set the devAuxSysRegBit, if not we can leave it cleared. */ 02322 "andi r31,%[_nIb_] \n\t" /* Make sure the I bit is cleared. (we do not want to activate global interrupts here). */ 02323 "sbr r30,%[_OCIE0Ab_] \n\t" /* If we do not keep track of tick interrupts we must set the timer interrupt per default */ 02324 #endif 02325 #endif 02326 "out __SREG__, r31 \n\t" /* Restore the status register. */ 02327 StoreTIMSK(r30) /* Restore the TIMSK register holding the tick interrupt */ 02328 "lds r0,%[_RegUse_] \n\t" /* Load the parameter describing which register are used, in order to restore them. */ 02329 TestRestoreGroup7 /* Check if the registergroup must be restored (if used) */ 02330 RestoreGroup7 /* Restore the registergroup (if used) */ 02331 "7: \n\t" /* */ 02332 TestRestoreGroup6 /* Same handling as group 7 */ 02333 RestoreGroup6 /* */ 02334 "6: \n\t" /* */ 02335 ReturnState(%[_RetPar_]) /* Now r24 is restored we can fill it with a return value when needed, plenty of registers free */ 02336 TestRestoreGroup5 /* Same handling as group 7 */ 02337 RestoreGroup5 /* */ 02338 "5: \n\t" /* */ 02339 TestRestoreGroup4 /* Same handling as group 7 */ 02340 RestoreGroup4 /* */ 02341 "4: \n\t" /* */ 02342 TestRestoreGroup3 /* Same handling as group 7 */ 02343 RestoreGroup3 /* */ 02344 "3: \n\t" /* */ 02345 TestRestoreGroup2 /* Same handling as group 7 */ 02346 RestoreGroup2 /* */ 02347 "2: \n\t" /* */ 02348 TestRestoreGroup1 /* Same handling as group 7 */ 02349 RestoreGroup1 /* */ 02350 "1: \n\t" /* */ 02351 TestRestoreGroup0 /* Same handling as group 7 */ 02352 RestoreGroup0 /* */ 02353 "0: \n\t" /* */ 02354 #if (cfgSysSqueezeState == cfgFalse) 02355 "sbis %[_ASR_],%[_ARB_] \n\t" /* devAuxSysReg contains a copy of the status register, test for the global interrupt bit */ 02356 "ret \n\t" /* if it is not set return normally (interrupt keeps deactivated) otherwise reti */ 02357 "cbi %[_ASR_],%[_ARB_] \n\t" /* bit ARB of must be zero before a portSaveContext occurs. */ 02358 "reti \n\t" /* */ 02359 #else 02360 "brhs 13f \n\t" /* The H bit contains the interrupt state, so if it is not set */ 02361 "ret \n\t" /* return without interrupts activated, and otherwise */ 02362 "13: \n\t" /* */ 02363 "reti \n\t" /* set the interrupts while returning. */ 02364 #endif 02365 "" :: 02366 [_RegUse_] "i" (&xOS.pxSave.uiRegisterUse), 02367 [_RetPar_] "i" (ReturnField), 02368 [_I_] "i" (SREG_I), 02369 [_H_] "i" (SREG_H), 02370 [_T_] "i" (SREG_T), 02371 [_Ib_] "i" (preBitSet1(0x00,SREG_I)), 02372 [_Tb_] "i" (preBitSet1(0x00,SREG_T)), 02373 [_nITb_] "i" (preBitClr2(0xFF,SREG_I,SREG_T)), 02374 [_nIb_] "i" (preBitClr1(0xFF,SREG_I)), 02375 [_RET0_] "i" (defRet0), 02376 [_RET1_] "i" (defRet1), 02377 [_STKBIT_] "i" (defCheckStackBit), 02378 [_STKBITb_] "i" (preBitSet1(0x00,defCheckStackBit)), 02379 [_TIMSKio_] "i" (_SFR_IO_ADDR(devTIMSK)), 02380 [_TIMSKmem_] "i" (_SFR_MEM_ADDR(devTIMSK)), 02381 [_OCIE0A_] "i" (devOCIE), 02382 [_OCIE0Ab_] "i" (preBitSet1(0x00,devOCIE)), 02383 [_nOCIE0Ab_] "i" (preBitClr1(0xFF,devOCIE)), 02384 [_ARB_] "i" (devAuxSysRegBit), 02385 [_ASR_] "i" (_SFR_IO_ADDR(devAuxSysReg))); 02386 02387 #endif 02388 }