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