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