source: trunk/minix/commands/bc/execute.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 17.3 KB
Line 
1/* execute.c - run a bc program. */
2
3/* This file is part of bc written for MINIX.
4 Copyright (C) 1991, 1992 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License , or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 You may contact the author by:
21 e-mail: phil@cs.wwu.edu
22 us-mail: Philip A. Nelson
23 Computer Science Department, 9062
24 Western Washington University
25 Bellingham, WA 98226-9062
26
27*************************************************************************/
28
29#include "bcdefs.h"
30#include <signal.h>
31#include "global.h"
32#include "proto.h"
33
34
35/* The SIGINT interrupt handling routine. */
36
37int had_sigint;
38
39void
40stop_execution (sig)
41 int sig;
42{
43 had_sigint = TRUE;
44 printf ("\n");
45 rt_error ("interrupted execution");
46}
47
48
49/* Get the current byte and advance the PC counter. */
50
51unsigned char
52byte (pc)
53 program_counter *pc;
54{
55 int seg, offset;
56
57 seg = pc->pc_addr >> BC_SEG_LOG;
58 offset = pc->pc_addr++ % BC_SEG_SIZE;
59 return (functions[pc->pc_func].f_body[seg][offset]);
60}
61
62
63/* The routine that actually runs the machine. */
64
65void
66execute ()
67{
68 int label_num, l_gp, l_off;
69 bc_label_group *gp;
70
71 char inst, ch;
72 int new_func;
73 int var_name;
74
75 int const_base;
76
77 bc_num temp_num;
78 arg_list *auto_list;
79
80 /* Initialize this run... */
81 pc.pc_func = 0;
82 pc.pc_addr = 0;
83 runtime_error = FALSE;
84 init_num (&temp_num);
85
86 /* Set up the interrupt mechanism for an interactive session. */
87 if (interactive)
88 {
89 signal (SIGINT, stop_execution);
90 had_sigint = FALSE;
91 }
92
93 while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error)
94 {
95 inst = byte(&pc);
96
97#if DEBUG > 3
98 { /* Print out address and the stack before each instruction.*/
99 int depth; estack_rec *temp = ex_stack;
100
101 printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst);
102 if (temp == NULL) printf ("empty stack.\n", inst);
103 else
104 {
105 depth = 1;
106 while (temp != NULL)
107 {
108 printf (" %d = ", depth);
109 out_num (temp->s_num, 10, out_char);
110 depth++;
111 temp = temp->s_next;
112 }
113 }
114 }
115#endif
116
117 switch ( inst )
118 {
119
120 case 'A' : /* increment array variable (Add one). */
121 var_name = byte(&pc);
122 if ((var_name & 0x80) != 0)
123 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
124 incr_array (var_name);
125 break;
126
127 case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */
128 case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */
129 c_code = !is_zero (ex_stack->s_num);
130 pop ();
131 case 'J' : /* Jump to a label. */
132 label_num = byte(&pc); /* Low order bits first. */
133 label_num += byte(&pc) << 8;
134 if (inst == 'J' || (inst == 'B' && c_code)
135 || (inst == 'Z' && !c_code)) {
136 gp = functions[pc.pc_func].f_label;
137 l_gp = label_num >> BC_LABEL_LOG;
138 l_off = label_num % BC_LABEL_GROUP;
139 while (l_gp-- > 0) gp = gp->l_next;
140 pc.pc_addr = gp->l_adrs[l_off];
141 }
142 break;
143
144 case 'C' : /* Call a function. */
145 /* Get the function number. */
146 new_func = byte(&pc);
147 if ((new_func & 0x80) != 0)
148 new_func = ((new_func << 8) & 0x7f) + byte(&pc);
149
150 /* Check to make sure it is defined. */
151 if (!functions[new_func].f_defined)
152 {
153 rt_error ("Function %s not defined.", f_names[new_func]);
154 break;
155 }
156
157 /* Check and push parameters. */
158 process_params (&pc, new_func);
159
160 /* Push auto variables. */
161 for (auto_list = functions[new_func].f_autos;
162 auto_list != NULL;
163 auto_list = auto_list->next)
164 auto_var (auto_list->av_name);
165
166 /* Push pc and ibase. */
167 fpush (pc.pc_func);
168 fpush (pc.pc_addr);
169 fpush (i_base);
170
171 /* Reset pc to start of function. */
172 pc.pc_func = new_func;
173 pc.pc_addr = 0;
174 break;
175
176 case 'D' : /* Duplicate top of stack */
177 push_copy (ex_stack->s_num);
178 break;
179
180 case 'K' : /* Push a constant */
181 /* Get the input base and convert it to a bc number. */
182 if (pc.pc_func == 0)
183 const_base = i_base;
184 else
185 const_base = fn_stack->s_val;
186 if (const_base == 10)
187 push_b10_const (&pc);
188 else
189 push_constant (prog_char, const_base);
190 break;
191
192 case 'L' : /* load array variable */
193 var_name = byte(&pc);
194 if ((var_name & 0x80) != 0)
195 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
196 load_array (var_name);
197 break;
198
199 case 'M' : /* decrement array variable (Minus!) */
200 var_name = byte(&pc);
201 if ((var_name & 0x80) != 0)
202 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
203 decr_array (var_name);
204 break;
205
206 case 'O' : /* Write a string to the output with processing. */
207 while ((ch = byte(&pc)) != '"')
208 if (ch != '\\')
209 out_char (ch);
210 else
211 {
212 ch = byte(&pc);
213 if (ch == '"') break;
214 switch (ch)
215 {
216 case 'n': out_char ('\n'); break;
217 case 't': out_char ('\t'); break;
218 case 'r': out_char ('\r'); break;
219 case 'b': out_char (007); break;
220 case 'f': out_char ('\f'); break;
221 case '\\': out_char ('\\'); break;
222 default: break;
223 }
224 }
225 if (interactive) fflush (stdout);
226 break;
227
228 case 'R' : /* Return from function */
229 if (pc.pc_func != 0)
230 {
231 /* "Pop" autos and parameters. */
232 pop_vars(functions[pc.pc_func].f_autos);
233 pop_vars(functions[pc.pc_func].f_params);
234 /* reset the pc. */
235 fpop ();
236 pc.pc_addr = fpop ();
237 pc.pc_func = fpop ();
238 }
239 else
240 rt_error ("Return from main program.");
241 break;
242
243 case 'S' : /* store array variable */
244 var_name = byte(&pc);
245 if ((var_name & 0x80) != 0)
246 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
247 store_array (var_name);
248 break;
249
250 case 'T' : /* Test tos for zero */
251 c_code = is_zero (ex_stack->s_num);
252 assign (c_code);
253 break;
254
255 case 'W' : /* Write the value on the top of the stack. */
256 case 'P' : /* Write the value on the top of the stack. No newline. */
257 out_num (ex_stack->s_num, o_base, out_char);
258 if (inst == 'W') out_char ('\n');
259 store_var (3); /* Special variable "last". */
260 if (interactive) fflush (stdout);
261 break;
262
263 case 'c' : /* Call special function. */
264 new_func = byte(&pc);
265
266 switch (new_func)
267 {
268 case 'L': /* Length function. */
269 /* For the number 0.xxxx, 0 is not significant. */
270 if (ex_stack->s_num->n_len == 1 &&
271 ex_stack->s_num->n_scale != 0 &&
272 ex_stack->s_num->n_value[0] == 0 )
273 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
274 else
275 int2num (&ex_stack->s_num, ex_stack->s_num->n_len
276 + ex_stack->s_num->n_scale);
277 break;
278
279 case 'S': /* Scale function. */
280 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
281 break;
282
283 case 'R': /* Square Root function. */
284 if (!bc_sqrt (&ex_stack->s_num, scale))
285 rt_error ("Square root of a negative number");
286 break;
287
288 case 'I': /* Read function. */
289 push_constant (input_char, i_base);
290 break;
291 }
292 break;
293
294 case 'd' : /* Decrement number */
295 var_name = byte(&pc);
296 if ((var_name & 0x80) != 0)
297 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
298 decr_var (var_name);
299 break;
300
301 case 'h' : /* Halt the machine. */
302 exit (0);
303
304 case 'i' : /* increment number */
305 var_name = byte(&pc);
306 if ((var_name & 0x80) != 0)
307 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
308 incr_var (var_name);
309 break;
310
311 case 'l' : /* load variable */
312 var_name = byte(&pc);
313 if ((var_name & 0x80) != 0)
314 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
315 load_var (var_name);
316 break;
317
318 case 'n' : /* Negate top of stack. */
319 bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num);
320 break;
321
322 case 'p' : /* Pop the execution stack. */
323 pop ();
324 break;
325
326 case 's' : /* store variable */
327 var_name = byte(&pc);
328 if ((var_name & 0x80) != 0)
329 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
330 store_var (var_name);
331 break;
332
333 case 'w' : /* Write a string to the output. */
334 while ((ch = byte(&pc)) != '"') out_char (ch);
335 if (interactive) fflush (stdout);
336 break;
337
338 case 'x' : /* Exchange Top of Stack with the one under the tos. */
339 if (check_stack(2)) {
340 bc_num temp = ex_stack->s_num;
341 ex_stack->s_num = ex_stack->s_next->s_num;
342 ex_stack->s_next->s_num = temp;
343 }
344 break;
345
346 case '0' : /* Load Constant 0. */
347 push_copy (_zero_);
348 break;
349
350 case '1' : /* Load Constant 0. */
351 push_copy (_one_);
352 break;
353
354 case '!' : /* Negate the boolean value on top of the stack. */
355 c_code = is_zero (ex_stack->s_num);
356 assign (c_code);
357 break;
358
359 case '&' : /* compare greater than */
360 if (check_stack(2))
361 {
362 c_code = !is_zero (ex_stack->s_next->s_num)
363 && !is_zero (ex_stack->s_num);
364 pop ();
365 assign (c_code);
366 }
367 break;
368
369 case '|' : /* compare greater than */
370 if (check_stack(2))
371 {
372 c_code = !is_zero (ex_stack->s_next->s_num)
373 || !is_zero (ex_stack->s_num);
374 pop ();
375 assign (c_code);
376 }
377 break;
378
379 case '+' : /* add */
380 if (check_stack(2))
381 {
382 bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
383 pop();
384 pop();
385 push_num (temp_num);
386 init_num (&temp_num);
387 }
388 break;
389
390 case '-' : /* subtract */
391 if (check_stack(2))
392 {
393 bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
394 pop();
395 pop();
396 push_num (temp_num);
397 init_num (&temp_num);
398 }
399 break;
400
401 case '*' : /* multiply */
402 if (check_stack(2))
403 {
404 bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
405 &temp_num, scale);
406 pop();
407 pop();
408 push_num (temp_num);
409 init_num (&temp_num);
410 }
411 break;
412
413 case '/' : /* divide */
414 if (check_stack(2))
415 {
416 if (bc_divide (ex_stack->s_next->s_num,
417 ex_stack->s_num, &temp_num, scale) == 0)
418 {
419 pop();
420 pop();
421 push_num (temp_num);
422 init_num (&temp_num);
423 }
424 else
425 rt_error ("Divide by zero");
426 }
427 break;
428
429 case '%' : /* remainder */
430 if (check_stack(2))
431 {
432 if (is_zero (ex_stack->s_num))
433 rt_error ("Modulo by zero");
434 else
435 {
436 bc_modulo (ex_stack->s_next->s_num,
437 ex_stack->s_num, &temp_num, scale);
438 pop();
439 pop();
440 push_num (temp_num);
441 init_num (&temp_num);
442 }
443 }
444 break;
445
446 case '^' : /* raise */
447 if (check_stack(2))
448 {
449 bc_raise (ex_stack->s_next->s_num,
450 ex_stack->s_num, &temp_num, scale);
451 if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num))
452 rt_error ("divide by zero");
453 pop();
454 pop();
455 push_num (temp_num);
456 init_num (&temp_num);
457 }
458 break;
459
460 case '=' : /* compare equal */
461 if (check_stack(2))
462 {
463 c_code = bc_compare (ex_stack->s_next->s_num,
464 ex_stack->s_num) == 0;
465 pop ();
466 assign (c_code);
467 }
468 break;
469
470 case '#' : /* compare not equal */
471 if (check_stack(2))
472 {
473 c_code = bc_compare (ex_stack->s_next->s_num,
474 ex_stack->s_num) != 0;
475 pop ();
476 assign (c_code);
477 }
478 break;
479
480 case '<' : /* compare less than */
481 if (check_stack(2))
482 {
483 c_code = bc_compare (ex_stack->s_next->s_num,
484 ex_stack->s_num) == -1;
485 pop ();
486 assign (c_code);
487 }
488 break;
489
490 case '{' : /* compare less than or equal */
491 if (check_stack(2))
492 {
493 c_code = bc_compare (ex_stack->s_next->s_num,
494 ex_stack->s_num) <= 0;
495 pop ();
496 assign (c_code);
497 }
498 break;
499
500 case '>' : /* compare greater than */
501 if (check_stack(2))
502 {
503 c_code = bc_compare (ex_stack->s_next->s_num,
504 ex_stack->s_num) == 1;
505 pop ();
506 assign (c_code);
507 }
508 break;
509
510 case '}' : /* compare greater than or equal */
511 if (check_stack(2))
512 {
513 c_code = bc_compare (ex_stack->s_next->s_num,
514 ex_stack->s_num) >= 0;
515 pop ();
516 assign (c_code);
517 }
518 break;
519
520 default : /* error! */
521 rt_error ("bad instruction: inst=%c", inst);
522 }
523 }
524
525 /* Clean up the function stack and pop all autos/parameters. */
526 while (pc.pc_func != 0)
527 {
528 pop_vars(functions[pc.pc_func].f_autos);
529 pop_vars(functions[pc.pc_func].f_params);
530 fpop ();
531 pc.pc_addr = fpop ();
532 pc.pc_func = fpop ();
533 }
534
535 /* Clean up the execution stack. */
536 while (ex_stack != NULL) pop();
537
538 /* Clean up the interrupt stuff. */
539 if (interactive)
540 {
541 signal (SIGINT, use_quit);
542 if (had_sigint)
543 printf ("Interruption completed.\n");
544 }
545}
546
547
548/* Prog_char gets another byte from the program. It is used for
549 conversion of text constants in the code to numbers. */
550
551char
552prog_char ()
553{
554 return byte(&pc);
555}
556
557
558/* Read a character from the standard input. This function is used
559 by the "read" function. */
560
561char
562input_char ()
563{
564 char in_ch;
565
566 /* Get a character from the standard input for the read function. */
567 in_ch = getchar();
568
569 /* Check for a \ quoted newline. */
570 if (in_ch == '\\')
571 {
572 in_ch = getchar();
573 if (in_ch == '\n')
574 in_ch = getchar();
575 }
576
577 /* Classify and preprocess the input character. */
578 if (isdigit(in_ch))
579 return (in_ch - '0');
580 if (in_ch >= 'A' && in_ch <= 'F')
581 return (in_ch + 10 - 'A');
582 if (in_ch >= 'a' && in_ch <= 'f')
583 return (in_ch + 10 - 'a');
584 if (in_ch == '.' || in_ch == '+' || in_ch == '-')
585 return (in_ch);
586 if (in_ch <= ' ')
587 return (' ');
588
589 return (':');
590}
591
592
593/* Push_constant converts a sequence of input characters as returned
594 by IN_CHAR into a number. The number is pushed onto the execution
595 stack. The number is converted as a number in base CONV_BASE. */
596
597void
598push_constant (in_char, conv_base)
599 char (*in_char)(VOID);
600 int conv_base;
601{
602 int digits;
603 bc_num build, temp, result, mult, divisor;
604 char in_ch, first_ch;
605 char negative;
606
607 /* Initialize all bc numbers */
608 init_num (&temp);
609 init_num (&result);
610 init_num (&mult);
611 build = copy_num (_zero_);
612 negative = FALSE;
613
614 /* The conversion base. */
615 int2num (&mult, conv_base);
616
617 /* Get things ready. */
618 in_ch = in_char();
619 while (in_ch == ' ')
620 in_ch = in_char();
621
622 if (in_ch == '+')
623 in_ch = in_char();
624 else
625 if (in_ch == '-')
626 {
627 negative = TRUE;
628 in_ch = in_char();
629 }
630
631 /* Check for the special case of a single digit. */
632 if (in_ch < 16)
633 {
634 first_ch = in_ch;
635 in_ch = in_char();
636 if (in_ch < 16 && first_ch >= conv_base)
637 first_ch = conv_base - 1;
638 int2num (&build, (int) first_ch);
639 }
640
641 /* Convert the integer part. */
642 while (in_ch < 16)
643 {
644 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
645 bc_multiply (build, mult, &result, 0);
646 int2num (&temp, (int) in_ch);
647 bc_add (result, temp, &build);
648 in_ch = in_char();
649 }
650 if (in_ch == '.')
651 {
652 in_ch = in_char();
653 if (in_ch >= conv_base) in_ch = conv_base-1;
654 free_num (&result);
655 free_num (&temp);
656 divisor = copy_num (_one_);
657 result = copy_num (_zero_);
658 digits = 0;
659 while (in_ch < 16)
660 {
661 bc_multiply (result, mult, &result, 0);
662 int2num (&temp, (int) in_ch);
663 bc_add (result, temp, &result);
664 bc_multiply (divisor, mult, &divisor, 0);
665 digits++;
666 in_ch = in_char();
667 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
668 }
669 bc_divide (result, divisor, &result, digits);
670 bc_add (build, result, &build);
671 }
672
673 /* Final work. */
674 if (negative)
675 bc_sub (_zero_, build, &build);
676
677 push_num (build);
678 free_num (&temp);
679 free_num (&result);
680 free_num (&mult);
681}
682
683
684/* When converting base 10 constants from the program, we use this
685 more efficient way to convert them to numbers. PC tells where
686 the constant starts and is expected to be advanced to after
687 the constant. */
688
689void
690push_b10_const (pc)
691 program_counter *pc;
692{
693 bc_num build;
694 program_counter look_pc;
695 int kdigits, kscale;
696 char inchar;
697 char *ptr;
698
699 /* Count the digits and get things ready. */
700 look_pc = *pc;
701 kdigits = 0;
702 kscale = 0;
703 inchar = byte (&look_pc);
704 while (inchar != '.' && inchar != ':')
705 {
706 kdigits++;
707 inchar = byte(&look_pc);
708 }
709 if (inchar == '.' )
710 {
711 inchar = byte(&look_pc);
712 while (inchar != ':')
713 {
714 kscale++;
715 inchar = byte(&look_pc);
716 }
717 }
718
719 /* Get the first character again and move the pc. */
720 inchar = byte(pc);
721
722 /* Secial cases of 0, 1, and A-F single inputs. */
723 if (kdigits == 1 && kscale == 0)
724 {
725 if (inchar == 0)
726 {
727 push_copy (_zero_);
728 inchar = byte(pc);
729 return;
730 }
731 if (inchar == 1) {
732 push_copy (_one_);
733 inchar = byte(pc);
734 return;
735 }
736 if (inchar > 9)
737 {
738 init_num (&build);
739 int2num (&build, inchar);
740 push_num (build);
741 inchar = byte(pc);
742 return;
743 }
744 }
745
746 /* Build the new number. */
747 if (kdigits == 0)
748 {
749 build = new_num (1,kscale);
750 ptr = build->n_value;
751 *ptr++ = 0;
752 }
753 else
754 {
755 build = new_num (kdigits,kscale);
756 ptr = build->n_value;
757 }
758
759 while (inchar != ':')
760 {
761 if (inchar != '.')
762 if (inchar > 9)
763 *ptr++ = 9;
764 else
765 *ptr++ = inchar;
766 inchar = byte(pc);
767 }
768 push_num (build);
769}
770
771
772/* Put the correct value on the stack for C_CODE. Frees TOS num. */
773
774void
775assign (c_code)
776 char c_code;
777{
778 free_num (&ex_stack->s_num);
779 if (c_code)
780 ex_stack->s_num = copy_num (_one_);
781 else
782 ex_stack->s_num = copy_num (_zero_);
783}
Note: See TracBrowser for help on using the repository browser.