/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /****************************************************************************** * >>>>>> NOTE 1: <<<<<< * * main() can be configured to create either a very simple LED flasher demo, or * a more comprehensive test/demo application. * * To create a very simple LED flasher example, set the * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY constant (defined below) to 1. When * this is done, only the standard demo flash tasks are created. The standard * demo flash example creates three tasks, each of which toggle an LED at a * fixed but different frequency. * * To create a more comprehensive test and demo application, set * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY to 0. * * >>>>>> NOTE 2: <<<<<< * * In addition to the normal set of standard demo tasks, the comprehensive test * makes heavy use of the floating point unit, and forces floating point * instructions to be used from interrupts that nest three deep. The nesting * starts from the tick hook function, resulting is an abnormally long context * switch time. This is done purely to stress test the FPU context switching * implementation, and that part of the test can be removed by setting * configUSE_TICK_HOOK to 0 in FreeRTOSConfig.h. ****************************************************************************** * * main() creates all the demo application tasks and software timers, then starts * the scheduler. The web documentation provides more details of the standard * demo application tasks, which provide no particular functionality, but do * provide a good example of how to use the FreeRTOS API. * * In addition to the standard demo tasks, the following tasks and tests are * defined and/or created within this file: * * "Reg test" tasks - These fill both the core and floating point registers with * known values, then check that each register maintains its expected value for * the lifetime of the task. Each task uses a different set of values. The reg * test tasks execute with a very low priority, so get preempted very * frequently. A register containing an unexpected value is indicative of an * error in the context switching mechanism. * * "Check" timer - The check software timer period is initially set to three * seconds. The callback function associated with the check software timer * checks that all the standard demo tasks, and the register check tasks, are * not only still executing, but are executing without reporting any errors. If * the check software timer discovers that a task has either stalled, or * reported an error, then it changes its own execution period from the initial * three seconds, to just 200ms. The check software timer callback function * also toggles an LED each time it is called. This provides a visual * indication of the system status: If the LED toggles every three seconds, * then no issues have been discovered. If the LED toggles every 200ms, then * an issue has been discovered with at least one task. * * Tick hook - The application tick hook is called from the schedulers tick * interrupt service routine when configUSE_TICK_HOOK is set to 1 in * FreeRTOSConfig.h. In this example, the tick hook is used to test the kernels * handling of the floating point units (FPU) context, both at the task level * and when nesting interrupts access the floating point unit registers. The * tick hook function first fills the FPU registers with a known value, it * then triggers a medium priority interrupt. The medium priority interrupt * fills the FPU registers with a different value, and triggers a high priority * interrupt. The high priority interrupt once again fills the the FPU * registers with a known value before returning to the medium priority * interrupt. The medium priority interrupt checks that the FPU registers * contain the values that it wrote to them, then returns to the tick hook * function. Finally, the tick hook function checks that the FPU registers * contain the values that it wrote to them, before it too returns. * * Button interrupt - The button marked "USER" on the starter kit is used to * demonstrate how to write an interrupt service routine, and how to synchronise * a task with an interrupt. A task is created that blocks on a test semaphore. * When the USER button is pressed, the button interrupt handler gives the * semaphore, causing the task to unblock. When the task unblocks, it simply * increments an execution count variable, then returns to block on the * semaphore again. */ /* Kernel includes. */ #include "FreeRTOS.h" #include "task.h" #include "timers.h" #include "semphr.h" /* Demo application includes. */ #include "partest.h" #include "flash.h" #include "flop.h" #include "integer.h" #include "PollQ.h" #include "semtest.h" #include "dynamic.h" #include "BlockQ.h" #include "blocktim.h" #include "countsem.h" #include "GenQTest.h" #include "recmutex.h" #include "death.h" /* Hardware and starter kit includes. */ #include "arm_comm.h" #include "iar_stm32f407zg_sk.h" #include "stm32f4xx.h" #include "stm32f4xx_conf.h" /* Priorities for the demo application tasks. */ #define mainFLASH_TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL ) #define mainQUEUE_POLL_PRIORITY ( tskIDLE_PRIORITY + 2UL ) #define mainSEM_TEST_PRIORITY ( tskIDLE_PRIORITY + 1UL ) #define mainBLOCK_Q_PRIORITY ( tskIDLE_PRIORITY + 2UL ) #define mainCREATOR_TASK_PRIORITY ( tskIDLE_PRIORITY + 3UL ) #define mainFLOP_TASK_PRIORITY ( tskIDLE_PRIORITY ) /* The LED used by the check timer. */ #define mainCHECK_LED ( 3UL ) /* A block time of zero simply means "don't block". */ #define mainDONT_BLOCK ( 0UL ) /* The period after which the check timer will expire, in ms, provided no errors * have been reported by any of the standard demo tasks. ms are converted to the * equivalent in ticks using the portTICK_PERIOD_MS constant. */ #define mainCHECK_TIMER_PERIOD_MS ( 3000UL / portTICK_PERIOD_MS ) /* The period at which the check timer will expire, in ms, if an error has been * reported in one of the standard demo tasks. ms are converted to the equivalent * in ticks using the portTICK_PERIOD_MS constant. */ #define mainERROR_CHECK_TIMER_PERIOD_MS ( 200UL / portTICK_PERIOD_MS ) /* Set mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY to 1 to create a simple demo. * Set mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY to 0 to create a much more * comprehensive test application. See the comments at the top of this file, and * the documentation page on the http://www.FreeRTOS.org web site for more * information. */ #define mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY 0 /*-----------------------------------------------------------*/ /* * Set up the hardware ready to run this demo. */ static void prvSetupHardware( void ); /* * The check timer callback function, as described at the top of this file. */ static void prvCheckTimerCallback( TimerHandle_t xTimer ); /* * Configure the interrupts used to test the interrupt nesting depth as * described at the top of this file. */ static void prvSetupNestedFPUInterruptsTest( void ); /* * Register check tasks, and the tasks used to write over and check the contents * of the FPU registers, as described at the top of this file. The nature of * these files necessitates that they are written in an assembly file. */ extern void vRegTest1Task( void * pvParameters ); extern void vRegTest2Task( void * pvParameters ); extern void vRegTestClearFlopRegistersToParameterValue( unsigned long ulValue ); extern unsigned long ulRegTestCheckFlopRegistersContainParameterValue( unsigned long ulValue ); /* * The task that is synchronised with the button interrupt. This is done just * to demonstrate how to write interrupt service routines, and how to * synchronise a task with an interrupt. */ static void prvButtonTestTask( void * pvParameters ); /* * This file can be used to create either a simple LED flasher example, or a * comprehensive test/demo application - depending on the setting of the * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY constant defined above. If * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to 1, then the following * function will create a lot of additional tasks and a software timer. If * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to 0, then the following * function will do nothing. */ static void prvOptionallyCreateComprehensveTestApplication( void ); /*-----------------------------------------------------------*/ /* The following two variables are used to communicate the status of the * register check tasks to the check software timer. If the variables keep * incrementing, then the register check tasks have not discovered any errors. If * a variable stops incrementing, then an error has been found. */ volatile unsigned long ulRegTest1LoopCounter = 0UL, ulRegTest2LoopCounter = 0UL; /* The following variables are used to verify that the interrupt nesting depth * is as intended. ulFPUInterruptNesting is incremented on entry to an interrupt * that uses the FPU, and decremented on exit of the same interrupt. * ulMaxFPUInterruptNesting latches the highest value reached by * ulFPUInterruptNesting. These variables have no other purpose. */ volatile unsigned long ulFPUInterruptNesting = 0UL, ulMaxFPUInterruptNesting = 0UL; /* The semaphore used to demonstrate a task being synchronised with an * interrupt. */ static SemaphoreHandle_t xTestSemaphore = NULL; /* The variable that is incremented by the task synchronised with the button * interrupt. */ volatile unsigned long ulButtonPressCounts = 0UL; /*-----------------------------------------------------------*/ int main( void ) { /* Configure the hardware ready to run the test. */ prvSetupHardware(); /* Start standard demo/test application flash tasks. See the comments at * the top of this file. The LED flash tasks are always created. The other * tasks are only created if mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to * 0 (at the top of this file). See the comments at the top of this file for * more information. */ vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY ); /* The following function will only create more tasks and timers if * mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY is set to 0 (at the top of this * file). See the comments at the top of this file for more information. */ prvOptionallyCreateComprehensveTestApplication(); /* Start the scheduler. */ vTaskStartScheduler(); /* If all is well, the scheduler will now be running, and the following line * will never be reached. If the following line does execute, then there was * insufficient FreeRTOS heap memory available for the idle and/or timer tasks * to be created. See the memory management section on the FreeRTOS web site * for more details. */ for( ; ; ) { } } /*-----------------------------------------------------------*/ static void prvCheckTimerCallback( TimerHandle_t xTimer ) { static long lChangedTimerPeriodAlready = pdFALSE; static unsigned long ulLastRegTest1Value = 0, ulLastRegTest2Value = 0; long lErrorFound = pdFALSE; /* Check all the demo tasks (other than the flash tasks) to ensure * that they are all still running, and that none have detected an error. */ if( xAreMathsTaskStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreIntegerMathsTaskStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreDynamicPriorityTasksStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreBlockingQueuesStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreBlockTimeTestTasksStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreGenericQueueTasksStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreRecursiveMutexTasksStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xIsCreateTaskStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xArePollingQueuesStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } if( xAreSemaphoreTasksStillRunning() != pdTRUE ) { lErrorFound = pdTRUE; } /* Check that the register test 1 task is still running. */ if( ulLastRegTest1Value == ulRegTest1LoopCounter ) { lErrorFound = pdTRUE; } ulLastRegTest1Value = ulRegTest1LoopCounter; /* Check that the register test 2 task is still running. */ if( ulLastRegTest2Value == ulRegTest2LoopCounter ) { lErrorFound = pdTRUE; } ulLastRegTest2Value = ulRegTest2LoopCounter; /* Toggle the check LED to give an indication of the system status. If * the LED toggles every mainCHECK_TIMER_PERIOD_MS milliseconds then * everything is ok. A faster toggle indicates an error. */ vParTestToggleLED( mainCHECK_LED ); /* Have any errors been latch in lErrorFound? If so, shorten the * period of the check timer to mainERROR_CHECK_TIMER_PERIOD_MS milliseconds. * This will result in an increase in the rate at which mainCHECK_LED * toggles. */ if( lErrorFound != pdFALSE ) { if( lChangedTimerPeriodAlready == pdFALSE ) { lChangedTimerPeriodAlready = pdTRUE; /* This call to xTimerChangePeriod() uses a zero block time. * Functions called from inside of a timer callback function must * never* attempt to block. */ xTimerChangePeriod( xTimer, ( mainERROR_CHECK_TIMER_PERIOD_MS ), mainDONT_BLOCK ); } } } /*-----------------------------------------------------------*/ static void prvButtonTestTask( void * pvParameters ) { configASSERT( xTestSemaphore ); /* This is the task used as an example of how to synchronise a task with * an interrupt. Each time the button interrupt gives the semaphore, this task * will unblock, increment its execution counter, then return to block * again. */ /* Take the semaphore before started to ensure it is in the correct * state. */ xSemaphoreTake( xTestSemaphore, mainDONT_BLOCK ); for( ; ; ) { xSemaphoreTake( xTestSemaphore, portMAX_DELAY ); ulButtonPressCounts++; } } /*-----------------------------------------------------------*/ static void prvSetupHardware( void ) { /* Setup STM32 system (clock, PLL and Flash configuration) */ SystemInit(); /* Ensure all priority bits are assigned as preemption priority bits. */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* Setup the LED outputs. */ vParTestInitialise(); /* Configure the button input. This configures the interrupt to use the * lowest interrupt priority, so it is ok to use the ISR safe FreeRTOS API * from the button interrupt handler. */ STM_EVAL_PBInit( BUTTON_USER, BUTTON_MODE_EXTI ); } /*-----------------------------------------------------------*/ void vApplicationTickHook( void ) { #if ( mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY == 0 ) { /* Just to verify that the interrupt nesting behaves as expected, * increment ulFPUInterruptNesting on entry, and decrement it on exit. */ ulFPUInterruptNesting++; /* Fill the FPU registers with 0. */ vRegTestClearFlopRegistersToParameterValue( 0UL ); /* Trigger a timer 2 interrupt, which will fill the registers with a * different value and itself trigger a timer 3 interrupt. Note that the * timers are not actually used. The timer 2 and 3 interrupt vectors are * just used for convenience. */ NVIC_SetPendingIRQ( TIM2_IRQn ); /* Ensure that, after returning from the nested interrupts, all the FPU * registers contain the value to which they were set by the tick hook * function. */ configASSERT( ulRegTestCheckFlopRegistersContainParameterValue( 0UL ) ); ulFPUInterruptNesting--; } #endif /* if ( mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY == 0 ) */ } /*-----------------------------------------------------------*/ static void prvSetupNestedFPUInterruptsTest( void ) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the TIM2 interrupt in the NVIC. The timer itself is not used, * just its interrupt vector to force nesting from software. TIM2 must have * a lower priority than TIM3, and both must have priorities above * configMAX_SYSCALL_INTERRUPT_PRIORITY. */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY - 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); /* Enable the TIM3 interrupt in the NVIC. The timer itself is not used, * just its interrupt vector to force nesting from software. TIM2 must have * a lower priority than TIM3, and both must have priorities above * configMAX_SYSCALL_INTERRUPT_PRIORITY. */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY - 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); } /*-----------------------------------------------------------*/ void TIM3_IRQHandler( void ) { /* Just to verify that the interrupt nesting behaves as expected, increment * ulFPUInterruptNesting on entry, and decrement it on exit. */ ulFPUInterruptNesting++; /* This is the highest priority interrupt in the chain of forced nesting * interrupts, so latch the maximum value reached by ulFPUInterruptNesting. * This is done purely to allow verification that the nesting depth reaches * that intended. */ if( ulFPUInterruptNesting > ulMaxFPUInterruptNesting ) { ulMaxFPUInterruptNesting = ulFPUInterruptNesting; } /* Fill the FPU registers with 99 to overwrite the values written by * TIM2_IRQHandler(). */ vRegTestClearFlopRegistersToParameterValue( 99UL ); ulFPUInterruptNesting--; } /*-----------------------------------------------------------*/ void TIM2_IRQHandler( void ) { /* Just to verify that the interrupt nesting behaves as expected, increment * ulFPUInterruptNesting on entry, and decrement it on exit. */ ulFPUInterruptNesting++; /* Fill the FPU registers with 1. */ vRegTestClearFlopRegistersToParameterValue( 1UL ); /* Trigger a timer 3 interrupt, which will fill the registers with a * different value. */ NVIC_SetPendingIRQ( TIM3_IRQn ); /* Ensure that, after returning from the nesting interrupt, all the FPU * registers contain the value to which they were set by this interrupt * function. */ configASSERT( ulRegTestCheckFlopRegistersContainParameterValue( 1UL ) ); ulFPUInterruptNesting--; } /*-----------------------------------------------------------*/ static void prvOptionallyCreateComprehensveTestApplication( void ) { #if ( mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY == 0 ) { TimerHandle_t xCheckTimer = NULL; /* Configure the interrupts used to test FPU registers being used from * nested interrupts. */ prvSetupNestedFPUInterruptsTest(); /* Start all the other standard demo/test tasks. */ vStartIntegerMathTasks( tskIDLE_PRIORITY ); vStartDynamicPriorityTasks(); vStartBlockingQueueTasks( mainBLOCK_Q_PRIORITY ); vCreateBlockTimeTasks(); vStartCountingSemaphoreTasks(); vStartGenericQueueTasks( tskIDLE_PRIORITY ); vStartRecursiveMutexTasks(); vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY ); vStartSemaphoreTasks( mainSEM_TEST_PRIORITY ); /* Most importantly, start the tasks that use the FPU. */ vStartMathTasks( mainFLOP_TASK_PRIORITY ); /* Create the register check tasks, as described at the top of this * file */ xTaskCreate( vRegTest1Task, "Reg1", configMINIMAL_STACK_SIZE, ( void * ) NULL, tskIDLE_PRIORITY, NULL ); xTaskCreate( vRegTest2Task, "Reg2", configMINIMAL_STACK_SIZE, ( void * ) NULL, tskIDLE_PRIORITY, NULL ); /* Create the semaphore that is used to demonstrate a task being * synchronised with an interrupt. */ vSemaphoreCreateBinary( xTestSemaphore ); /* Create the task that is unblocked by the demonstration interrupt. */ xTaskCreate( prvButtonTestTask, "BtnTest", configMINIMAL_STACK_SIZE, ( void * ) NULL, tskIDLE_PRIORITY, NULL ); /* Create the software timer that performs the 'check' functionality, * as described at the top of this file. */ xCheckTimer = xTimerCreate( "CheckTimer", /* A text name, purely to help debugging. */ ( mainCHECK_TIMER_PERIOD_MS ), /* The timer period, in this case 3000ms (3s). */ pdTRUE, /* This is an auto-reload timer, so xAutoReload is set to pdTRUE. */ ( void * ) 0, /* The ID is not used, so can be set to anything. */ prvCheckTimerCallback /* The callback function that inspects the status of all the other tasks. */ ); if( xCheckTimer != NULL ) { xTimerStart( xCheckTimer, mainDONT_BLOCK ); } /* This task has to be created last as it keeps account of the number of * tasks it expects to see running. */ vCreateSuicidalTasks( mainCREATOR_TASK_PRIORITY ); } #else /* mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY */ { /* Just to prevent compiler warnings when the configuration options are * set such that these static functions are not used. */ ( void ) vRegTest1Task; ( void ) vRegTest2Task; ( void ) prvCheckTimerCallback; ( void ) prvSetupNestedFPUInterruptsTest; } #endif /* mainCREATE_SIMPLE_LED_FLASHER_DEMO_ONLY */ } /*-----------------------------------------------------------*/ void EXTI9_5_IRQHandler( void ) { long lHigherPriorityTaskWoken = pdFALSE; /* Only line 6 is enabled, so there is no need to test which line generated * the interrupt. */ EXTI_ClearITPendingBit( EXTI_Line6 ); /* This interrupt does nothing more than demonstrate how to synchronise a * task with an interrupt. First the handler releases a semaphore. * lHigherPriorityTaskWoken has been initialised to zero. */ xSemaphoreGiveFromISR( xTestSemaphore, &lHigherPriorityTaskWoken ); /* If there was a task that was blocked on the semaphore, and giving the * semaphore caused the task to unblock, and the unblocked task has a priority * higher than the currently executing task (the task that this interrupt * interrupted), then lHigherPriorityTaskWoken will have been set to pdTRUE. * Passing pdTRUE into the following macro call will cause this interrupt to * return directly to the unblocked, higher priority, task. */ portEND_SWITCHING_ISR( lHigherPriorityTaskWoken ); } /*-----------------------------------------------------------*/ void vApplicationMallocFailedHook( void ) { /* vApplicationMallocFailedHook() will only be called if * configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook * function that will get called if a call to pvPortMalloc() fails. * pvPortMalloc() is called internally by the kernel whenever a task, queue, * timer or semaphore is created. It is also called by various parts of the * demo application. If heap_1.c or heap_2.c are used, then the size of the * heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in * FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used * to query the size of free heap space that remains (although it does not * provide information on how the remaining heap might be fragmented). */ taskDISABLE_INTERRUPTS(); for( ; ; ) { } } /*-----------------------------------------------------------*/ void vApplicationIdleHook( void ) { /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set * to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle * task. It is essential that code added to this hook function never attempts * to block in any way (for example, call xQueueReceive() with a block time * specified, or call vTaskDelay()). If the application makes use of the * vTaskDelete() API function (as this demo application does) then it is also * important that vApplicationIdleHook() is permitted to return to its calling * function, because it is the responsibility of the idle task to clean up * memory allocated by the kernel to any task that has since been deleted. */ } /*-----------------------------------------------------------*/ void vApplicationStackOverflowHook( TaskHandle_t pxTask, char * pcTaskName ) { ( void ) pcTaskName; ( void ) pxTask; /* Run time stack overflow checking is performed if * configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook * function is called if a stack overflow is detected. */ taskDISABLE_INTERRUPTS(); for( ; ; ) { } } /*-----------------------------------------------------------*/