996
|
1 /* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
|
|
2 *
|
|
3 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
|
|
4 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
|
|
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 3 of the License, or
|
|
9 * (at the proxy's option) any later version. Arvid Norlander is a
|
|
10 * proxy who can decide which future versions of the GNU General Public
|
|
11 * License can be used.
|
|
12 *
|
|
13 * This program is distributed in the hope that it will be useful,
|
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 * GNU General Public License for more details.
|
|
17 *
|
|
18 * You should have received a copy of the GNU General Public License
|
|
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20 */
|
|
21
|
|
22 #include "global.h"
|
|
23 #include "interpreter.h"
|
|
24
|
|
25 #include "diagnostic.h"
|
|
26 #include "funge-space/funge-space.h"
|
|
27 #include "input.h"
|
|
28 #include "ip.h"
|
|
29 #include "settings.h"
|
|
30 #include "stack.h"
|
|
31 #include "vector.h"
|
|
32
|
|
33 #include "fingerprints/manager.h"
|
|
34
|
|
35 #include "instructions/execute.h"
|
|
36 #include "instructions/io.h"
|
|
37 #include "instructions/iterate.h"
|
|
38 #include "instructions/sysinfo.h"
|
|
39
|
|
40 #include <errno.h>
|
|
41 #include <stdbool.h>
|
|
42 #include <stdio.h>
|
|
43 #include <stdlib.h>
|
|
44 #include <string.h> /* strerror */
|
|
45
|
|
46 #ifdef HAVE_clock_gettime
|
|
47 # include <time.h>
|
|
48 #else
|
|
49 # include <sys/time.h>
|
|
50 #endif
|
|
51 #include <assert.h>
|
|
52
|
|
53 /**
|
|
54 * Either the IP or the IP list.
|
|
55 */
|
|
56 /*@{*/
|
|
57 #ifdef CONCURRENT_FUNGE
|
|
58 static ipList *IPList = NULL;
|
|
59 #else
|
|
60 static instructionPointer *IP = NULL;
|
|
61 #endif
|
|
62 /*@}*/
|
|
63
|
|
64 /**
|
|
65 * Print warning on unknown instruction if such warnings are enabled.
|
|
66 */
|
|
67 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
68 static inline void warn_unknown_instr(funge_cell opcode, instructionPointer * restrict ip)
|
|
69 {
|
|
70 if (FUNGE_UNLIKELY(setting_enable_warnings))
|
|
71 diag_warn_format("Unknown instruction at x=%" FUNGECELLPRI " y=%" FUNGECELLPRI ": %c (%" FUNGECELLPRI ")",
|
|
72 ip->position.x, ip->position.y, (char)opcode, opcode);
|
|
73 }
|
|
74
|
|
75 // These two are called from elsewhere. Avoid code duplication.
|
|
76
|
|
77 FUNGE_ATTR_FAST inline void if_east_west(instructionPointer * restrict ip)
|
|
78 {
|
|
79 if (stack_pop(ip->stack) == 0)
|
|
80 ip_go_east(ip);
|
|
81 else
|
|
82 ip_go_west(ip);
|
|
83 }
|
|
84
|
|
85 FUNGE_ATTR_FAST inline void if_north_south(instructionPointer * restrict ip)
|
|
86 {
|
|
87 if (stack_pop(ip->stack) == 0)
|
|
88 ip_go_south(ip);
|
|
89 else
|
|
90 ip_go_north(ip);
|
|
91 }
|
|
92
|
|
93 #ifdef CONCURRENT_FUNGE
|
|
94 # define ReturnFromexecute_instruction(x) return (x)
|
|
95 /// Return with value if we are concurrent
|
|
96 # define ReturnIfCon(x) return (x)
|
|
97 # define CON_RETTYPE bool
|
|
98 #else
|
|
99 # define ReturnFromexecute_instruction(x) return
|
|
100 # define CON_RETTYPE void
|
|
101 # define ReturnIfCon(x) (x); return
|
|
102 #endif
|
|
103
|
|
104 /// Generate a case for use in execute_instruction() that pushes a number on
|
|
105 /// the stack.
|
|
106 #define PUSHVAL(x, y) \
|
|
107 case (x): \
|
|
108 stack_push(ip->stack, (funge_cell)y); \
|
|
109 break;
|
|
110
|
|
111 /// This function handles string mode.
|
|
112 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
113 static inline CON_RETTYPE handle_string_mode(funge_cell opcode, instructionPointer * restrict ip)
|
|
114 {
|
|
115 if (opcode == '"') {
|
|
116 ip->mode = ipmCODE;
|
|
117 } else if (opcode != ' ') {
|
|
118 ip->stringLastWasSpace = false;
|
|
119 stack_push(ip->stack, opcode);
|
|
120 } else if (opcode == ' ') {
|
|
121 if ((!ip->stringLastWasSpace) || (setting_current_standard == stdver93)) {
|
|
122 ip->stringLastWasSpace = true;
|
|
123 stack_push(ip->stack, opcode);
|
|
124 // More than one space in string mode take no tick in concurrent Funge.
|
|
125 } else {
|
|
126 ReturnFromexecute_instruction(true);
|
|
127 }
|
|
128 }
|
|
129 ReturnFromexecute_instruction(false);
|
|
130 }
|
|
131
|
|
132 /// This function handles fingerprint instructions.
|
|
133 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
134 static inline void handle_fprint(funge_cell opcode, instructionPointer * restrict ip)
|
|
135 {
|
|
136 if (FUNGE_UNLIKELY(setting_disable_fingerprints)) {
|
|
137 warn_unknown_instr(opcode, ip);
|
|
138 ip_reverse(ip);
|
|
139 } else {
|
|
140 int_fast8_t entry = (int_fast8_t)(opcode - 'A');
|
|
141 if ((ip->fingerOpcodes[entry].top > 0)
|
|
142 && ip->fingerOpcodes[entry].entries[ip->fingerOpcodes[entry].top - 1]) {
|
|
143 // Call the fingerprint.
|
|
144 ip->fingerOpcodes[entry].entries[ip->fingerOpcodes[entry].top - 1](ip);
|
|
145 } else {
|
|
146 warn_unknown_instr(opcode, ip);
|
|
147 ip_reverse(ip);
|
|
148 }
|
|
149 }
|
|
150 }
|
|
151
|
|
152 #ifdef CONCURRENT_FUNGE
|
|
153 FUNGE_ATTR_FAST CON_RETTYPE execute_instruction(funge_cell opcode, instructionPointer * restrict ip, ssize_t * threadindex)
|
|
154 #else
|
|
155 FUNGE_ATTR_FAST CON_RETTYPE execute_instruction(funge_cell opcode, instructionPointer * restrict ip)
|
|
156 #endif
|
|
157 {
|
|
158 // First check if we are in string mode, and do special stuff then.
|
|
159 if (ip->mode == ipmSTRING) {
|
|
160 ReturnIfCon(handle_string_mode(opcode, ip));
|
|
161 // Next: Is this a fingerprint opcode?
|
|
162 } else if ((opcode >= 'A') && (opcode <= 'Z')) {
|
|
163 handle_fprint(opcode, ip);
|
|
164 // OK a core instruction.
|
|
165 // Find what one and execute it.
|
|
166 } else {
|
|
167 switch (opcode) {
|
|
168 case ' ': {
|
|
169 do {
|
|
170 ip_forward(ip);
|
|
171 } while (fungespace_get(&ip->position) == ' ');
|
|
172 ip->needMove = false;
|
|
173 ReturnFromexecute_instruction(true);
|
|
174 }
|
|
175 case 'z':
|
|
176 break;
|
|
177 case ';': {
|
|
178 do {
|
|
179 ip_forward(ip);
|
|
180 } while (fungespace_get(&ip->position) != ';');
|
|
181 ReturnFromexecute_instruction(true);
|
|
182 }
|
|
183 case '^':
|
|
184 ip_go_north(ip);
|
|
185 break;
|
|
186 case '>':
|
|
187 ip_go_east(ip);
|
|
188 break;
|
|
189 case 'v':
|
|
190 ip_go_south(ip);
|
|
191 break;
|
|
192 case '<':
|
|
193 ip_go_west(ip);
|
|
194 break;
|
|
195 case 'j': {
|
|
196 // Currently need to do it like this or wrapping
|
|
197 // won't work for j.
|
|
198 funge_cell jumps = stack_pop(ip->stack);
|
|
199 ip_forward(ip);
|
|
200 if (jumps != 0) {
|
|
201 funge_vector tmp;
|
|
202 tmp.x = ip->delta.x;
|
|
203 tmp.y = ip->delta.y;
|
|
204 ip->delta.y *= jumps;
|
|
205 ip->delta.x *= jumps;
|
|
206 ip_forward(ip);
|
|
207 ip->delta.x = tmp.x;
|
|
208 ip->delta.y = tmp.y;
|
|
209 }
|
|
210 ip->needMove = false;
|
|
211 break;
|
|
212 }
|
|
213 case '?': {
|
|
214 // May not be perfectly uniform.
|
|
215 // If this matters for you, contact me (with a patch).
|
|
216 long int rnd = random() % 4;
|
|
217 switch (rnd) {
|
|
218 case 0: ip_go_north(ip); break;
|
|
219 case 1: ip_go_east(ip); break;
|
|
220 case 2: ip_go_south(ip); break;
|
|
221 case 3: ip_go_west(ip); break;
|
|
222 }
|
|
223 break;
|
|
224 }
|
|
225 case 'r':
|
|
226 ip_reverse(ip);
|
|
227 break;
|
|
228 case '[':
|
|
229 ip_turn_left(ip);
|
|
230 break;
|
|
231 case ']':
|
|
232 ip_turn_right(ip);
|
|
233 break;
|
|
234 case 'x': {
|
|
235 funge_vector pos = stack_pop_vector(ip->stack);
|
|
236 ip_set_delta(ip, &pos);
|
|
237 break;
|
|
238 }
|
|
239
|
|
240 PUSHVAL('0', 0)
|
|
241 PUSHVAL('1', 1)
|
|
242 PUSHVAL('2', 2)
|
|
243 PUSHVAL('3', 3)
|
|
244 PUSHVAL('4', 4)
|
|
245 PUSHVAL('5', 5)
|
|
246 PUSHVAL('6', 6)
|
|
247 PUSHVAL('7', 7)
|
|
248 PUSHVAL('8', 8)
|
|
249 PUSHVAL('9', 9)
|
|
250 PUSHVAL('a', 0xa)
|
|
251 PUSHVAL('b', 0xb)
|
|
252 PUSHVAL('c', 0xc)
|
|
253 PUSHVAL('d', 0xd)
|
|
254 PUSHVAL('e', 0xe)
|
|
255 PUSHVAL('f', 0xf)
|
|
256
|
|
257 case '"':
|
|
258 ip->mode = ipmSTRING;
|
|
259 ip->stringLastWasSpace = false;
|
|
260 break;
|
|
261 case ':':
|
|
262 stack_dup_top(ip->stack);
|
|
263 break;
|
|
264
|
|
265 case '#':
|
|
266 ip_forward(ip);
|
|
267 break;
|
|
268
|
|
269 case '_':
|
|
270 if_east_west(ip);
|
|
271 break;
|
|
272 case '|':
|
|
273 if_north_south(ip);
|
|
274 break;
|
|
275 case 'w': {
|
|
276 funge_cell a, b;
|
|
277 b = stack_pop(ip->stack);
|
|
278 a = stack_pop(ip->stack);
|
|
279 if (a < b)
|
|
280 ip_turn_left(ip);
|
|
281 else if (a > b)
|
|
282 ip_turn_right(ip);
|
|
283 break;
|
|
284 }
|
|
285 case 'k':
|
|
286 #ifdef CONCURRENT_FUNGE
|
|
287 run_iterate(ip, &IPList, threadindex, false);
|
|
288 #else
|
|
289 run_iterate(ip, false);
|
|
290 #endif
|
|
291 break;
|
|
292
|
|
293 case '-': {
|
|
294 funge_cell a, b;
|
|
295 b = stack_pop(ip->stack);
|
|
296 a = stack_pop(ip->stack);
|
|
297 stack_push(ip->stack, a - b);
|
|
298 break;
|
|
299 }
|
|
300 case '+': {
|
|
301 funge_cell a, b;
|
|
302 b = stack_pop(ip->stack);
|
|
303 a = stack_pop(ip->stack);
|
|
304 stack_push(ip->stack, a + b);
|
|
305 break;
|
|
306 }
|
|
307 case '*': {
|
|
308 funge_cell a, b;
|
|
309 b = stack_pop(ip->stack);
|
|
310 a = stack_pop(ip->stack);
|
|
311 stack_push(ip->stack, a * b);
|
|
312 break;
|
|
313 }
|
|
314 case '/': {
|
|
315 funge_cell a, b;
|
|
316 b = stack_pop(ip->stack);
|
|
317 a = stack_pop(ip->stack);
|
|
318 if (b == 0)
|
|
319 stack_push(ip->stack, 0);
|
|
320 else
|
|
321 stack_push(ip->stack, a / b);
|
|
322 break;
|
|
323 }
|
|
324 case '%': {
|
|
325 funge_cell a, b;
|
|
326 b = stack_pop(ip->stack);
|
|
327 a = stack_pop(ip->stack);
|
|
328 if (b == 0)
|
|
329 stack_push(ip->stack, 0);
|
|
330 else
|
|
331 stack_push(ip->stack, a % b);
|
|
332 break;
|
|
333 }
|
|
334
|
|
335 case '!':
|
|
336 stack_push(ip->stack, !stack_pop(ip->stack));
|
|
337 break;
|
|
338 case '`': {
|
|
339 funge_cell a, b;
|
|
340 b = stack_pop(ip->stack);
|
|
341 a = stack_pop(ip->stack);
|
|
342 stack_push(ip->stack, a > b);
|
|
343 break;
|
|
344 }
|
|
345
|
|
346 case 'g': {
|
|
347 funge_vector pos;
|
|
348 funge_cell a;
|
|
349 pos = stack_pop_vector(ip->stack);
|
|
350 a = fungespace_get_offset(&pos, &ip->storageOffset);
|
|
351 stack_push(ip->stack, a);
|
|
352 break;
|
|
353 }
|
|
354 case 'p': {
|
|
355 funge_vector pos;
|
|
356 funge_cell a;
|
|
357 pos = stack_pop_vector(ip->stack);
|
|
358 a = stack_pop(ip->stack);
|
|
359 fungespace_set_offset(a, &pos, &ip->storageOffset);
|
|
360 break;
|
|
361 }
|
|
362
|
|
363 case '\'':
|
|
364 ip_forward(ip);
|
|
365 stack_push(ip->stack, fungespace_get(&ip->position));
|
|
366 break;
|
|
367 case 's':
|
|
368 ip_forward(ip);
|
|
369 fungespace_set(stack_pop(ip->stack), &ip->position);
|
|
370 break;
|
|
371
|
|
372 case '$':
|
|
373 stack_discard(ip->stack, 1);
|
|
374 break;
|
|
375 case '\\':
|
|
376 stack_swap_top(ip->stack);
|
|
377 break;
|
|
378 case 'n':
|
|
379 stack_clear(ip->stack);
|
|
380 break;
|
|
381
|
|
382 case ',': {
|
|
383 funge_cell a = stack_pop(ip->stack);
|
|
384 // Reverse on failed output
|
|
385 if (FUNGE_UNLIKELY(cf_putchar_maybe_locked((int)a) != (unsigned char)a))
|
|
386 ip_reverse(ip);
|
|
387 break;
|
|
388 }
|
|
389 case '.':
|
|
390 // Reverse on failed output
|
|
391 if (FUNGE_UNLIKELY(printf("%" FUNGECELLPRI " ", stack_pop(ip->stack)) < 0))
|
|
392 ip_reverse(ip);
|
|
393 break;
|
|
394
|
|
395 case '~': {
|
|
396 funge_cell a;
|
|
397 if (input_getchar(&a)) {
|
|
398 stack_push(ip->stack, a);
|
|
399 } else {
|
|
400 ip_reverse(ip);
|
|
401 }
|
|
402 break;
|
|
403 }
|
|
404 case '&': {
|
|
405 funge_cell a;
|
|
406 ret_getint gotint = rgi_noint;
|
|
407 while (gotint == rgi_noint)
|
|
408 gotint = input_getint(&a, 10);
|
|
409 if (gotint == rgi_success) {
|
|
410 stack_push(ip->stack, a);
|
|
411 } else {
|
|
412 ip_reverse(ip);
|
|
413 }
|
|
414 break;
|
|
415 }
|
|
416
|
|
417 case 'y':
|
|
418 run_sys_info(ip);
|
|
419 break;
|
|
420
|
|
421 case '{': {
|
|
422 funge_cell count;
|
|
423 funge_vector pos;
|
|
424 count = stack_pop(ip->stack);
|
|
425 ip_forward(ip);
|
|
426 pos.x = ip->position.x;
|
|
427 pos.y = ip->position.y;
|
|
428 ip_backward(ip);
|
|
429 if (!stackstack_begin(ip, count, &pos))
|
|
430 ip_reverse(ip);
|
|
431 break;
|
|
432 }
|
|
433 case '}':
|
|
434 if (ip->stackstack->size == 1) {
|
|
435 ip_reverse(ip);
|
|
436 } else {
|
|
437 funge_cell count;
|
|
438 count = stack_pop(ip->stack);
|
|
439 if (!stackstack_end(ip, count))
|
|
440 ip_reverse(ip);
|
|
441 }
|
|
442 break;
|
|
443 case 'u':
|
|
444 if (ip->stackstack->size == 1) {
|
|
445 ip_reverse(ip);
|
|
446 } else {
|
|
447 funge_cell count;
|
|
448 count = stack_pop(ip->stack);
|
|
449 stackstack_transfer(count,
|
|
450 ip->stackstack->stacks[ip->stackstack->current],
|
|
451 ip->stackstack->stacks[ip->stackstack->current - 1]);
|
|
452 }
|
|
453 break;
|
|
454
|
|
455 case 'i':
|
|
456 run_file_input(ip);
|
|
457 break;
|
|
458 case 'o':
|
|
459 run_file_output(ip);
|
|
460 break;
|
|
461 case '=':
|
|
462 run_system_execute(ip);
|
|
463 break;
|
|
464
|
|
465 case '(':
|
|
466 case ')': {
|
|
467 // TODO: Handle Funge-109 style too.
|
|
468 funge_cell fpsize = stack_pop(ip->stack);
|
|
469 // Check for sanity (because we won't have any fingerprints
|
|
470 // outside such a range. This prevents long lockups here.
|
|
471 if (fpsize < 1) {
|
|
472 ip_reverse(ip);
|
|
473 } else if (FUNGE_UNLIKELY(setting_disable_fingerprints)) {
|
|
474 stack_discard(ip->stack, (size_t)fpsize);
|
|
475 ip_reverse(ip);
|
|
476 } else {
|
|
477 funge_cell fprint = 0;
|
|
478 if (FUNGE_UNLIKELY((fpsize > 8) && setting_enable_warnings)) {
|
|
479 diag_warn_format("WARN: %c (x=%" FUNGECELLPRI " y=%"
|
|
480 FUNGECELLPRI "): count is very large(%" FUNGECELLPRI
|
|
481 "), probably a bug.\n", (char)opcode,
|
|
482 ip->position.x, ip->position.y, fpsize);
|
|
483 }
|
|
484 while (fpsize--) {
|
|
485 fprint <<= 8;
|
|
486 fprint += stack_pop(ip->stack);
|
|
487 }
|
|
488 if (opcode == '(') {
|
|
489 if (!manager_load(ip, fprint))
|
|
490 ip_reverse(ip);
|
|
491 } else {
|
|
492 if (!manager_unload(ip, fprint))
|
|
493 ip_reverse(ip);
|
|
494 }
|
|
495 }
|
|
496 break;
|
|
497 }
|
|
498
|
|
499 #ifdef CONCURRENT_FUNGE
|
|
500 case 't':
|
|
501 *threadindex = iplist_duplicate_ip(&IPList, *threadindex);
|
|
502 break;
|
|
503
|
|
504 #endif /* CONCURRENT_FUNGE */
|
|
505
|
|
506 case '@':
|
|
507 #ifdef CONCURRENT_FUNGE
|
|
508 if (IPList->top == 0) {
|
|
509 fflush(stdout);
|
|
510 exit(0);
|
|
511 } else {
|
|
512 *threadindex = iplist_terminate_ip(&IPList, *threadindex);
|
|
513 //if (IPList->top == 0)
|
|
514 IPList->ips[*threadindex].needMove = false;
|
|
515 }
|
|
516 #else
|
|
517 exit(0);
|
|
518 #endif /* CONCURRENT_FUNGE */
|
|
519 break;
|
|
520
|
|
521 case 'q':
|
|
522 // We do the wrong thing here when fuzz testing to reduce false positives.
|
|
523 #ifdef FUZZ_TESTING
|
|
524 exit(0);
|
|
525 #else
|
|
526 exit((int)stack_pop(ip->stack));
|
|
527 #endif
|
|
528 break;
|
|
529
|
|
530 default:
|
|
531 warn_unknown_instr(opcode, ip);
|
|
532 ip_reverse(ip);
|
|
533 }
|
|
534 }
|
|
535 ReturnFromexecute_instruction(false);
|
|
536 }
|
|
537
|
|
538
|
|
539 #ifdef CONCURRENT_FUNGE
|
|
540 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
541 static inline void thread_forward(instructionPointer * restrict ip)
|
|
542 {
|
|
543 assert(ip != NULL);
|
|
544
|
|
545 if (ip->needMove)
|
|
546 ip_forward(ip);
|
|
547 else
|
|
548 ip->needMove = true;
|
|
549 }
|
|
550 #endif
|
|
551
|
|
552
|
|
553 FUNGE_ATTR_FAST FUNGE_ATTR_NORET
|
|
554 static inline void interpreter_main_loop(void)
|
|
555 {
|
|
556 #ifdef CONCURRENT_FUNGE
|
|
557 while (true) {
|
|
558 ssize_t i = IPList->top;
|
|
559 while (i >= 0) {
|
|
560 bool retval;
|
|
561 funge_cell opcode;
|
|
562
|
|
563 opcode = fungespace_get(&IPList->ips[i].position);
|
|
564 # ifndef DISABLE_TRACE
|
|
565 if (FUNGE_UNLIKELY(setting_trace_level != 0)) {
|
|
566 if (setting_trace_level > 8) {
|
|
567 fprintf(stderr, "tix=%zd tid=%" FUNGECELLPRI " x=%" FUNGECELLPRI " y=%" FUNGECELLPRI ": %c (%" FUNGECELLPRI ")\n",
|
|
568 i, IPList->ips[i].ID, IPList->ips[i].position.x,
|
|
569 IPList->ips[i].position.y, (char)opcode, opcode);
|
|
570 stack_print_top(IPList->ips[i].stack);
|
|
571 } else if (setting_trace_level > 3) {
|
|
572 fprintf(stderr, "tix=%zd tid=%" FUNGECELLPRI " x=%" FUNGECELLPRI " y=%" FUNGECELLPRI ": %c (%" FUNGECELLPRI ")\n",
|
|
573 i, IPList->ips[i].ID, IPList->ips[i].position.x,
|
|
574 IPList->ips[i].position.y, (char)opcode, opcode);
|
|
575 } else if (setting_trace_level > 2)
|
|
576 fprintf(stderr, "%c", (char)opcode);
|
|
577 }
|
|
578 # endif /* DISABLE_TRACE */
|
|
579
|
|
580 retval = execute_instruction(opcode, &IPList->ips[i], &i);
|
|
581 thread_forward(&IPList->ips[i]);
|
|
582 if (!retval)
|
|
583 i--;
|
|
584 }
|
|
585 }
|
|
586 #else /* CONCURRENT_FUNGE */
|
|
587 while (true) {
|
|
588 funge_cell opcode;
|
|
589
|
|
590 opcode = fungespace_get(&IP->position);
|
|
591 # ifndef DISABLE_TRACE
|
|
592 if (FUNGE_UNLIKELY(setting_trace_level != 0)) {
|
|
593 if (setting_trace_level > 8) {
|
|
594 fprintf(stderr, "x=%" FUNGECELLPRI " y=%" FUNGECELLPRI ": %c (%" FUNGECELLPRI ")\n",
|
|
595 IP->position.x, IP->position.y, (char)opcode, opcode);
|
|
596 stack_print_top(IP->stack);
|
|
597 } else if (setting_trace_level > 3) {
|
|
598 fprintf(stderr, "x=%" FUNGECELLPRI " y=%" FUNGECELLPRI ": %c (%" FUNGECELLPRI ")\n",
|
|
599 IP->position.x, IP->position.y, (char)opcode, opcode);
|
|
600 } else if (setting_trace_level > 2)
|
|
601 fprintf(stderr, "%c", (char)opcode);
|
|
602 }
|
|
603 # endif /* DISABLE_TRACE */
|
|
604
|
|
605 execute_instruction(opcode, IP);
|
|
606 if (IP->needMove)
|
|
607 ip_forward(IP);
|
|
608 else
|
|
609 IP->needMove = true;
|
|
610 }
|
|
611 #endif /* CONCURRENT_FUNGE */
|
|
612 }
|
|
613
|
|
614
|
|
615 #ifndef NDEBUG
|
|
616 // Used with debugging for freeing stuff at end of the program.
|
|
617 // Not needed, but useful to check that free functions works,
|
|
618 // and for detecting real memory leaks.
|
|
619 static void debug_free(void)
|
|
620 {
|
|
621 # ifdef CONCURRENT_FUNGE
|
|
622 iplist_free(IPList);
|
|
623 # else
|
|
624 ip_free(IP);
|
|
625 # endif
|
|
626 sysinfo_cleanup();
|
|
627 fungespace_free();
|
|
628 }
|
|
629 #endif
|
|
630
|
|
631 FUNGE_ATTR_FAST void interpreter_run(const char *filename)
|
|
632 {
|
|
633 if (FUNGE_UNLIKELY(!fungespace_create())) {
|
|
634 DIAG_FATAL_FORMAT_LOC("Couldn't create funge space: %s", strerror(errno));
|
|
635 }
|
|
636 #ifndef NDEBUG
|
|
637 atexit(&debug_free);
|
|
638 #endif
|
|
639 if (FUNGE_UNLIKELY(!fungespace_load(filename))) {
|
|
640 diag_fatal_format("Failed to process file \"%s\": %s", filename, strerror(errno));
|
|
641 }
|
|
642 #ifdef CONCURRENT_FUNGE
|
|
643 IPList = iplist_create();
|
|
644 if (FUNGE_UNLIKELY(IPList == NULL)) {
|
|
645 DIAG_FATAL_LOC("Couldn't create instruction pointer list!?");
|
|
646 }
|
|
647 #else
|
|
648 IP = ip_create();
|
|
649 if (FUNGE_UNLIKELY(IP == NULL)) {
|
|
650 DIAG_FATAL_LOC("Couldn't create instruction pointer!?");
|
|
651 }
|
|
652 #endif
|
|
653 {
|
|
654 #ifdef HAVE_clock_gettime
|
|
655 struct timespec tv;
|
|
656 if (FUNGE_UNLIKELY(clock_gettime(CLOCK_REALTIME, &tv))) {
|
|
657 diag_fatal_format("clock_gettime() failed (needed for random seed): %s", strerror(errno));
|
|
658 }
|
|
659 // Set up randomness
|
|
660 srandom((unsigned int)tv.tv_nsec);
|
|
661 #else
|
|
662 struct timeval tv;
|
|
663 if (FUNGE_UNLIKELY(gettimeofday(&tv, NULL))) {
|
|
664 diag_fatal_format("gettimeofday() failed (needed for random seed): %s", strerror(errno));
|
|
665 }
|
|
666 // Set up randomness
|
|
667 srandom((unsigned int)tv.tv_usec);
|
|
668 #endif
|
|
669 }
|
|
670 interpreter_main_loop();
|
|
671 }
|