CoOS Demonstrator adapted to mbed Hardware.

Dependencies:   mbed

Committer:
ericebert
Date:
Fri Dec 03 19:45:30 2010 +0000
Revision:
0:57690853989a
Some basic LED-Flashing works in the CoOS-RTOS using Tasks

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ericebert 0:57690853989a 1 /**
ericebert 0:57690853989a 2 *******************************************************************************
ericebert 0:57690853989a 3 * @file time.c
ericebert 0:57690853989a 4 * @version V1.1.3
ericebert 0:57690853989a 5 * @date 2010.04.26
ericebert 0:57690853989a 6 * @brief time management implementation code of CooCox CoOS kernel.
ericebert 0:57690853989a 7 *******************************************************************************
ericebert 0:57690853989a 8 * @copy
ericebert 0:57690853989a 9 *
ericebert 0:57690853989a 10 * INTERNAL FILE,DON'T PUBLIC.
ericebert 0:57690853989a 11 *
ericebert 0:57690853989a 12 * <h2><center>&copy; COPYRIGHT 2009 CooCox </center></h2>
ericebert 0:57690853989a 13 *******************************************************************************
ericebert 0:57690853989a 14 */
ericebert 0:57690853989a 15
ericebert 0:57690853989a 16
ericebert 0:57690853989a 17
ericebert 0:57690853989a 18 /*---------------------------- Include ---------------------------------------*/
ericebert 0:57690853989a 19 #include <coocox.h>
ericebert 0:57690853989a 20
ericebert 0:57690853989a 21 #if CFG_TASK_WAITTING_EN > 0
ericebert 0:57690853989a 22
ericebert 0:57690853989a 23 /*---------------------------- Variable Define -------------------------------*/
ericebert 0:57690853989a 24 P_OSTCB DlyList = 0; /*!< Header pointer to the DELAY list.*/
ericebert 0:57690853989a 25
ericebert 0:57690853989a 26
ericebert 0:57690853989a 27 /**
ericebert 0:57690853989a 28 *******************************************************************************
ericebert 0:57690853989a 29 * @brief Insert into DELAY list
ericebert 0:57690853989a 30 *
ericebert 0:57690853989a 31 * @param[in] ptcb Task that want to insert into DELAY list.
ericebert 0:57690853989a 32 * @param[in] ticks Delay system ticks.
ericebert 0:57690853989a 33 * @param[out] None
ericebert 0:57690853989a 34 * @retval None.
ericebert 0:57690853989a 35 *
ericebert 0:57690853989a 36 * @par Description
ericebert 0:57690853989a 37 * @details This function is called to insert task into DELAY list.
ericebert 0:57690853989a 38 *******************************************************************************
ericebert 0:57690853989a 39 */
ericebert 0:57690853989a 40 void InsertDelayList(P_OSTCB ptcb,U32 ticks)
ericebert 0:57690853989a 41 {
ericebert 0:57690853989a 42 S32 deltaTicks;
ericebert 0:57690853989a 43 P_OSTCB dlyNext;
ericebert 0:57690853989a 44
ericebert 0:57690853989a 45 if(ticks == 0) /* Is delay tick == 0? */
ericebert 0:57690853989a 46 return; /* Yes,do nothing,return */
ericebert 0:57690853989a 47 if(DlyList == 0) /* Is no item in DELAY list? */
ericebert 0:57690853989a 48 {
ericebert 0:57690853989a 49 ptcb->delayTick = ticks; /* Yes,set this as first item */
ericebert 0:57690853989a 50 DlyList = ptcb;
ericebert 0:57690853989a 51 }
ericebert 0:57690853989a 52 else
ericebert 0:57690853989a 53 {
ericebert 0:57690853989a 54 /* No,find correct place ,and insert the task */
ericebert 0:57690853989a 55 dlyNext = DlyList;
ericebert 0:57690853989a 56 deltaTicks = ticks; /* Get task delay ticks */
ericebert 0:57690853989a 57
ericebert 0:57690853989a 58 /* Find correct place */
ericebert 0:57690853989a 59 while(dlyNext != 0)
ericebert 0:57690853989a 60 {
ericebert 0:57690853989a 61 /* Get delta ticks with previous item */
ericebert 0:57690853989a 62 deltaTicks -= dlyNext->delayTick;
ericebert 0:57690853989a 63 if(deltaTicks < 0) /* Is delta ticks<0? */
ericebert 0:57690853989a 64 {
ericebert 0:57690853989a 65 /* Yes,get correct place */
ericebert 0:57690853989a 66 if(dlyNext->TCBprev != 0) /* Is head item of DELAY list? */
ericebert 0:57690853989a 67 {
ericebert 0:57690853989a 68 dlyNext->TCBprev->TCBnext = ptcb; /* No,insert into */
ericebert 0:57690853989a 69 ptcb->TCBprev = dlyNext->TCBprev;
ericebert 0:57690853989a 70 ptcb->TCBnext = dlyNext;
ericebert 0:57690853989a 71 dlyNext->TCBprev = ptcb;
ericebert 0:57690853989a 72 }
ericebert 0:57690853989a 73 else /* Yes,set task as first item */
ericebert 0:57690853989a 74 {
ericebert 0:57690853989a 75 ptcb->TCBnext = DlyList;
ericebert 0:57690853989a 76 DlyList->TCBprev = ptcb;
ericebert 0:57690853989a 77 DlyList = ptcb;
ericebert 0:57690853989a 78 }
ericebert 0:57690853989a 79 ptcb->delayTick = ptcb->TCBnext->delayTick+deltaTicks;
ericebert 0:57690853989a 80 ptcb->TCBnext->delayTick -= ptcb->delayTick;
ericebert 0:57690853989a 81 break;
ericebert 0:57690853989a 82 }
ericebert 0:57690853989a 83 /* Is last item in DELAY list? */
ericebert 0:57690853989a 84 else if((deltaTicks >= 0) && (dlyNext->TCBnext == 0) )
ericebert 0:57690853989a 85 {
ericebert 0:57690853989a 86 ptcb->TCBprev = dlyNext; /* Yes,insert into */
ericebert 0:57690853989a 87 dlyNext->TCBnext = ptcb;
ericebert 0:57690853989a 88 ptcb->delayTick = deltaTicks;
ericebert 0:57690853989a 89 break;
ericebert 0:57690853989a 90 }
ericebert 0:57690853989a 91 dlyNext = dlyNext->TCBnext; /* Get the next item in DELAY list */
ericebert 0:57690853989a 92 }
ericebert 0:57690853989a 93 }
ericebert 0:57690853989a 94
ericebert 0:57690853989a 95 ptcb->state = TASK_WAITING; /* Set task status as TASK_WAITING */
ericebert 0:57690853989a 96 TaskSchedReq = TRUE;
ericebert 0:57690853989a 97 }
ericebert 0:57690853989a 98
ericebert 0:57690853989a 99
ericebert 0:57690853989a 100 /**
ericebert 0:57690853989a 101 *******************************************************************************
ericebert 0:57690853989a 102 * @brief Remove from the DELAY list
ericebert 0:57690853989a 103 * @param[in] ptcb Task that want to remove from the DELAY list.
ericebert 0:57690853989a 104 * @param[out] None
ericebert 0:57690853989a 105 * @retval None
ericebert 0:57690853989a 106 *
ericebert 0:57690853989a 107 * @par Description
ericebert 0:57690853989a 108 * @details This function is called to remove task from the DELAY list.
ericebert 0:57690853989a 109 *******************************************************************************
ericebert 0:57690853989a 110 */
ericebert 0:57690853989a 111 void RemoveDelayList(P_OSTCB ptcb)
ericebert 0:57690853989a 112 {
ericebert 0:57690853989a 113
ericebert 0:57690853989a 114 /* Is there only one item in the DELAY list? */
ericebert 0:57690853989a 115 if((ptcb->TCBprev == 0) && ( ptcb->TCBnext == 0))
ericebert 0:57690853989a 116 {
ericebert 0:57690853989a 117 DlyList = 0; /* Yes,set DELAY list as 0 */
ericebert 0:57690853989a 118 }
ericebert 0:57690853989a 119 else if(ptcb->TCBprev == 0) /* Is the first item in DELAY list? */
ericebert 0:57690853989a 120 {
ericebert 0:57690853989a 121 /* Yes,remove task from the DELAY list,and reset the list */
ericebert 0:57690853989a 122 DlyList = ptcb->TCBnext;
ericebert 0:57690853989a 123 ptcb->TCBnext->delayTick += ptcb->delayTick;
ericebert 0:57690853989a 124 ptcb->TCBnext->TCBprev = 0;
ericebert 0:57690853989a 125 ptcb->TCBnext = 0;
ericebert 0:57690853989a 126
ericebert 0:57690853989a 127 }
ericebert 0:57690853989a 128 else if(ptcb->TCBnext == 0) /* Is the last item in DELAY list? */
ericebert 0:57690853989a 129 {
ericebert 0:57690853989a 130 ptcb->TCBprev->TCBnext = 0; /* Yes,remove task form DELAY list */
ericebert 0:57690853989a 131 ptcb->TCBprev = 0;
ericebert 0:57690853989a 132 }
ericebert 0:57690853989a 133 else /* No, remove task from DELAY list */
ericebert 0:57690853989a 134 {
ericebert 0:57690853989a 135 ptcb->TCBprev->TCBnext = ptcb->TCBnext;
ericebert 0:57690853989a 136 ptcb->TCBnext->TCBprev = ptcb->TCBprev;
ericebert 0:57690853989a 137 ptcb->TCBnext->delayTick += ptcb->delayTick;
ericebert 0:57690853989a 138 ptcb->TCBnext = 0;
ericebert 0:57690853989a 139 ptcb->TCBprev = 0;
ericebert 0:57690853989a 140 }
ericebert 0:57690853989a 141 ptcb->delayTick = INVALID_VALUE; /* Set task delay tick value as invalid */
ericebert 0:57690853989a 142 }
ericebert 0:57690853989a 143
ericebert 0:57690853989a 144 /**
ericebert 0:57690853989a 145 *******************************************************************************
ericebert 0:57690853989a 146 * @brief Get current ticks
ericebert 0:57690853989a 147 * @param[in] None
ericebert 0:57690853989a 148 * @param[out] None
ericebert 0:57690853989a 149 * @retval Return current system tick counter.
ericebert 0:57690853989a 150 *
ericebert 0:57690853989a 151 * @par Description
ericebert 0:57690853989a 152 * @details This function is called to obtain current system tick counter.
ericebert 0:57690853989a 153 *******************************************************************************
ericebert 0:57690853989a 154 */
ericebert 0:57690853989a 155 U64 CoGetOSTime(void)
ericebert 0:57690853989a 156 {
ericebert 0:57690853989a 157 return OSTickCnt; /* Get system time(tick) */
ericebert 0:57690853989a 158 }
ericebert 0:57690853989a 159
ericebert 0:57690853989a 160
ericebert 0:57690853989a 161 /**
ericebert 0:57690853989a 162 *******************************************************************************
ericebert 0:57690853989a 163 * @brief Delay current task for specify ticks number
ericebert 0:57690853989a 164 * @param[in] ticks Specify system tick number which will delay.
ericebert 0:57690853989a 165 * @param[out] None
ericebert 0:57690853989a 166 * @retval E_CALL Error call in ISR.
ericebert 0:57690853989a 167 * @retval E_OK The current task was insert to DELAY list successful,it
ericebert 0:57690853989a 168 * will delay specify time.
ericebert 0:57690853989a 169 * @par Description
ericebert 0:57690853989a 170 * @details This function delay specify ticks for current task.
ericebert 0:57690853989a 171 *
ericebert 0:57690853989a 172 * @note This function be called in ISR,do nothing and return immediately.
ericebert 0:57690853989a 173 *******************************************************************************
ericebert 0:57690853989a 174 */
ericebert 0:57690853989a 175 StatusType CoTickDelay(U32 ticks)
ericebert 0:57690853989a 176 {
ericebert 0:57690853989a 177 if(OSIntNesting >0) /* Is call in ISR? */
ericebert 0:57690853989a 178 {
ericebert 0:57690853989a 179 return E_CALL; /* Yes,error return */
ericebert 0:57690853989a 180 }
ericebert 0:57690853989a 181
ericebert 0:57690853989a 182 if(ticks == INVALID_VALUE) /* Is tick==INVALID_VALUE? */
ericebert 0:57690853989a 183 {
ericebert 0:57690853989a 184 return E_INVALID_PARAMETER; /* Yes,error return */
ericebert 0:57690853989a 185 }
ericebert 0:57690853989a 186 if(ticks == 0) /* Is tick==0? */
ericebert 0:57690853989a 187 {
ericebert 0:57690853989a 188 return E_OK; /* Yes,do nothing ,return OK */
ericebert 0:57690853989a 189 }
ericebert 0:57690853989a 190 if(OSSchedLock != 0) /* Is OS lock? */
ericebert 0:57690853989a 191 {
ericebert 0:57690853989a 192 return E_OS_IN_LOCK; /* Yes,error return */
ericebert 0:57690853989a 193 }
ericebert 0:57690853989a 194 OsSchedLock(); /* Lock schedule */
ericebert 0:57690853989a 195 InsertDelayList(TCBRunning,ticks); /* Insert task in DELAY list */
ericebert 0:57690853989a 196 OsSchedUnlock(); /* Unlock schedule,and call task schedule */
ericebert 0:57690853989a 197 return E_OK; /* Return OK */
ericebert 0:57690853989a 198 }
ericebert 0:57690853989a 199
ericebert 0:57690853989a 200
ericebert 0:57690853989a 201 /**
ericebert 0:57690853989a 202 *******************************************************************************
ericebert 0:57690853989a 203 * @brief Reset task delay ticks
ericebert 0:57690853989a 204 * @param[in] ptcb Task that want to insert into DELAY list.
ericebert 0:57690853989a 205 * @param[in] ticks Specify system tick number which will delay .
ericebert 0:57690853989a 206 * @param[out] None
ericebert 0:57690853989a 207 * @retval E_CALL Error call in ISR.
ericebert 0:57690853989a 208 * @retval E_INVALID_ID Invalid task id.
ericebert 0:57690853989a 209 * @retval E_NOT_IN_DELAY_LIST Task not in delay list.
ericebert 0:57690853989a 210 * @retval E_OK The current task was inserted to DELAY list
ericebert 0:57690853989a 211 * successful,it will delay for specify time.
ericebert 0:57690853989a 212 * @par Description
ericebert 0:57690853989a 213 * @details This function delay specify ticks for current task.
ericebert 0:57690853989a 214 *******************************************************************************
ericebert 0:57690853989a 215 */
ericebert 0:57690853989a 216 StatusType CoResetTaskDelayTick(OS_TID taskID,U32 ticks)
ericebert 0:57690853989a 217 {
ericebert 0:57690853989a 218 P_OSTCB ptcb;
ericebert 0:57690853989a 219
ericebert 0:57690853989a 220
ericebert 0:57690853989a 221 #if CFG_PAR_CHECKOUT_EN >0 /* Check validity of parameter */
ericebert 0:57690853989a 222 if(taskID >= CFG_MAX_USER_TASKS + SYS_TASK_NUM)
ericebert 0:57690853989a 223 {
ericebert 0:57690853989a 224 return E_INVALID_ID;
ericebert 0:57690853989a 225 }
ericebert 0:57690853989a 226 #endif
ericebert 0:57690853989a 227
ericebert 0:57690853989a 228 ptcb = &TCBTbl[taskID];
ericebert 0:57690853989a 229 #if CFG_PAR_CHECKOUT_EN >0
ericebert 0:57690853989a 230 if(ptcb->stkPtr == 0)
ericebert 0:57690853989a 231 {
ericebert 0:57690853989a 232 return E_INVALID_ID;
ericebert 0:57690853989a 233 }
ericebert 0:57690853989a 234 #endif
ericebert 0:57690853989a 235
ericebert 0:57690853989a 236 if(ptcb->delayTick == INVALID_VALUE) /* Is tick==INVALID_VALUE? */
ericebert 0:57690853989a 237 {
ericebert 0:57690853989a 238 return E_NOT_IN_DELAY_LIST; /* Yes,error return */
ericebert 0:57690853989a 239 }
ericebert 0:57690853989a 240 OsSchedLock(); /* Lock schedule */
ericebert 0:57690853989a 241 RemoveDelayList(ptcb); /* Remove task from the DELAY list */
ericebert 0:57690853989a 242
ericebert 0:57690853989a 243 if(ticks == 0) /* Is delay tick==0? */
ericebert 0:57690853989a 244 {
ericebert 0:57690853989a 245 InsertToTCBRdyList(ptcb); /* Insert task into the DELAY list */
ericebert 0:57690853989a 246 }
ericebert 0:57690853989a 247 else
ericebert 0:57690853989a 248 {
ericebert 0:57690853989a 249 InsertDelayList(ptcb,ticks); /* No,insert task into DELAY list */
ericebert 0:57690853989a 250 }
ericebert 0:57690853989a 251 OsSchedUnlock(); /* Unlock schedule,and call task schedule */
ericebert 0:57690853989a 252 return E_OK; /* Return OK */
ericebert 0:57690853989a 253 }
ericebert 0:57690853989a 254
ericebert 0:57690853989a 255
ericebert 0:57690853989a 256 /**
ericebert 0:57690853989a 257 *******************************************************************************
ericebert 0:57690853989a 258 * @brief Delay current task for detail time
ericebert 0:57690853989a 259 * @param[in] hour Specify the number of hours.
ericebert 0:57690853989a 260 * @param[in] minute Specify the number of minutes.
ericebert 0:57690853989a 261 * @param[in] sec Specify the number of seconds.
ericebert 0:57690853989a 262 * @param[in] millsec Specify the number of millseconds.
ericebert 0:57690853989a 263 * @param[out] None
ericebert 0:57690853989a 264 * @retval E_CALL Error call in ISR.
ericebert 0:57690853989a 265 * @retval E_INVALID_PARAMETER Parameter passed was invalid,delay fail.
ericebert 0:57690853989a 266 * @retval E_OK The current task was inserted to DELAY list
ericebert 0:57690853989a 267 * successful,it will delay for specify time.
ericebert 0:57690853989a 268 * @par Description
ericebert 0:57690853989a 269 * @details This function delay specify time for current task.
ericebert 0:57690853989a 270 *
ericebert 0:57690853989a 271 * @note If this function called in ISR,do nothing and return immediately.
ericebert 0:57690853989a 272 *******************************************************************************
ericebert 0:57690853989a 273 */
ericebert 0:57690853989a 274 #if CFG_TIME_DELAY_EN >0
ericebert 0:57690853989a 275 StatusType CoTimeDelay(U8 hour,U8 minute,U8 sec,U16 millsec)
ericebert 0:57690853989a 276 {
ericebert 0:57690853989a 277 U32 ticks;
ericebert 0:57690853989a 278 #if CFG_PAR_CHECKOUT_EN >0 /* Check validity of parameter */
ericebert 0:57690853989a 279 if(OSIntNesting > 0)
ericebert 0:57690853989a 280 {
ericebert 0:57690853989a 281 return E_CALL;
ericebert 0:57690853989a 282 }
ericebert 0:57690853989a 283 if((minute > 59)||(sec > 59)||(millsec > 999))
ericebert 0:57690853989a 284 return E_INVALID_PARAMETER;
ericebert 0:57690853989a 285 #endif
ericebert 0:57690853989a 286 if(OSSchedLock != 0) /* Is OS lock? */
ericebert 0:57690853989a 287 {
ericebert 0:57690853989a 288 return E_OS_IN_LOCK; /* Yes,error return */
ericebert 0:57690853989a 289 }
ericebert 0:57690853989a 290
ericebert 0:57690853989a 291 /* Get tick counter from time */
ericebert 0:57690853989a 292 ticks = ((hour*3600) + (minute*60) + (sec)) * (CFG_SYSTICK_FREQ)\
ericebert 0:57690853989a 293 + (millsec*CFG_SYSTICK_FREQ + 500)/1000;
ericebert 0:57690853989a 294
ericebert 0:57690853989a 295 CoTickDelay(ticks); /* Call tick delay */
ericebert 0:57690853989a 296 return E_OK; /* Return OK */
ericebert 0:57690853989a 297 }
ericebert 0:57690853989a 298 #endif
ericebert 0:57690853989a 299
ericebert 0:57690853989a 300
ericebert 0:57690853989a 301
ericebert 0:57690853989a 302
ericebert 0:57690853989a 303 /**
ericebert 0:57690853989a 304 *******************************************************************************
ericebert 0:57690853989a 305 * @brief Dispose time delay
ericebert 0:57690853989a 306 * @param[in] None
ericebert 0:57690853989a 307 * @param[out] None
ericebert 0:57690853989a 308 * @retval None
ericebert 0:57690853989a 309 *
ericebert 0:57690853989a 310 * @par Description
ericebert 0:57690853989a 311 * @details This function is called to dispose time delay of all task.
ericebert 0:57690853989a 312 *******************************************************************************
ericebert 0:57690853989a 313 */
ericebert 0:57690853989a 314 void TimeDispose(void)
ericebert 0:57690853989a 315 {
ericebert 0:57690853989a 316 P_OSTCB dlyList;
ericebert 0:57690853989a 317
ericebert 0:57690853989a 318 dlyList = DlyList; /* Get first item of DELAY list */
ericebert 0:57690853989a 319 while((dlyList != 0) && (dlyList->delayTick == 0) )
ericebert 0:57690853989a 320 {
ericebert 0:57690853989a 321
ericebert 0:57690853989a 322 #if CFG_EVENT_EN > 0
ericebert 0:57690853989a 323 if(dlyList->eventID != INVALID_ID) /* Is task in event waiting list? */
ericebert 0:57690853989a 324 {
ericebert 0:57690853989a 325 RemoveEventWaittingList(dlyList); /* Yes,remove task from list */
ericebert 0:57690853989a 326 }
ericebert 0:57690853989a 327 #endif
ericebert 0:57690853989a 328
ericebert 0:57690853989a 329 #if CFG_FLAG_EN > 0
ericebert 0:57690853989a 330 if(dlyList->pnode != 0) /* Is task in flag waiting list? */
ericebert 0:57690853989a 331 {
ericebert 0:57690853989a 332 RemoveLinkNode((P_FLAG_NODE)dlyList->pnode); /* Yes,remove task from list */
ericebert 0:57690853989a 333 }
ericebert 0:57690853989a 334 #endif
ericebert 0:57690853989a 335 dlyList->delayTick = INVALID_VALUE; /* Set delay tick value as invalid*/
ericebert 0:57690853989a 336 DlyList = dlyList->TCBnext; /* Get next item as the head of DELAY list*/
ericebert 0:57690853989a 337 dlyList->TCBnext = 0;
ericebert 0:57690853989a 338
ericebert 0:57690853989a 339 InsertToTCBRdyList(dlyList); /* Insert task into READY list */
ericebert 0:57690853989a 340
ericebert 0:57690853989a 341 dlyList = DlyList; /* Get the first item of DELAY list */
ericebert 0:57690853989a 342 if(dlyList != 0) /* Is DELAY list as 0? */
ericebert 0:57690853989a 343 {
ericebert 0:57690853989a 344 dlyList->TCBprev = 0; /* No,initialize the first item */
ericebert 0:57690853989a 345 }
ericebert 0:57690853989a 346 }
ericebert 0:57690853989a 347 }
ericebert 0:57690853989a 348
ericebert 0:57690853989a 349
ericebert 0:57690853989a 350 /**
ericebert 0:57690853989a 351 *******************************************************************************
ericebert 0:57690853989a 352 * @brief Dispose time delay in ISR
ericebert 0:57690853989a 353 * @param[in] None
ericebert 0:57690853989a 354 * @param[out] None
ericebert 0:57690853989a 355 * @retval None
ericebert 0:57690853989a 356 *
ericebert 0:57690853989a 357 * @par Description
ericebert 0:57690853989a 358 * @details This function is called in systick interrupt to dispose time delay
ericebert 0:57690853989a 359 * of all task.
ericebert 0:57690853989a 360 *******************************************************************************
ericebert 0:57690853989a 361 */
ericebert 0:57690853989a 362 void isr_TimeDispose(void)
ericebert 0:57690853989a 363 {
ericebert 0:57690853989a 364 if(OSSchedLock > 1) /* Is schedule lock? */
ericebert 0:57690853989a 365 {
ericebert 0:57690853989a 366 IsrReq = TRUE;
ericebert 0:57690853989a 367 TimeReq = TRUE; /* Yes,set time request true */
ericebert 0:57690853989a 368 }
ericebert 0:57690853989a 369 else
ericebert 0:57690853989a 370 {
ericebert 0:57690853989a 371 TimeDispose(); /* No,call handler */
ericebert 0:57690853989a 372 }
ericebert 0:57690853989a 373 }
ericebert 0:57690853989a 374
ericebert 0:57690853989a 375
ericebert 0:57690853989a 376 #endif