1 /* execute.c - run a bc program. */
3 /* This file is part of bc written for MINIX.
4 Copyright (C) 1991, 1992 Free Software Foundation, Inc.
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.
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.
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.
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
27 *************************************************************************/
35 /* The SIGINT interrupt handling routine. */
45 rt_error ("interrupted execution");
49 /* Get the current byte and advance the PC counter. */
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
]);
63 /* The routine that actually runs the machine. */
68 int label_num
, l_gp
, l_off
;
80 /* Initialize this run... */
83 runtime_error
= FALSE
;
86 /* Set up the interrupt mechanism for an interactive session. */
89 signal (SIGINT
, stop_execution
);
93 while (pc
.pc_addr
< functions
[pc
.pc_func
].f_code_size
&& !runtime_error
)
98 { /* Print out address and the stack before each instruction.*/
99 int depth
; estack_rec
*temp
= ex_stack
;
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
);
108 printf (" %d = ", depth
);
109 out_num (temp
->s_num
, 10, out_char
);
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
);
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
);
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
];
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
);
150 /* Check to make sure it is defined. */
151 if (!functions
[new_func
].f_defined
)
153 rt_error ("Function %s not defined.", f_names
[new_func
]);
157 /* Check and push parameters. */
158 process_params (&pc
, new_func
);
160 /* Push auto variables. */
161 for (auto_list
= functions
[new_func
].f_autos
;
163 auto_list
= auto_list
->next
)
164 auto_var (auto_list
->av_name
);
166 /* Push pc and ibase. */
171 /* Reset pc to start of function. */
172 pc
.pc_func
= new_func
;
176 case 'D' : /* Duplicate top of stack */
177 push_copy (ex_stack
->s_num
);
180 case 'K' : /* Push a constant */
181 /* Get the input base and convert it to a bc number. */
185 const_base
= fn_stack
->s_val
;
186 if (const_base
== 10)
187 push_b10_const (&pc
);
189 push_constant (prog_char
, const_base
);
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
);
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
);
206 case 'O' : /* Write a string to the output with processing. */
207 while ((ch
= byte(&pc
)) != '"')
213 if (ch
== '"') break;
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;
225 if (interactive
) fflush (stdout
);
228 case 'R' : /* Return from function */
231 /* "Pop" autos and parameters. */
232 pop_vars(functions
[pc
.pc_func
].f_autos
);
233 pop_vars(functions
[pc
.pc_func
].f_params
);
236 pc
.pc_addr
= fpop ();
237 pc
.pc_func
= fpop ();
240 rt_error ("Return from main program.");
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
);
250 case 'T' : /* Test tos for zero */
251 c_code
= is_zero (ex_stack
->s_num
);
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
);
263 case 'c' : /* Call special function. */
264 new_func
= byte(&pc
);
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
);
275 int2num (&ex_stack
->s_num
, ex_stack
->s_num
->n_len
276 + ex_stack
->s_num
->n_scale
);
279 case 'S': /* Scale function. */
280 int2num (&ex_stack
->s_num
, ex_stack
->s_num
->n_scale
);
283 case 'R': /* Square Root function. */
284 if (!bc_sqrt (&ex_stack
->s_num
, scale
))
285 rt_error ("Square root of a negative number");
288 case 'I': /* Read function. */
289 push_constant (input_char
, i_base
);
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
);
301 case 'h' : /* Halt the machine. */
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
);
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
);
318 case 'n' : /* Negate top of stack. */
319 bc_sub (_zero_
, ex_stack
->s_num
, &ex_stack
->s_num
);
322 case 'p' : /* Pop the execution stack. */
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
);
333 case 'w' : /* Write a string to the output. */
334 while ((ch
= byte(&pc
)) != '"') out_char (ch
);
335 if (interactive
) fflush (stdout
);
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
;
346 case '0' : /* Load Constant 0. */
350 case '1' : /* Load Constant 0. */
354 case '!' : /* Negate the boolean value on top of the stack. */
355 c_code
= is_zero (ex_stack
->s_num
);
359 case '&' : /* compare greater than */
362 c_code
= !is_zero (ex_stack
->s_next
->s_num
)
363 && !is_zero (ex_stack
->s_num
);
369 case '|' : /* compare greater than */
372 c_code
= !is_zero (ex_stack
->s_next
->s_num
)
373 || !is_zero (ex_stack
->s_num
);
382 bc_add (ex_stack
->s_next
->s_num
, ex_stack
->s_num
, &temp_num
);
386 init_num (&temp_num
);
390 case '-' : /* subtract */
393 bc_sub (ex_stack
->s_next
->s_num
, ex_stack
->s_num
, &temp_num
);
397 init_num (&temp_num
);
401 case '*' : /* multiply */
404 bc_multiply (ex_stack
->s_next
->s_num
, ex_stack
->s_num
,
409 init_num (&temp_num
);
413 case '/' : /* divide */
416 if (bc_divide (ex_stack
->s_next
->s_num
,
417 ex_stack
->s_num
, &temp_num
, scale
) == 0)
422 init_num (&temp_num
);
425 rt_error ("Divide by zero");
429 case '%' : /* remainder */
432 if (is_zero (ex_stack
->s_num
))
433 rt_error ("Modulo by zero");
436 bc_modulo (ex_stack
->s_next
->s_num
,
437 ex_stack
->s_num
, &temp_num
, scale
);
441 init_num (&temp_num
);
446 case '^' : /* raise */
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");
456 init_num (&temp_num
);
460 case '=' : /* compare equal */
463 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
464 ex_stack
->s_num
) == 0;
470 case '#' : /* compare not equal */
473 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
474 ex_stack
->s_num
) != 0;
480 case '<' : /* compare less than */
483 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
484 ex_stack
->s_num
) == -1;
490 case '{' : /* compare less than or equal */
493 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
494 ex_stack
->s_num
) <= 0;
500 case '>' : /* compare greater than */
503 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
504 ex_stack
->s_num
) == 1;
510 case '}' : /* compare greater than or equal */
513 c_code
= bc_compare (ex_stack
->s_next
->s_num
,
514 ex_stack
->s_num
) >= 0;
520 default : /* error! */
521 rt_error ("bad instruction: inst=%c", inst
);
525 /* Clean up the function stack and pop all autos/parameters. */
526 while (pc
.pc_func
!= 0)
528 pop_vars(functions
[pc
.pc_func
].f_autos
);
529 pop_vars(functions
[pc
.pc_func
].f_params
);
531 pc
.pc_addr
= fpop ();
532 pc
.pc_func
= fpop ();
535 /* Clean up the execution stack. */
536 while (ex_stack
!= NULL
) pop();
538 /* Clean up the interrupt stuff. */
541 signal (SIGINT
, use_quit
);
543 printf ("Interruption completed.\n");
548 /* Prog_char gets another byte from the program. It is used for
549 conversion of text constants in the code to numbers. */
558 /* Read a character from the standard input. This function is used
559 by the "read" function. */
566 /* Get a character from the standard input for the read function. */
569 /* Check for a \ quoted newline. */
577 /* Classify and preprocess the input character. */
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
== '-')
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. */
598 push_constant (in_char
, conv_base
)
599 char (*in_char
)(VOID
);
603 bc_num build
, temp
, result
, mult
, divisor
;
604 char in_ch
, first_ch
;
607 /* Initialize all bc numbers */
611 build
= copy_num (_zero_
);
614 /* The conversion base. */
615 int2num (&mult
, conv_base
);
617 /* Get things ready. */
631 /* Check for the special case of a single digit. */
636 if (in_ch
< 16 && first_ch
>= conv_base
)
637 first_ch
= conv_base
- 1;
638 int2num (&build
, (int) first_ch
);
641 /* Convert the integer part. */
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
);
653 if (in_ch
>= conv_base
) in_ch
= conv_base
-1;
656 divisor
= copy_num (_one_
);
657 result
= copy_num (_zero_
);
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);
667 if (in_ch
< 16 && in_ch
>= conv_base
) in_ch
= conv_base
-1;
669 bc_divide (result
, divisor
, &result
, digits
);
670 bc_add (build
, result
, &build
);
675 bc_sub (_zero_
, build
, &build
);
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
694 program_counter look_pc
;
699 /* Count the digits and get things ready. */
703 inchar
= byte (&look_pc
);
704 while (inchar
!= '.' && inchar
!= ':')
707 inchar
= byte(&look_pc
);
711 inchar
= byte(&look_pc
);
712 while (inchar
!= ':')
715 inchar
= byte(&look_pc
);
719 /* Get the first character again and move the pc. */
722 /* Secial cases of 0, 1, and A-F single inputs. */
723 if (kdigits
== 1 && kscale
== 0)
739 int2num (&build
, inchar
);
746 /* Build the new number. */
749 build
= new_num (1,kscale
);
750 ptr
= build
->n_value
;
755 build
= new_num (kdigits
,kscale
);
756 ptr
= build
->n_value
;
759 while (inchar
!= ':')
772 /* Put the correct value on the stack for C_CODE. Frees TOS num. */
778 free_num (&ex_stack
->s_num
);
780 ex_stack
->s_num
= copy_num (_one_
);
782 ex_stack
->s_num
= copy_num (_zero_
);