5761892 [rkeene@sledge /home/rkeene/devel/old/bc-dos/bc]$ cat -n execute.c
  1 /* execute.c - run a bc program. */
  2 
  3 /*  This file is part of GNU bc.
  4     Copyright (C) 1991, 1992, 1993, 1994, 1997 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 
 37 int had_sigint;
 38 
 39 void
 40 stop_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 
 51 unsigned char
 52 byte (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 
 65 void
 66 execute ()
 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_schar (ch);
210       else
211         {
212           ch = byte(&pc);
213           if (ch == '"') break;
214           switch (ch)
215         {
216         case 'a':  out_schar (007); break;
217         case 'b':  out_schar ('\b'); break;
218         case 'f':  out_schar ('\f'); break;
219         case 'n':  out_schar ('\n'); break;
220         case 'q':  out_schar ('"'); break;
221         case 'r':  out_schar ('\r'); break;
222         case 't':  out_schar ('\t'); break;
223         case '\\': out_schar ('\\'); break;
224         default:  break;
225         }
226         }
227     fflush (stdout);
228     break;
229 
230       case 'R' : /* Return from function */
231     if (pc.pc_func != 0)
232       {
233         /* "Pop" autos and parameters. */
234         pop_vars(functions[pc.pc_func].f_autos);
235         pop_vars(functions[pc.pc_func].f_params);
236         /* reset the pc. */
237         fpop ();
238         pc.pc_addr = fpop ();
239         pc.pc_func = fpop ();
240       }
241     else
242       rt_error ("Return from main program.");
243     break;
244 
245       case 'S' : /* store array variable */
246     var_name = byte(&pc);
247     if ((var_name & 0x80) != 0)
248       var_name = ((var_name << 8) & 0x7f) + byte(&pc);
249     store_array (var_name);
250     break;
251 
252       case 'T' : /* Test tos for zero */
253     c_code = is_zero (ex_stack->s_num);
254     assign (c_code);
255     break;
256 
257       case 'W' : /* Write the value on the top of the stack. */
258       case 'P' : /* Write the value on the top of the stack.  No newline. */
259     out_num (ex_stack->s_num, o_base, out_char);
260     if (inst == 'W') out_char ('\n');
261     store_var (4);  /* Special variable "last". */
262     fflush (stdout);
263     pop ();
264     break;
265 
266       case 'c' : /* Call special function. */
267     new_func = byte(&pc);
268 
269       switch (new_func)
270     {
271     case 'L':  /* Length function. */
272       /* For the number 0.xxxx,  0 is not significant. */
273       if (ex_stack->s_num->n_len == 1 &&
274           ex_stack->s_num->n_scale != 0 &&
275           ex_stack->s_num->n_value[0] == 0 )
276         int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
277       else
278         int2num (&ex_stack->s_num, ex_stack->s_num->n_len
279              + ex_stack->s_num->n_scale);
280       break;
281         
282     case 'S':  /* Scale function. */ 
283       int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
284       break;
285 
286     case 'R':  /* Square Root function. */
287       if (!bc_sqrt (&ex_stack->s_num, scale))
288         rt_error ("Square root of a negative number");
289       break;
290 
291     case 'I': /* Read function. */
292       push_constant (input_char, i_base);
293       break;
294     }
295     break;
296 
297       case 'd' : /* Decrement number */
298     var_name = byte(&pc);
299     if ((var_name & 0x80) != 0)
300       var_name = ((var_name << 8) & 0x7f) + byte(&pc);
301     decr_var (var_name);
302     break;
303       
304       case 'h' : /* Halt the machine. */
305     exit (0);
306 
307       case 'i' : /* increment number */
308     var_name = byte(&pc);
309     if ((var_name & 0x80) != 0)
310       var_name = ((var_name << 8) & 0x7f) + byte(&pc);
311     incr_var (var_name);
312     break;
313 
314       case 'l' : /* load variable */
315     var_name = byte(&pc);
316     if ((var_name & 0x80) != 0)
317       var_name = ((var_name << 8) & 0x7f) + byte(&pc);
318     load_var (var_name);
319     break;
320 
321       case 'n' : /* Negate top of stack. */
322     bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num, 0);
323     break;
324 
325       case 'p' : /* Pop the execution stack. */
326     pop ();
327     break;
328 
329       case 's' : /* store variable */
330     var_name = byte(&pc);
331     if ((var_name & 0x80) != 0)
332       var_name = ((var_name << 8) & 0x7f) + byte(&pc);
333     store_var (var_name);
334     break;
335 
336       case 'w' : /* Write a string to the output. */
337     while ((ch = byte(&pc)) != '"') out_schar (ch);
338     fflush (stdout);
339     break;
340            
341       case 'x' : /* Exchange Top of Stack with the one under the tos. */
342     if (check_stack(2)) {
343       bc_num temp = ex_stack->s_num;
344       ex_stack->s_num = ex_stack->s_next->s_num;
345       ex_stack->s_next->s_num = temp;
346     }
347     break;
348 
349       case '0' : /* Load Constant 0. */
350     push_copy (_zero_);
351     break;
352 
353       case '1' : /* Load Constant 0. */
354     push_copy (_one_);
355     break;
356 
357       case '!' : /* Negate the boolean value on top of the stack. */
358     c_code = is_zero (ex_stack->s_num);
359     assign (c_code);
360     break;
361 
362       case '&' : /* compare greater than */
363     if (check_stack(2))
364       {
365         c_code = !is_zero (ex_stack->s_next->s_num)
366           && !is_zero (ex_stack->s_num);
367         pop ();
368         assign (c_code);
369       }
370     break;
371 
372       case '|' : /* compare greater than */
373     if (check_stack(2))
374       {
375         c_code = !is_zero (ex_stack->s_next->s_num)
376           || !is_zero (ex_stack->s_num);
377         pop ();
378         assign (c_code);
379       }
380     break;
381 
382       case '+' : /* add */
383     if (check_stack(2))
384       {
385         bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
386         pop();
387         pop();
388         push_num (temp_num);
389         init_num (&temp_num);
390       }
391     break;
392 
393       case '-' : /* subtract */
394     if (check_stack(2))
395       {
396         bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
397         pop();
398         pop();
399         push_num (temp_num);
400         init_num (&temp_num);
401       }
402     break;
403 
404       case '*' : /* multiply */
405     if (check_stack(2))
406       {
407         bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
408              &temp_num, scale);
409         pop();
410         pop();
411         push_num (temp_num);
412         init_num (&temp_num);
413       }
414     break;
415 
416       case '/' : /* divide */
417     if (check_stack(2))
418       {
419         if (bc_divide (ex_stack->s_next->s_num,
420                ex_stack->s_num, &temp_num, scale) == 0)
421           {
422         pop();
423         pop();
424         push_num (temp_num);
425         init_num (&temp_num);
426           }
427         else
428           rt_error ("Divide by zero");
429       }
430     break;
431 
432       case '%' : /* remainder */
433     if (check_stack(2))
434       {
435         if (is_zero (ex_stack->s_num))
436           rt_error ("Modulo by zero");
437         else
438           {
439         bc_modulo (ex_stack->s_next->s_num,
440                ex_stack->s_num, &temp_num, scale);
441         pop();
442         pop();
443         push_num (temp_num);
444         init_num (&temp_num);
445           }
446       }
447     break;
448 
449       case '^' : /* raise */
450     if (check_stack(2))
451       {
452         bc_raise (ex_stack->s_next->s_num,
453               ex_stack->s_num, &temp_num, scale);
454         if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num))
455           rt_error ("divide by zero");
456         pop();
457         pop();
458         push_num (temp_num);
459         init_num (&temp_num);
460       }
461     break;
462 
463       case '=' : /* compare equal */
464     if (check_stack(2))
465       {
466         c_code = bc_compare (ex_stack->s_next->s_num,
467                  ex_stack->s_num) == 0;
468         pop ();
469         assign (c_code);
470       }
471     break;
472 
473       case '#' : /* compare not equal */
474     if (check_stack(2))
475       {
476         c_code = bc_compare (ex_stack->s_next->s_num,
477                  ex_stack->s_num) != 0;
478         pop ();
479         assign (c_code);
480       }
481     break;
482 
483       case '<' : /* compare less than */
484     if (check_stack(2))
485       {
486         c_code = bc_compare (ex_stack->s_next->s_num,
487                  ex_stack->s_num) == -1;
488         pop ();
489         assign (c_code);
490       }
491     break;
492 
493       case '{' : /* compare less than or equal */
494     if (check_stack(2))
495       {
496         c_code = bc_compare (ex_stack->s_next->s_num,
497                  ex_stack->s_num) <= 0;
498         pop ();
499         assign (c_code);
500       }
501     break;
502 
503       case '>' : /* compare greater than */
504     if (check_stack(2))
505       {
506         c_code = bc_compare (ex_stack->s_next->s_num,
507                  ex_stack->s_num) == 1;
508         pop ();
509         assign (c_code);
510       }
511     break;
512 
513       case '}' : /* compare greater than or equal */
514     if (check_stack(2))
515       {
516         c_code = bc_compare (ex_stack->s_next->s_num,
517                  ex_stack->s_num) >= 0;
518         pop ();
519         assign (c_code);
520       }
521     break;
522 
523     default  : /* error! */
524       rt_error ("bad instruction: inst=%c", inst);
525       }
526     }
527 
528   /* Clean up the function stack and pop all autos/parameters. */
529   while (pc.pc_func != 0)
530     {
531       pop_vars(functions[pc.pc_func].f_autos);
532       pop_vars(functions[pc.pc_func].f_params);
533       fpop ();
534       pc.pc_addr = fpop ();
535       pc.pc_func = fpop ();
536     }
537 
538   /* Clean up the execution stack. */ 
539   while (ex_stack != NULL) pop();
540 
541   /* Clean up the interrupt stuff. */
542   if (interactive)
543     {
544       signal (SIGINT, use_quit);
545       if (had_sigint)
546     printf ("Interruption completed.\n");
547     }
548 }
549 
550 
551 /* Prog_char gets another byte from the program.  It is used for
552    conversion of text constants in the code to numbers. */
553 
554 char
555 prog_char ()
556 {
557   return byte(&pc);
558 }
559 
560 
561 /* Read a character from the standard input.  This function is used
562    by the "read" function. */
563 
564 char
565 input_char ()
566 {
567   char in_ch;
568   
569   /* Get a character from the standard input for the read function. */
570   in_ch = getchar();
571 
572   /* Check for a \ quoted newline. */
573   if (in_ch == '\\')
574     {
575       in_ch = getchar();
576       if (in_ch == '\n')
577     in_ch = getchar();
578     }
579 
580   /* Classify and preprocess the input character. */
581   if (isdigit(in_ch))
582     return (in_ch - '0');
583   if (in_ch >= 'A' && in_ch <= 'F')
584     return (in_ch + 10 - 'A');
585   if (in_ch >= 'a' && in_ch <= 'f')
586     return (in_ch + 10 - 'a');
587   if (in_ch == '.' || in_ch == '+' || in_ch == '-')
588     return (in_ch);
589   if (in_ch <= ' ')
590     return (' ');
591   
592   return (':');
593 }
594 
595 
596 /* Push_constant converts a sequence of input characters as returned
597    by IN_CHAR into a number.  The number is pushed onto the execution
598    stack.  The number is converted as a number in base CONV_BASE. */
599 
600 void
601 push_constant (in_char, conv_base)
602    char (*in_char)(VOID);
603    int conv_base;
604 {
605   int digits;
606   bc_num build, temp, result, mult, divisor;
607   char  in_ch, first_ch;
608   char  negative;
609 
610   /* Initialize all bc numbers */
611   init_num (&temp);
612   init_num (&result);
613   init_num (&mult);
614   build = copy_num (_zero_);
615   negative = FALSE;
616 
617   /* The conversion base. */
618   int2num (&mult, conv_base);
619   
620   /* Get things ready. */
621   in_ch = in_char();
622   while (in_ch == ' ')
623     in_ch = in_char();
624 
625   if (in_ch == '+')
626     in_ch = in_char();
627   else
628     if (in_ch == '-')
629       {
630     negative = TRUE;
631     in_ch = in_char();
632       }
633 
634   /* Check for the special case of a single digit. */
635   if (in_ch < 16)
636     {
637       first_ch = in_ch;
638       in_ch = in_char();
639       if (in_ch < 16 && first_ch >= conv_base)
640     first_ch = conv_base - 1;
641       int2num (&build, (int) first_ch);
642     }
643 
644   /* Convert the integer part. */
645   while (in_ch < 16)
646     {
647       if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
648       bc_multiply (build, mult, &result, 0);
649       int2num (&temp, (int) in_ch);
650       bc_add (result, temp, &build, 0);
651       in_ch = in_char();
652     }
653   if (in_ch == '.')
654     {
655       in_ch = in_char();
656       if (in_ch >= conv_base) in_ch = conv_base-1;
657       free_num (&result);
658       free_num (&temp);
659       divisor = copy_num (_one_);
660       result = copy_num (_zero_);
661       digits = 0;
662       while (in_ch < 16)
663     {
664       bc_multiply (result, mult, &result, 0);
665       int2num (&temp, (int) in_ch);
666       bc_add (result, temp, &result, 0);
667       bc_multiply (divisor, mult, &divisor, 0);
668       digits++;
669       in_ch = in_char();
670       if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
671     }
672       bc_divide (result, divisor, &result, digits);
673       bc_add (build, result, &build, 0);
674     }
675   
676   /* Final work.  */
677   if (negative)
678     bc_sub (_zero_, build, &build, 0);
679 
680   push_num (build);
681   free_num (&temp);
682   free_num (&result);
683   free_num (&mult);
684 }
685 
686 
687 /* When converting base 10 constants from the program, we use this
688    more efficient way to convert them to numbers.  PC tells where
689    the constant starts and is expected to be advanced to after
690    the constant. */
691 
692 void
693 push_b10_const (pc)
694      program_counter *pc;
695 {
696   bc_num build;
697   program_counter look_pc;
698   int kdigits, kscale;
699   char inchar;
700   char *ptr;
701   
702   /* Count the digits and get things ready. */
703   look_pc = *pc;
704   kdigits = 0;
705   kscale  = 0;
706   inchar = byte (&look_pc);
707   while (inchar != '.' && inchar != ':')
708     {
709       kdigits++;
710       inchar = byte(&look_pc);
711     }
712   if (inchar == '.' )
713     {
714       inchar = byte(&look_pc);
715       while (inchar != ':')
716     {
717       kscale++;
718       inchar = byte(&look_pc);
719     }
720     }
721 
722   /* Get the first character again and move the pc. */
723   inchar = byte(pc);
724   
725   /* Secial cases of 0, 1, and A-F single inputs. */
726   if (kdigits == 1 && kscale == 0)
727     {
728       if (inchar == 0)
729     {
730       push_copy (_zero_);
731       inchar = byte(pc);
732       return;
733     }
734       if (inchar == 1) {
735       push_copy (_one_);
736       inchar = byte(pc);
737       return;
738     }
739     if (inchar > 9)
740       {
741     init_num (&build);
742     int2num (&build, inchar);
743     push_num (build);
744     inchar = byte(pc);
745     return;
746       }
747     }
748 
749   /* Build the new number. */
750   if (kdigits == 0)
751     {
752       build = new_num (1,kscale);
753       ptr = build->n_value;
754       *ptr++ = 0;
755     }
756   else
757     {
758       build = new_num (kdigits,kscale);
759       ptr = build->n_value;
760     }
761 
762   while (inchar != ':')
763     {
764       if (inchar != '.')
765     if (inchar > 9)
766       *ptr++ = 9;
767     else
768       *ptr++ = inchar;
769       inchar = byte(pc);
770     }
771   push_num (build);
772 }
773 
774 
775 /* Put the correct value on the stack for C_CODE.  Frees TOS num. */
776 
777 void
778 assign (c_code)
779      char c_code;
780 {
781   free_num (&ex_stack->s_num);
782   if (c_code)
783     ex_stack->s_num = copy_num (_one_);
784   else
785     ex_stack->s_num = copy_num (_zero_);
786 }
5761893 [rkeene@sledge /home/rkeene/devel/old/bc-dos/bc]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 1997-04-10 21:35:08