Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1663 | pj | 1 | /* |
2 | * Project: S.Ha.R.K. |
||
3 | * |
||
4 | * Coordinators: |
||
5 | * Giorgio Buttazzo <giorgio@sssup.it> |
||
6 | * Paolo Gai <pj@gandalf.sssup.it> |
||
7 | * |
||
8 | * Authors : |
||
9 | * Paolo Gai <pj@gandalf.sssup.it> |
||
10 | * (see the web pages for full authors list) |
||
11 | * |
||
12 | * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy) |
||
13 | * |
||
14 | * http://www.sssup.it |
||
15 | * http://retis.sssup.it |
||
16 | * http://shark.sssup.it |
||
17 | */ |
||
18 | |||
19 | /** |
||
20 | ------------ |
||
21 | CVS : $Id: crunch.c,v 1.1 2004-07-05 14:17:16 pj Exp $ |
||
22 | |||
23 | File: $File$ |
||
24 | Revision: $Revision: 1.1 $ |
||
25 | Last update: $Date: 2004-07-05 14:17:16 $ |
||
26 | ------------ |
||
27 | |||
28 | **/ |
||
29 | |||
30 | /* |
||
31 | * Copyright (C) 2001 Paolo Gai |
||
32 | * |
||
33 | * This program is free software; you can redistribute it and/or modify |
||
34 | * it under the terms of the GNU General Public License as published by |
||
35 | * the Free Software Foundation; either version 2 of the License, or |
||
36 | * (at your option) any later version. |
||
37 | * |
||
38 | * This program is distributed in the hope that it will be useful, |
||
39 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
40 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
41 | * GNU General Public License for more details. |
||
42 | * |
||
43 | * You should have received a copy of the GNU General Public License |
||
44 | * along with this program; if not, write to the Free Software |
||
45 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
46 | * |
||
47 | */ |
||
48 | |||
49 | |||
50 | |||
51 | /* |
||
52 | * Crunch.c |
||
53 | */ |
||
54 | |||
55 | |||
56 | //#define DEBUG_CONFFILE |
||
57 | //#define DEBUG_MASTER_HARDCREATE |
||
58 | //#define DEBUG_MASTER_VALUECREATE |
||
59 | |||
60 | #include "valmodel.h" |
||
61 | #include "crunch.h" |
||
62 | #include <kernel/kern.h> |
||
63 | |||
64 | /* |
||
65 | * This small application simply reads the tasks and executes them. |
||
66 | * When they are all finished, the system shuts down. |
||
67 | */ |
||
68 | |||
69 | |||
70 | |||
71 | |||
72 | /* |
||
73 | * this table contains all the tasks information collected from the |
||
74 | * configuration file |
||
75 | */ |
||
76 | |||
77 | #define MAX_PATTERN 100000 |
||
78 | struct task_struct pattern[MAX_PATTERN]; |
||
79 | int pattern_dim; |
||
80 | |||
81 | /* This is some data collected by the task destructors that |
||
82 | * can be useful for understanding how the Module works */ |
||
83 | |||
84 | /* this int contains the value as seen by the application */ |
||
85 | int current_value = 0; |
||
86 | |||
87 | /* the number of finished tasks */ |
||
88 | int current_finished_tasks = 0; |
||
89 | |||
90 | /* this flag is triggered by the master task when it finishes */ |
||
91 | int finished = 0; |
||
92 | |||
93 | /* number of instances of an hard tasks |
||
94 | NOTE that it is 79 and not 80 as in the generation algorithm!!! */ |
||
95 | #define ASTER_LIM 79 |
||
96 | |||
97 | // Master task stuffs |
||
98 | PID master_pid; // the PID of the master task |
||
99 | int current_task; // the current position in the pattern array |
||
100 | |||
101 | // number of periodic tasks currently running... |
||
102 | int current_periodic_tasks = 0; |
||
103 | |||
104 | |||
105 | |||
106 | /* |
||
107 | * This function prints all the data read from the configuration file |
||
108 | */ |
||
109 | void print_conffile(char *name) |
||
110 | { |
||
111 | int i; |
||
112 | |||
113 | cprintf("--------------------------------------------------------------------\n"); |
||
114 | cprintf("Configuration file name : %s\n", name); |
||
115 | cprintf("Number of tasks in the pattern : %d\n", pattern_dim); |
||
116 | cprintf(" Time Hard/Value Dline WCET Iterations Value Penalty\n"); |
||
117 | cprintf("--------------------------------------------------------------------\n"); |
||
118 | |||
119 | for (i=0; i<pattern_dim; i++) { |
||
120 | cprintf("%10d %10d %10d %10d %10d %5d %7d\n", |
||
121 | pattern[i].time, |
||
122 | pattern[i].type, |
||
123 | pattern[i].reldline, |
||
124 | pattern[i].wcet, |
||
125 | pattern[i].iterations, |
||
126 | pattern[i].value, |
||
127 | pattern[i].penalty); |
||
128 | } |
||
129 | cprintf("--------------------------------------------------------------------\n"); |
||
130 | } |
||
131 | |||
132 | /* |
||
133 | * main() |
||
134 | */ |
||
135 | |||
136 | void scenario(void); |
||
137 | void create_master_task(void); |
||
138 | void update_scenario(void); |
||
139 | void print_statistics(void); |
||
140 | |||
141 | int main(int argc, char **argv) |
||
142 | { |
||
143 | srand(18); |
||
144 | |||
145 | set_exchandler_grx(); |
||
146 | |||
147 | /* |
||
148 | * Parse the configuration file |
||
149 | */ |
||
150 | crunch_file(pattern, &pattern_dim, MAX_PATTERN); |
||
151 | |||
152 | if (!pattern_dim) { |
||
153 | cprintf("Nothing to do!\n"); |
||
154 | sys_end(); |
||
155 | } |
||
156 | |||
157 | /* |
||
158 | * Print configuration file |
||
159 | */ |
||
160 | #ifdef DEBUG_CONFFILE |
||
161 | print_conffile(argv[1]); return 0; |
||
162 | #endif |
||
163 | |||
164 | /* |
||
165 | * Print the scenario |
||
166 | */ |
||
167 | scenario(); |
||
168 | |||
169 | /* |
||
170 | * Create the high priority task that will create all the periodic |
||
171 | * and value tasks. |
||
172 | */ |
||
173 | create_master_task(); |
||
174 | |||
175 | /* |
||
176 | * While in idle time, update the scenario |
||
177 | */ |
||
178 | while (!finished || task_counter != 2) { |
||
179 | update_scenario(); |
||
180 | } |
||
181 | |||
182 | print_statistics(); |
||
183 | |||
184 | return 0; |
||
185 | } |
||
186 | |||
187 | |||
188 | /* |
||
189 | * |
||
190 | * MASTER TASK STUFFS |
||
191 | * |
||
192 | */ |
||
193 | |||
194 | |||
195 | void activate_master_task(void *); |
||
196 | void *master_task(void *); |
||
197 | void *hard_task(void *); |
||
198 | void *value_task(void *); |
||
199 | |||
200 | void create_master_task() |
||
201 | { |
||
202 | NRT_TASK_MODEL nrt; |
||
203 | struct timespec t; |
||
204 | |||
205 | nrt_task_default_model(nrt); |
||
206 | nrt_task_def_usemath(nrt); |
||
207 | nrt_task_def_ctrl_jet(nrt); |
||
208 | nrt_task_def_save_arrivals(nrt); |
||
209 | |||
210 | master_pid = task_create("master", master_task, &nrt, NULL); |
||
211 | if(master_pid == -1) { |
||
212 | cprintf("Cannot create the master task!!!\n"); |
||
213 | sys_end(); |
||
214 | } |
||
215 | |||
216 | current_task = 0; |
||
217 | NULL_TIMESPEC(&t); |
||
218 | ADDUSEC2TIMESPEC(pattern[0].time, &t); |
||
219 | kern_cli(); |
||
220 | kern_event_post(&t, activate_master_task, NULL); |
||
221 | kern_sti(); |
||
222 | } |
||
223 | |||
224 | void activate_master_task(void *arg) |
||
225 | { |
||
226 | task_activate(master_pid); |
||
227 | } |
||
228 | |||
229 | void *master_task(void *arg) |
||
230 | { |
||
231 | int current_time; |
||
232 | |||
233 | HARD_TASK_MODEL hard; |
||
234 | PID phard; |
||
235 | |||
236 | VALUE_TASK_MODEL value; |
||
237 | |||
238 | PID group_container_pid[100]; |
||
239 | int group_container_pattern[100]; |
||
240 | int group_container_index; |
||
241 | |||
242 | // used to record the group_creation for value task |
||
243 | int i; |
||
244 | PID p; |
||
245 | |||
246 | task_nopreempt(); |
||
247 | |||
248 | hard_task_default_model(hard); |
||
249 | hard_task_def_usemath(hard); |
||
250 | value_task_default_model(value); |
||
251 | value_task_def_usemath(value); |
||
252 | |||
253 | do { |
||
254 | // current_task indexes the next task to be activated. |
||
255 | |||
256 | current_time = pattern[current_task].time; |
||
257 | |||
258 | // create and activate all the hard tasks with the same starting time |
||
259 | if (!pattern[current_task].type) { |
||
260 | do { |
||
261 | #ifdef DEBUG_MASTER_HARDCREATE |
||
262 | { int i=current_task; cprintf("%10d %10d %10d %10d %10d %5d %7d\n", |
||
263 | pattern[i].time, pattern[i].type, pattern[i].reldline, |
||
264 | pattern[i].wcet, pattern[i].iterations, pattern[i].value, |
||
265 | pattern[i].penalty); } |
||
266 | #endif |
||
267 | |||
268 | hard_task_def_mit(hard, pattern[current_task].reldline); |
||
269 | hard_task_def_wcet(hard, pattern[current_task].wcet); |
||
270 | hard_task_def_arg(hard, (void *)current_task); |
||
271 | |||
272 | phard = task_create("hard_task", hard_task, &hard, NULL); |
||
273 | if (phard == NIL) { |
||
274 | clear(); |
||
275 | cprintf("Cannot create hard task! (errno=%d)\n", errno); |
||
276 | sys_end(); |
||
277 | } |
||
278 | else { |
||
279 | task_activate(phard); |
||
280 | current_periodic_tasks++; |
||
281 | } |
||
282 | |||
283 | current_task++; |
||
284 | } while (current_task != pattern_dim && |
||
285 | pattern[current_task].time == current_time && |
||
286 | !pattern[current_task].type); |
||
287 | } |
||
288 | |||
289 | // create and activate all the soft tasks with the same starting time |
||
290 | if (current_task != pattern_dim && |
||
291 | pattern[current_task].time == current_time && |
||
292 | pattern[current_task].type) { |
||
293 | group_container_index = 0; |
||
294 | |||
295 | kern_cli(); |
||
296 | while (current_task != pattern_dim && |
||
297 | pattern[current_task].time == current_time && |
||
298 | pattern[current_task].type) { |
||
299 | |||
300 | #ifdef DEBUG_MASTER_VALUECREATE |
||
301 | { int i=current_task; cprintf("V%10d %10d %10d %10d %10d %5d %7d\n", |
||
302 | pattern[i].time, pattern[i].type, pattern[i].reldline, |
||
303 | pattern[i].wcet, pattern[i].iterations, pattern[i].value, |
||
304 | pattern[i].penalty); } |
||
305 | #endif |
||
306 | |||
307 | // account for the penalty before the task starts |
||
308 | current_value -= pattern[current_task].penalty; |
||
309 | // kern_printf("master_task: current_value=%d\n",current_value); |
||
310 | |||
311 | value_task_def_wcet(value,pattern[current_task].wcet); |
||
312 | value_task_def_dline(value,pattern[current_task].reldline); |
||
313 | value_task_def_value(value,pattern[current_task].value); |
||
314 | value_task_def_penalty(value,pattern[current_task].penalty); |
||
315 | value_task_def_arg(value, (void *)current_task); |
||
316 | p = group_create("value_task", value_task, (TASK_MODEL *)&value, NULL); |
||
317 | // kern_printf("\nCreating %d PID = %d\n",current_task, p); |
||
318 | // we need to know which task we are inserting, so we insert it in |
||
319 | // a list to check them later |
||
320 | group_container_pid[group_container_index]=p; |
||
321 | group_container_pattern[group_container_index]=current_task; |
||
322 | group_container_index++; |
||
323 | |||
324 | current_task++; |
||
325 | } |
||
326 | |||
327 | /* {kern_printf("group_container: index=%d, 0=%d, 1=%d, 2=%d, 3=%d\n", |
||
328 | group_container_index, group_container_pid[0], group_container_pid[1], |
||
329 | group_container_pid[2], group_container_pid[3]);}*/ |
||
330 | |||
331 | guarantee(); |
||
332 | // kern_printf("group_container_index=%d\n",group_container_index); |
||
333 | for (i=0; i<group_container_index; i++) { |
||
334 | // {kern_printf("i=%d, group_container: index=%d, 0=%d, 1=%d, 2=%d, 3=%d\n",i, |
||
335 | // group_container_index, group_container_pid[0], group_container_pid[1], |
||
336 | // group_container_pid[2], group_container_pid[3]);} |
||
337 | p = group_container_pid[i]; |
||
338 | // kern_printf("+%d\n",p); |
||
339 | if (crunch_taskaccepted(p)) { |
||
340 | // we need to pass the original model for the task... |
||
341 | // but just the common part is needed O:-) |
||
342 | value_task_def_arg(value, (void *)group_container_pattern[i]); |
||
343 | group_create_accept(p,(TASK_MODEL *)&value); |
||
344 | // kern_printf("Accetto task PID=%d\n",p); |
||
345 | } |
||
346 | else { |
||
347 | group_container_pid[i] = NIL; |
||
348 | // kern_printf("Rigetto task PID=%d\n",p); |
||
349 | |||
350 | group_create_reject(p); |
||
351 | } |
||
352 | } |
||
353 | kern_sti(); |
||
354 | |||
355 | for (i=0; i<group_container_index; i++) { |
||
356 | // kern_printf("Attivo %d PID=%d\n",i,group_container_pid[i]); |
||
357 | if (group_container_pid[i] != NIL) |
||
358 | task_activate(group_container_pid[i]); |
||
359 | } |
||
360 | } |
||
361 | |||
362 | if (current_task != pattern_dim) { |
||
363 | struct timespec t; |
||
364 | NULL_TIMESPEC(&t); |
||
365 | ADDUSEC2TIMESPEC(pattern[current_task].time, &t); |
||
366 | kern_cli(); |
||
367 | kern_event_post(&t, activate_master_task, NULL); |
||
368 | task_endcycle(); |
||
369 | } |
||
370 | } while(current_task != pattern_dim); |
||
371 | |||
372 | /* since all the tasks has been activated, the master_task will stop |
||
373 | forever. |
||
374 | In that way we can look at the elapsed time consumed by the master task. |
||
375 | */ |
||
376 | // task_nopreempt(); |
||
377 | finished = 1; |
||
378 | task_endcycle(); |
||
379 | |||
380 | return (void *)0; |
||
381 | } |
||
382 | |||
383 | |||
384 | void *hard_task(void *arg) |
||
385 | { |
||
386 | struct timespec dl, curr; // for deadline checking |
||
387 | |||
388 | int index, j, x, y; |
||
389 | char s[2]; |
||
390 | |||
391 | task_setcanceltype(TASK_CANCEL_ASYNCHRONOUS,&index); |
||
392 | |||
393 | index = (int)arg; |
||
394 | |||
395 | x = 0; |
||
396 | y = rand() % 6 + 3; |
||
397 | s[0] = '*'; s[1] = 0; |
||
398 | while (x < ASTER_LIM) { |
||
399 | for (j=0; j<pattern[index].iterations; j++) { |
||
400 | s[0] = '*' + rand() % 100; |
||
401 | puts_xy(x,y,rand()%15+1,s); |
||
402 | } |
||
403 | |||
404 | // Dline check :-) it is not perfect, but it works :-) |
||
405 | kern_cli(); |
||
406 | ll_gettime(TIME_EXACT,&curr); |
||
407 | dl = proc_table[exec_shadow].request_time; |
||
408 | ADDUSEC2TIMESPEC(pattern[index].reldline, &dl); |
||
409 | if (TIMESPEC_A_LT_B(&dl,&curr)) { |
||
410 | cprintf("\n\nPERIODIC TASK %d: Deadline miss!!!\n", index); |
||
411 | cprintf("current time= %10ld sec, %10ld ns\n", curr.tv_sec, curr.tv_nsec); |
||
412 | cprintf("deadline = %10ld sec, %10ld ns\n", dl.tv_sec, dl.tv_nsec); |
||
413 | sys_end(); |
||
414 | } |
||
415 | task_endcycle(); |
||
416 | |||
417 | puts_xy(x,y,WHITE," "); |
||
418 | x++; |
||
419 | } |
||
420 | |||
421 | kern_cli(); |
||
422 | |||
423 | // account for a finished task |
||
424 | current_finished_tasks++; |
||
425 | current_periodic_tasks--; |
||
426 | |||
427 | // Dline check :-) it is not perfect, but it works :-) |
||
428 | ll_gettime(TIME_EXACT,&curr); |
||
429 | dl=proc_table[exec_shadow].request_time; |
||
430 | ADDUSEC2TIMESPEC(pattern[index].reldline, &dl); |
||
431 | if (TIMESPEC_A_LT_B(&dl,&curr)) { |
||
432 | cprintf("\n\nPERIODIC TASK %d: Deadline miss!!!\n", index); |
||
433 | cprintf("current time= %10ld sec, %10ld ns\n", curr.tv_sec, curr.tv_nsec); |
||
434 | cprintf("deadline = %10ld sec, %10ld ns\n", dl.tv_sec, dl.tv_nsec); |
||
435 | sys_end(); |
||
436 | } |
||
437 | |||
438 | return (void *)0; |
||
439 | } |
||
440 | |||
441 | void *value_task(void *arg) |
||
442 | { |
||
443 | struct timespec dl, curr; // for deadline checking |
||
444 | int index, j, x, y; |
||
445 | char s[2]; |
||
446 | |||
447 | task_setcanceltype(TASK_CANCEL_ASYNCHRONOUS,&index); |
||
448 | index = (int)arg; |
||
449 | // kern_printf("Ú%dÙ",index); |
||
450 | |||
451 | x = rand() % 26 + 1; |
||
452 | y = rand() % 3 + 10; |
||
453 | s[0] = '*'; s[1] = 0; |
||
454 | for (j=0; j<pattern[index].iterations; j++) { |
||
455 | s[0] = '*' + rand() % 100; |
||
456 | puts_xy(x,y,rand()%15+1,s); |
||
457 | } |
||
458 | // kern_printf("*%d*\n",exec_shadow); |
||
459 | |||
460 | kern_cli(); |
||
461 | |||
462 | // account for a finished task |
||
463 | current_finished_tasks++; |
||
464 | |||
465 | // Dline check :-) it is not perfect, but it works :-) |
||
466 | ll_gettime(TIME_EXACT,&curr); |
||
467 | TIMESPEC_ASSIGN(&dl, &proc_table[exec_shadow].request_time); |
||
468 | ADDUSEC2TIMESPEC(pattern[index].reldline, &dl); |
||
469 | |||
470 | if (TIMESPEC_A_GT_B(&dl,&curr)) { |
||
471 | // The task finishes in time. account for the value |
||
472 | current_value += pattern[index].penalty + pattern[index].value; |
||
473 | } |
||
474 | // kern_printf("value: arg=%d current_value=%d pen=%d val=%d\n",arg,current_value, |
||
475 | // ((struct task_struct *)arg)->penalty, |
||
476 | // ((struct task_struct *)arg)->value); |
||
477 | |||
478 | return (void *)0; |
||
479 | } |
||
480 | |||
481 | void print_statistics(void) |
||
482 | { |
||
483 | TIME sum; |
||
484 | |||
485 | clear(); |
||
486 | |||
487 | jet_getstat(master_pid, &sum, 0, 0, 0); |
||
488 | task_activate(master_pid); // let the master task die... |
||
489 | |||
490 | clear(); |
||
491 | cprintf("CRUNCH 1.0 - by Paolo Gai 2001\n\n"); |
||
492 | cprintf("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n\n"); |
||
493 | cprintf("Final Results:\n\n"); |
||
494 | |||
495 | cprintf("The Master task elapsed time : %d microseconds.\n\n",(int)sum); |
||
496 | |||
497 | cprintf("Total number of tasks : %5d\n", pattern_dim); |
||
498 | cprintf("Total number of scheduled tasks: %5d\n\n",current_task); |
||
499 | |||
500 | cprintf("Per.tasks created but not ended: %5d\n\n",current_periodic_tasks); |
||
501 | cprintf("Total Value (from Module) : %5d\n",crunch_getvalue()); |
||
502 | cprintf("Total Value (from CRUNCH) : %5d\n",current_value); |
||
503 | cprintf("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n\n"); |
||
504 | } |
||
505 | |||
506 | /* |
||
507 | * Scenario |
||
508 | */ |
||
509 | |||
510 | void scenario(void) |
||
511 | { |
||
512 | char buf[100]; |
||
513 | clear(); |
||
514 | |||
515 | puts_xy(0, 0,BLUE , "CRUNCH 1.0"); |
||
516 | puts_xy(15,0,GREEN, "by Paolo Gai 2001"); |
||
517 | puts_xy(0, 2,WHITE, "ÄÄÄ Hard Periodic Tasks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); |
||
518 | puts_xy(0, 9,WHITE, "ÂÄÄ Soft Aperiodic Tasks ÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); |
||
519 | puts_xy(0,10,WHITE, "³ ³"); |
||
520 | puts_xy(0,11,WHITE, "³ ³"); |
||
521 | puts_xy(0,12,WHITE, "³ ³"); |
||
522 | puts_xy(0,13,WHITE, "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); |
||
523 | puts_xy(29,10,YELLOW, "Statistics:"); |
||
524 | sprintf(buf,"Total: %5d Already created: Periodic:",pattern_dim); |
||
525 | puts_xy(29,11,CYAN, buf); |
||
526 | puts_xy(29,12,BLUE, "Value reported (by Modules):"); |
||
527 | puts_xy(29,13,RED, " (by CRUNCH ):"); |
||
528 | |||
529 | puts_xy(0,14,WHITE, "ÄÄÄ Debug space for Logging Module functions... ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); |
||
530 | place(0,15); |
||
531 | } |
||
532 | |||
533 | void update_scenario(void) |
||
534 | { |
||
535 | char buf[100]; |
||
536 | sprintf(buf,"%5d",current_task); |
||
537 | puts_xy(58,11,WHITE,buf); |
||
538 | sprintf(buf,"%5d",current_periodic_tasks); |
||
539 | puts_xy(75,11,WHITE,buf); |
||
540 | sprintf(buf,"%5d",crunch_getvalue()); |
||
541 | puts_xy(58,12,WHITE,buf); |
||
542 | sprintf(buf,"%5d",current_value); |
||
543 | puts_xy(58,13,WHITE,buf); |
||
544 | } |