1// This file is part of SmallBASIC
2//
3// byte-code executor
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#define BRUN_MODULE
11
12#include "config.h"
13
14#include "common/sys.h"
15#include "common/blib.h"
16#include "common/str.h"
17#include "common/fmt.h"
18#include "common/extlib.h"
19#include "common/units.h"
20#include "common/kw.h"
21#include "common/var.h"
22#include "common/scan.h"
23#include "common/smbas.h"
24#include "common/messages.h"
25#include "common/device.h"
26#include "common/pproc.h"
27#include "common/keymap.h"
28
29int brun_create_task(const char *filename, byte *preloaded_bc, int libf);
30int exec_close_task();
31void sys_before_comp();
32
33static char fileName[OS_FILENAME_SIZE + 1];
34static stknode_t err_node;
35
36#define EVT_CHECK_EVERY 50
37#define IF_ERR_BREAK if (prog_error) { \
38 if (prog_error == errThrow) \
39 prog_error = errNone; else break;}
40
41/**
42 * jump to label
43 */
44void code_jump_label(uint16_t label_id) {
45 prog_ip = tlab[label_id].ip;
46}
47
48/**
49 * Push a new node onto the stack
50 */
51stknode_t *code_push(code_t type) {
52 stknode_t *result;
53 if (prog_stack_count + 1 >= prog_stack_alloc) {
54 err_stackoverflow();
55 result = &err_node;
56 } else {
57 result = &prog_stack[prog_stack_count++];
58 result->type = type;
59 result->line = prog_line;
60 }
61 return result;
62}
63
64void free_node(stknode_t *node) {
65 switch (node->type) {
66 case kwTYPE_CRVAR:
67 // free local variable data and retore ptr
68 if (node->x.vdvar.vptr != tvar[node->x.vdvar.vid]) {
69 v_free(tvar[node->x.vdvar.vid]);
70 v_detach(tvar[node->x.vdvar.vid]);
71 tvar[node->x.vdvar.vid] = node->x.vdvar.vptr;
72 }
73 break;
74
75 case kwBYREF:
76 tvar[node->x.vdvar.vid] = node->x.vdvar.vptr;
77 break;
78
79 case kwTYPE_VAR:
80 if ((node->x.param.vcheck == 1) || (node->x.param.vcheck == 0x81)) {
81 v_free(node->x.param.res);
82 v_detach(node->x.param.res);
83 }
84 break;
85
86 case kwTYPE_RET:
87 v_free(node->x.vdvar.vptr); // free ret-var
88 v_detach(node->x.vdvar.vptr);
89 break;
90
91 case kwFUNC:
92 case kwPROC:
93 if (node->x.vcall.rvid != INVALID_ADDR) {
94 v_detach(tvar[node->x.vcall.rvid]);
95 tvar[node->x.vcall.rvid] = node->x.vcall.retvar;
96 }
97 break;
98
99 case kwFOR:
100 if (node->x.vfor.subtype == kwIN) {
101 if (node->x.vfor.flags & 1) {
102 // allocated in for
103 v_free(node->x.vfor.arr_ptr);
104 v_detach(node->x.vfor.arr_ptr);
105 }
106 }
107 break;
108
109 case kwSELECT:
110 v_free(node->x.vcase.var_ptr);
111 v_detach(node->x.vcase.var_ptr);
112 break;
113
114 case kwCATCH:
115 if (node->x.vcatch.catch_var != NULL) {
116 // clear the catch variable once out of scope
117 v_free(node->x.vcatch.catch_var);
118 }
119 break;
120
121 default:
122 break;
123 }
124}
125
126/**
127 * Returns and deletes the topmost node from stack (POP)
128 */
129void code_pop(stknode_t *node, int expected_type) {
130 if (prog_stack_count) {
131 prog_stack_count--;
132 if (node) {
133 *node = prog_stack[prog_stack_count];
134 }
135 if (node != NULL && expected_type != 0 && node->type != expected_type) {
136 free_node(node);
137 switch (node->type) {
138 case kwTYPE_RET:
139 // a FUNC result was not previously consumed
140 rt_raise(MSG_RETURN_NOT_ASSIGNED, node->line);
141 break;
142 case kwTYPE_CRVAR:
143 // pop local variable and continue
144 free_node(node);
145 code_pop(node, expected_type);
146 break;
147 default:
148 break;
149 }
150 }
151 } else {
152 if (node) {
153 err_stackunderflow();
154 node->type = 0xFF;
155 }
156 }
157}
158
159/**
160 * POPs and frees the topmost node from stack and returns the node type
161 */
162int code_pop_and_free() {
163 int type;
164 if (prog_stack_count) {
165 prog_stack_count--;
166 type = prog_stack[prog_stack_count].type;
167 free_node(&prog_stack[prog_stack_count]);
168 } else {
169 type = 0xFF;
170 }
171 return type;
172}
173
174/**
175 * Peek the topmost node of stack
176 */
177stknode_t *code_stackpeek() {
178 if (prog_stack_count) {
179 return &prog_stack[prog_stack_count - 1];
180 }
181 return NULL;
182}
183
184/**
185 * sets the value of an system-variable with the given type
186 */
187void setsysvar_var(int index, var_int_t value, int type) {
188 int tid;
189 int i;
190
191 tid = ctask->tid;
192 for (i = 0; i < count_tasks(); i++) {
193 activate_task(i);
194 if (ctask->has_sysvars) {
195 var_t *var_p = tvar[index];
196 var_p->type = type;
197 var_p->const_flag = 1;
198 var_p->v.i = value;
199 }
200 }
201 activate_task(tid);
202}
203
204/**
205 * sets the value of an integer system-variable
206 */
207void setsysvar_int(int index, var_int_t value) {
208 setsysvar_var(index, value, V_INT);
209}
210
211/**
212 * sets the value of a real system-variable
213 */
214void setsysvar_num(int index, var_num_t value) {
215 int tid;
216 int i;
217
218 tid = ctask->tid;
219 for (i = 0; i < count_tasks(); i++) {
220 activate_task(i);
221 if (ctask->has_sysvars) {
222 var_t *var_p = tvar[index];
223 var_p->type = V_NUM;
224 var_p->const_flag = 1;
225 var_p->v.n = value;
226 }
227 }
228 activate_task(tid);
229}
230
231/**
232 * sets the value of an string system-variable
233 */
234void setsysvar_str(int index, const char *value) {
235 int i;
236 int tid = ctask->tid;
237
238 for (i = 0; i < count_tasks(); i++) {
239 activate_task(i);
240 if (ctask->has_sysvars) {
241 var_t *var_p = tvar[index];
242 v_free(var_p);
243 v_createstr(var_p, value);
244 var_p->const_flag = 1;
245 }
246 }
247 activate_task(tid);
248}
249
250/**
251 * create predefined system variables for this task
252 */
253void exec_setup_predefined_variables() {
254 char homedir[OS_PATHNAME_SIZE + 1];
255 homedir[0] = '\0';
256
257 // needed here (otherwise task will not updated)
258 ctask->has_sysvars = 1;
259 setsysvar_str(SYSVAR_SBVER, SB_STR_VER);
260 setsysvar_num(SYSVAR_PI, M_PI);
261 setsysvar_int(SYSVAR_XMAX, os_graf_mx - 1);
262 setsysvar_int(SYSVAR_YMAX, os_graf_my - 1);
263 setsysvar_int(SYSVAR_TRUE, 1);
264 setsysvar_int(SYSVAR_FALSE, 0);
265 setsysvar_str(SYSVAR_CWD, dev_getcwd());
266 setsysvar_str(SYSVAR_COMMAND, opt_command);
267 setsysvar_int(SYSVAR_SELF, 0);
268 setsysvar_var(SYSVAR_NONE, 0, V_NIL);
269 setsysvar_int(SYSVAR_MAXINT, VAR_MAX_INT);
270
271#if defined(_ANDROID)
272 if (getenv("HOME_DIR")) {
273 strlcpy(homedir, getenv("HOME_DIR"), sizeof(homedir));
274 }
275#elif defined(_Win32)
276 if (getenv("HOMEPATH")) {
277 if (getenv("HOMEDRIVE")) {
278 strlcpy(homedir, getenv("HOMEDRIVE"), sizeof(homedir));
279 strlcat(homedir, getenv("HOMEPATH"), sizeof(homedir));
280 } else {
281 strlcpy(homedir, getenv("HOMEPATH"), sizeof(homedir));
282 }
283 } else {
284 GetModuleFileName(NULL, homedir, sizeof(homedir) - 1);
285 char *p = strrchr(homedir, '\\');
286 if (p) {
287 *p = '\0';
288 }
289 }
290 for (char *p = homedir; *p; p++) {
291 if (*p == '\\') {
292 *p = '/';
293 }
294 }
295#elif defined(_UnixOS)
296 if (getenv("HOME")) {
297 strlcpy(homedir, getenv("HOME"), sizeof(homedir));
298 } else {
299 strcpy(homedir, "/tmp/");
300 }
301#endif
302 // don't end with trailing slash
303 int l = strlen(homedir);
304 if (homedir[l - 1] == OS_DIRSEP) {
305 homedir[l - 1] = '\0';
306 }
307 setsysvar_str(SYSVAR_HOME, homedir);
308}
309
310/**
311 * BREAK
312 */
313void brun_stop() {
314 prog_error = errBreak;
315}
316
317/**
318 * returns the status of executor (runing or stopped)
319 */
320int brun_status() {
321 if (prog_error) {
322 return BRUN_STOPPED;
323 }
324 return BRUN_RUNNING;
325}
326
327/**
328 * BREAK - display message, too
329 */
330void brun_break() {
331 if (brun_status() == BRUN_RUNNING) {
332 inf_break(prog_line);
333 }
334 brun_stop();
335}
336
337/**
338 * CHAIN sb-source
339 */
340void cmd_chain(void) {
341 var_t var;
342 char *code = NULL;
343
344 v_init(&var);
345 eval(&var);
346
347 if (prog_error) {
348 v_free(&var);
349 return;
350 }
351
352 if (var.type == V_STR) {
353 if (access(var.v.p.ptr, R_OK) == 0) {
354 // argument is a file name
355 int h = open(var.v.p.ptr, O_BINARY | O_RDONLY);
356 if (h != -1) {
357 struct stat st;
358 if (fstat(h, &st) == 0) {
359 int len = st.st_size;
360 code = (char *)malloc(len + 1);
361 len = read(h, code, len);
362 code[len] = '\0';
363 }
364 close(h);
365 }
366 }
367 if (!code) {
368 code = strdup(var.v.p.ptr);
369 }
370 } else if (var.type == V_ARRAY) {
371 int len = 0;
372 uint32_t size = v_asize(&var);
373 for (int el = 0; el < size; el++) {
374 var_t *el_p = v_elem(&var, el);
375 if (el_p->type == V_STR) {
376 int str_len = strlen(el_p->v.p.ptr) + 2;
377 if (len) {
378 code = realloc(code, len + str_len);
379 strcat(code, el_p->v.p.ptr);
380 } else {
381 code = malloc(str_len);
382 strcpy(code, el_p->v.p.ptr);
383 }
384 strcat(code, "\n");
385 len += str_len + 1;
386 }
387 }
388 }
389
390 if (code == NULL) {
391 v_free(&var);
392 err_typemismatch();
393 return;
394 }
395
396 v_free(&var);
397
398 int tid_base = create_task("CH_BASE");
399 int tid_prev = activate_task(tid_base);
400
401 // compile the buffer
402 sys_before_comp();
403 int success = comp_compile_buffer(code);
404
405 free(code);
406 code = NULL;
407
408 if (success == 0) {
409 close_task(tid_base);
410 activate_task(tid_prev);
411 prog_error = errCompile;
412 return;
413 }
414
415 char last_file[OS_PATHNAME_SIZE + 1];
416 char bas_dir[OS_PATHNAME_SIZE + 1];
417 strcpy(last_file, gsb_last_file);
418 strcpy(bas_dir, gsb_bas_dir);
419
420 int tid_main = brun_create_task("CH_MAIN", ctask->bytecode, 0);
421 exec_sync_variables(0);
422
423 bc_loop(0);
424 success = prog_error; // save tid_main status
425 exec_close_task(); // cleanup task data - tid_main
426 close_task(tid_main); // cleanup task container
427 close_task(tid_base); // cleanup task container
428 activate_task(tid_prev); // resume calling task
429
430 // reset globals
431 gsb_last_line = 0;
432 gsb_last_error = 0;
433 strcpy(gsb_last_file, last_file);
434 strcpy(gsb_bas_dir, bas_dir);
435 strcpy(gsb_last_errmsg, "");
436
437 if (success == 0) {
438 prog_error = errRuntime;
439 }
440}
441
442/**
443 * RUN "program"
444 */
445void cmd_run(int retf) {
446 var_t var;
447 v_init(&var);
448 eval(&var);
449 IF_ERR_RETURN;
450
451 if (var.type != V_STR) {
452 err_typemismatch();
453 return;
454 } else {
455 strlcpy(fileName, var.v.p.ptr, sizeof(fileName));
456 v_free(&var);
457 }
458
459 if (!dev_run(fileName, NULL, retf)) {
460 err_run_err(fileName);
461 }
462}
463
464/**
465 * OPTION (run-time part) keyword
466 */
467void cmd_options(void) {
468 byte c;
469 bcip_t data;
470
471 c = code_getnext();
472 data = code_getaddr();
473 switch (c) {
474 case OPTION_BASE:
475 opt_base = data;
476 break;
477 case OPTION_MATCH:
478 opt_usepcre = data;
479 break;
480 };
481}
482
483static inline void bc_loop_call_proc() {
484 bcip_t pcode = code_getaddr();
485 switch (pcode) {
486 case kwCLS:
487 dev_cls();
488 break;
489 case kwTHROW:
490 cmd_throw();
491 break;
492 case kwENVIRON:
493 cmd_environ();
494 break;
495 case kwLOCATE:
496 cmd_locate();
497 break;
498 case kwAT:
499 cmd_at();
500 break;
501 case kwPEN:
502 cmd_pen();
503 break;
504 case kwDATEDMY:
505 cmd_datedmy();
506 break;
507 case kwTIMEHMS:
508 cmd_timehms();
509 break;
510 case kwBEEP:
511 cmd_beep();
512 break;
513 case kwSOUND:
514 cmd_sound();
515 break;
516 case kwNOSOUND:
517 cmd_nosound();
518 break;
519 case kwPSET:
520 cmd_pset();
521 break;
522 case kwRECT:
523 cmd_rect();
524 break;
525 case kwCIRCLE:
526 cmd_circle();
527 break;
528 case kwRANDOMIZE:
529 cmd_randomize();
530 break;
531 case kwSPLIT:
532 cmd_split();
533 break;
534 case kwWJOIN:
535 cmd_wjoin();
536 break;
537 case kwPAUSE:
538 cmd_pause();
539 break;
540 case kwDELAY:
541 cmd_delay();
542 break;
543 case kwARC:
544 cmd_arc();
545 break;
546 case kwDRAW:
547 cmd_draw();
548 break;
549 case kwPAINT:
550 cmd_paint();
551 break;
552 case kwPLAY:
553 cmd_play();
554 break;
555 case kwSORT:
556 cmd_sort();
557 break;
558 case kwSEARCH:
559 cmd_search();
560 break;
561 case kwROOT:
562 cmd_root();
563 break;
564 case kwDIFFEQ:
565 cmd_diffeq();
566 break;
567 case kwCHART:
568 cmd_chart();
569 break;
570 case kwWINDOW:
571 cmd_window();
572 break;
573 case kwVIEW:
574 cmd_view();
575 break;
576 case kwDRAWPOLY:
577 cmd_drawpoly();
578 break;
579 case kwM3IDENT:
580 cmd_m3ident();
581 break;
582 case kwM3ROTATE:
583 cmd_m3rotate();
584 break;
585 case kwM3SCALE:
586 cmd_m3scale();
587 break;
588 case kwM3TRANSLATE:
589 cmd_m3translate();
590 break;
591 case kwM3APPLY:
592 cmd_m3apply();
593 break;
594 case kwSEGINTERSECT:
595 cmd_intersect();
596 break;
597 case kwPOLYEXT:
598 cmd_polyext();
599 break;
600 case kwDERIV:
601 cmd_deriv();
602 break;
603 case kwLOADLN:
604 cmd_floadln();
605 break;
606 case kwSAVELN:
607 cmd_fsaveln();
608 break;
609 case kwKILL:
610 cmd_fkill();
611 break;
612 case kwRENAME:
613 cmd_filecp(1);
614 break;
615 case kwCOPY:
616 cmd_filecp(0);
617 break;
618 case kwCHDIR:
619 cmd_chdir();
620 break;
621 case kwMKDIR:
622 cmd_mkdir();
623 break;
624 case kwRMDIR:
625 cmd_rmdir();
626 break;
627 case kwFLOCK:
628 cmd_flock();
629 break;
630 case kwCHMOD:
631 cmd_chmod();
632 break;
633 case kwPLOT:
634 cmd_plot();
635 break;
636 case kwSWAP:
637 cmd_swap();
638 break;
639 case kwDIRWALK:
640 cmd_dirwalk();
641 break;
642 case kwBPUTC:
643 cmd_bputc();
644 break;
645 case kwBSAVE:
646 cmd_bsave();
647 break;
648 case kwBLOAD:
649 cmd_bload();
650 break;
651 case kwEXPRSEQ:
652 cmd_exprseq();
653 break;
654 case kwSTKDUMP:
655 dev_print("\nSTKDUMP:\n");
656 dump_stack();
657 // end of program
658 prog_error = errEnd;
659 break;
660 case kwDEFINEKEY:
661 cmd_definekey();
662 break;
663 case kwSHOWPAGE:
664 dev_show_page();
665 break;
666 case kwTYPE_CALL_VFUNC:
667 cmd_call_vfunc();
668 break;
669 case kwTIMER:
670 cmd_timer();
671 break;
672 default:
673 err_pcode_err(pcode);
674 }
675
676 if (!prog_error && prog_source[prog_ip] == kwTYPE_LEVEL_END) {
677 // allow redundant close bracket around function call
678 prog_ip++;
679 }
680}
681
682static inline void bc_loop_call_extp() {
683 bcip_t lib = code_getaddr();
684 bcip_t idx = code_getaddr();
685 if (lib & UID_UNIT_BIT) {
686 unit_exec(lib & (~UID_UNIT_BIT), idx, NULL);
687 if (gsb_last_error) {
688 prog_error = gsb_last_error;
689 }
690 } else {
691 slib_procexec(lib, prog_symtable[idx].exp_idx);
692 }
693}
694
695static inline void bc_loop_end() {
696 // end of program
697 prog_error = errEnd;
698}
699
700void bc_loop_goto() {
701 bcip_t next_ip = code_getaddr();
702
703 // clear the stack
704 byte pops = code_getnext();
705 while (pops > 0) {
706 code_pop_and_free();
707 pops--;
708 }
709
710 // jump
711 prog_ip = next_ip;
712}
713
714/**
715 * execute commands (loop)
716 *
717 * @param isf if 1, the program must return if found return (by level <= 0);
718 * otherwise an RTE will generated
719 * if 2; like 1, but increase the proc_level because UDF call executed internaly
720 */
721void bc_loop(int isf) {
722 byte pops;
723 int i;
724 int proc_level = 0;
725 byte code = 0;
726
727 // setup event checker time = 50ms
728 uint32_t now = dev_get_millisecond_count();
729 uint32_t next_check = now + EVT_CHECK_EVERY;
730
731 /**
732 * For commands that change the IP use
733 *
734 * case mycommand:
735 * command();
736 * if ( prog_error ) break;
737 * continue;
738 */
739 if (isf == 2) {
740 proc_level++;
741 }
742 while (prog_ip < prog_length) {
743 switch (code) {
744 case kwLABEL:
745 case kwREM:
746 case kwTYPE_EOC:
747 case kwTYPE_LINE:
748 break;
749 default:
750 now = dev_get_millisecond_count();
751 break;
752 }
753
754 // check events every ~50ms
755 if (now >= next_check) {
756 next_check = now + EVT_CHECK_EVERY;
757
758 switch (dev_events(0)) {
759 case -1:
760 // break event
761 break;
762 case -2:
763 prog_error = errBreak;
764 inf_break(prog_line);
765 break;
766 default:
767 if (prog_timer) {
768 timer_run(now);
769 }
770 };
771 }
772
773 // proceed to the next command
774 if (!prog_error) {
775 code = prog_source[prog_ip++];
776 switch (code) {
777 case kwLABEL:
778 case kwREM:
779 case kwTYPE_EOC:
780 continue;
781 case kwTYPE_LINE:
782 prog_line = code_getaddr();
783 if (opt_trace_on) {
784 dev_trace_line(prog_line);
785 }
786 continue;
787 case kwLET:
788 cmd_let(0);
789 break;
790 case kwLET_OPT:
791 cmd_let_opt();
792 break;
793 case kwCONST:
794 cmd_let(1);
795 break;
796 case kwPACKED_LET:
797 cmd_packed_let();
798 break;
799 case kwGOTO:
800 bc_loop_goto();
801 continue;
802 case kwGOSUB:
803 cmd_gosub();
804 IF_ERR_BREAK;
805 continue;
806 case kwRETURN:
807 cmd_return();
808 IF_ERR_BREAK;
809 continue;
810 case kwONJMP:
811 cmd_on_go();
812 IF_ERR_BREAK;
813 continue;
814 case kwPRINT:
815 cmd_print(PV_CONSOLE);
816 break;
817 case kwINPUT:
818 cmd_input(PV_CONSOLE);
819 break;
820 case kwIF:
821 cmd_if();
822 IF_ERR_BREAK;
823 continue;
824 case kwELIF:
825 cmd_elif();
826 IF_ERR_BREAK;
827 continue;
828 case kwELSE:
829 cmd_else();
830 IF_ERR_BREAK;
831 continue;
832 case kwENDIF:
833 cmd_endif();
834 IF_ERR_BREAK;
835 continue;
836 case kwFOR:
837 cmd_for();
838 IF_ERR_BREAK;
839 continue;
840 case kwNEXT:
841 cmd_next();
842 IF_ERR_BREAK;
843 continue;
844 case kwWHILE:
845 cmd_while();
846 IF_ERR_BREAK;
847 continue;
848 case kwWEND:
849 cmd_wend();
850 IF_ERR_BREAK;
851 continue;
852 case kwREPEAT:
853 cmd_repeat();
854 IF_ERR_BREAK;
855 continue;
856 case kwUNTIL:
857 cmd_until();
858 IF_ERR_BREAK;
859 continue;
860 case kwSELECT:
861 cmd_select();
862 IF_ERR_BREAK;
863 continue;
864 case kwCASE:
865 cmd_case();
866 IF_ERR_BREAK;
867 continue;
868 case kwCASE_ELSE:
869 cmd_case_else();
870 IF_ERR_BREAK;
871 continue;
872 case kwENDSELECT:
873 cmd_end_select();
874 IF_ERR_BREAK;
875 continue;
876 case kwDIM:
877 cmd_dim(0);
878 break;
879 case kwREDIM:
880 cmd_redim();
881 break;
882 case kwAPPEND:
883 cmd_append();
884 break;
885 case kwINSERT:
886 cmd_lins();
887 break;
888 case kwDELETE:
889 cmd_ldel();
890 break;
891 case kwERASE:
892 cmd_erase();
893 break;
894 case kwREAD:
895 cmd_read();
896 break;
897 case kwDATA:
898 cmd_data();
899 break;
900 case kwRESTORE:
901 cmd_restore();
902 break;
903 case kwOPTION:
904 cmd_options();
905 break;
906 case kwTYPE_CALLEXTP:
907 bc_loop_call_extp();
908 IF_ERR_BREAK;
909 continue;
910 case kwTYPE_CALLP:
911 bc_loop_call_proc();
912 break;
913 case kwTYPE_CALL_UDP:
914 cmd_udp(kwPROC);
915 if (isf) {
916 proc_level++;
917 }
918 IF_ERR_BREAK;
919 continue;
920 case kwTYPE_CALL_UDF:
921 if (isf) {
922 cmd_udp(kwFUNC);
923 proc_level++;
924 } else {
925 err_syntax(kwTYPE_CALL_UDF, "%G");
926 }
927 IF_ERR_BREAK;
928 continue;
929 case kwTYPE_RET:
930 cmd_udpret();
931 if (isf) {
932 proc_level--;
933 if (proc_level == 0) {
934 return;
935 }
936 }
937 IF_ERR_BREAK;
938 continue;
939 case kwTYPE_CRVAR:
940 cmd_crvar();
941 break;
942 case kwTYPE_PARAM:
943 cmd_param();
944 break;
945 case kwEXIT:
946 pops = cmd_exit();
947 if (isf && pops) {
948 proc_level--;
949 if (proc_level == 0) {
950 return;
951 }
952 }
953 IF_ERR_BREAK;
954 continue;
955 case kwLINE:
956 cmd_line();
957 break;
958 case kwCOLOR:
959 cmd_color();
960 break;
961 case kwOPEN:
962 cmd_fopen();
963 break;
964 case kwCLOSE:
965 cmd_fclose();
966 break;
967 case kwFILEWRITE:
968 cmd_fwrite();
969 break;
970 case kwFILEREAD:
971 cmd_fread();
972 break;
973 case kwLOGPRINT:
974 cmd_print(PV_LOG);
975 break;
976 case kwFILEPRINT:
977 cmd_print(PV_FILE);
978 break;
979 case kwSPRINT:
980 cmd_print(PV_STRING);
981 break;
982 case kwLINEINPUT:
983 cmd_flineinput();
984 break;
985 case kwSINPUT:
986 cmd_input(PV_STRING);
987 break;
988 case kwFILEINPUT:
989 cmd_input(PV_FILE);
990 break;
991 case kwSEEK:
992 cmd_fseek();
993 break;
994 case kwTRON:
995 opt_trace_on = 1;
996 continue;
997 case kwTROFF:
998 opt_trace_on = 0;
999 continue;
1000 case kwSTOP:
1001 case kwEND:
1002 bc_loop_end();
1003 break;
1004 case kwCHAIN:
1005 cmd_chain();
1006 break;
1007 case kwRUN:
1008 cmd_run(1);
1009 break;
1010 case kwEXEC:
1011 cmd_run(0);
1012 break;
1013 case kwTRY:
1014 cmd_try();
1015 IF_ERR_BREAK;
1016 continue;
1017 case kwCATCH:
1018 cmd_catch();
1019 IF_ERR_BREAK;
1020 continue;
1021 case kwENDTRY:
1022 cmd_end_try();
1023 continue;
1024 default:
1025 log_printf("OUT OF ADDRESS SPACE\n");
1026 for (i = 0; keyword_table[i].name[0] != '\0'; i++) {
1027 if (prog_source[prog_ip] == keyword_table[i].code) {
1028 log_printf("OR ILLEGAL CALL TO '%s'\n", keyword_table[i].name);
1029 break;
1030 }
1031 }
1032 if (!opt_quiet) {
1033 hex_dump(prog_source, prog_length);
1034 }
1035 rt_raise("SEG:CODE[%x]=%02x", prog_ip, prog_source[prog_ip]);
1036 }
1037 }
1038 if (prog_ip < prog_length) {
1039 code = prog_source[prog_ip++];
1040 if (code == kwTYPE_LINE) {
1041 prog_line = code_getaddr();
1042 if (opt_trace_on) {
1043 dev_trace_line(prog_line);
1044 }
1045 } else if (code != kwTYPE_EOC) {
1046 if (!opt_quiet) {
1047 hex_dump(prog_source, prog_length);
1048 }
1049 prog_ip--;
1050 if (code == kwTYPE_SEP) {
1051 rt_raise("COMMAND SEPARATOR '%c' FOUND", prog_source[prog_ip + 1]);
1052 } else {
1053 rt_raise("PARAM COUNT ERROR @%d=%X %d", prog_ip, prog_source[prog_ip], code);
1054 }
1055 }
1056 }
1057 // quit on error
1058 IF_ERR_BREAK;
1059 }
1060}
1061
1062/**
1063 * debug info
1064 * stack dump
1065 */
1066void dump_stack() {
1067 stknode_t node;
1068 int i;
1069
1070 do {
1071 code_pop(&node, 0);
1072 if (node.type != 0xFF) {
1073 for (i = 0; keyword_table[i].name[0] != '\0'; i++) {
1074 if (node.type == keyword_table[i].code) {
1075 dev_printf("\t%s", keyword_table[i].name);
1076 switch (node.type) {
1077 case kwGOSUB:
1078 dev_printf(" RIP: %d", node.x.vgosub.ret_ip);
1079 if (prog_source[node.x.vgosub.ret_ip] == kwTYPE_LINE) {
1080 dev_printf(" = LI %d", (*((uint16_t *)(prog_source + node.x.vgosub.ret_ip + 1))) - 1);
1081 }
1082 break;
1083 }
1084 dev_print("\n");
1085 break;
1086 }
1087 }
1088 } else {
1089 break;
1090 }
1091 } while (1);
1092}
1093
1094// load libraries - each library is loaded on new task
1095void brun_load_libraries(int tid) {
1096 // reset symbol mapping
1097 for (int i = 0; i < prog_symcount; i++) {
1098 prog_symtable[i].task_id = prog_symtable[i].exp_idx = -1;
1099 }
1100 // for each library
1101 for (int i = 0; i < prog_libcount; i++) {
1102 if (prog_libtable[i].type == 1) {
1103 // === SB Unit ===
1104 // create task
1105 int lib_tid = brun_create_task(prog_libtable[i].lib, 0, 1);
1106 activate_task(tid);
1107
1108 // update lib-table's task-id field (in this code; not in lib's code)
1109 prog_libtable[i].tid = lib_tid;
1110
1111 // update lib-symbols's task-id field (in this code; not in lib's code)
1112 for (int j = 0; j < prog_symcount; j++) {
1113 char *pname = strrchr(prog_symtable[j].symbol, '.') + 1;
1114 // the name without the 'class'
1115 if ((prog_symtable[j].lib_id & (~UID_UNIT_BIT)) == prog_libtable[i].id) {
1116 // find symbol by name (for sure) and update it
1117 // this is required because lib may be newer than
1118 // parent
1119 for (int k = 0; k < taskinfo(lib_tid)->sbe.exec.expcount; k++) {
1120 if (strcmp(pname, taskinfo(lib_tid)->sbe.exec.exptable[k].symbol) == 0) {
1121 prog_symtable[j].exp_idx = k;
1122 // adjust sid (sid is <-> exp_idx in lib)
1123 prog_symtable[j].task_id = lib_tid;
1124 // connect the library
1125 break;
1126 }
1127 }
1128 }
1129 }
1130 } else {
1131 // === C Module ===
1132 // update lib-table's task-id field (in this code; not in lib's code)
1133 prog_libtable[i].tid = -1; // not a task
1134 int lib_id = prog_libtable[i].id;
1135 slib_import(lib_id, 0);
1136
1137 // update lib-symbols's task-id field (in this code; not in lib's code)
1138 for (int j = 0; j < prog_symcount; j++) {
1139 if (prog_symtable[j].lib_id == lib_id) {
1140 prog_symtable[j].exp_idx = slib_get_kid(lib_id, prog_symtable[j].symbol);
1141 prog_symtable[j].task_id = -1;
1142 }
1143 }
1144 }
1145
1146 // return
1147 activate_task(tid);
1148 }
1149}
1150
1151/*
1152 * RUN byte-code
1153 *
1154 * ByteCode Structure (executables, not units):
1155 *
1156 * [header (bc_head_t)]
1157 * [label-table (ADDRSZ) * label_count]
1158 * [import-lib-table (bc_lib_rec_t) * lib_count]
1159 * [import-symbol-table (bc_symbol_rec_t) * symbol_count]
1160 * [the bytecode itself]
1161 *
1162 * brun_init(source)
1163 * ...brun_create_task(source)
1164 *
1165 * brun()
1166 *
1167 * exec_close()
1168 * ...exec_close_task()
1169 */
1170int brun_create_task(const char *filename, byte *preloaded_bc, int libf) {
1171 bc_head_t hdr;
1172 unit_file_t uft;
1173 byte *source;
1174 char fname[OS_PATHNAME_SIZE + 1];
1175
1176 if (preloaded_bc) {
1177 // I have already BC
1178 source = preloaded_bc;
1179 strlcpy(fname, filename, sizeof(fname));
1180 } else {
1181 // prepare filename
1182 if (!libf) {
1183 char *p;
1184 strlcpy(fname, filename, sizeof(fname));
1185 p = strrchr(fname, '.');
1186 if (p) {
1187 *p = '\0';
1188 }
1189 strlcat(fname, ".sbx", sizeof(fname));
1190 } else {
1191 find_unit(filename, fname);
1192 }
1193 if (access(fname, R_OK)) {
1194 panic("File '%s' not found", fname);
1195 }
1196 // look if it is already loaded
1197 if (search_task(fname) != -1) {
1198 return search_task(fname);
1199 }
1200 // open & load
1201 int h = open(fname, O_RDWR | O_BINARY);
1202 if (h == -1) {
1203 panic("File '%s' not found", fname);
1204 }
1205 // load it
1206 if (libf) {
1207 read(h, &uft, sizeof(unit_file_t));
1208 lseek(h, sizeof(unit_sym_t) * uft.sym_count, SEEK_CUR);
1209 }
1210 read(h, &hdr, sizeof(bc_head_t));
1211 if (hdr.sbver != SB_DWORD_VER) {
1212 panic("File '%s' version incorrect", fname);
1213 }
1214 source = malloc(hdr.size + 4);
1215 lseek(h, 0, SEEK_SET);
1216 read(h, source, hdr.size);
1217 close(h);
1218 }
1219
1220 // create task
1221 int tid = create_task(fname); // create a task
1222 activate_task(tid); // make it active
1223 ctask->bytecode = source;
1224 byte *cp = source;
1225
1226 if (memcmp(source, "SBUn", 4) == 0) { // load a unit
1227 memcpy(&uft, cp, sizeof(unit_file_t));
1228 cp += sizeof(unit_file_t);
1229 prog_expcount = uft.sym_count;
1230
1231 // copy export-symbols from BC
1232 if (prog_expcount) {
1233 prog_exptable = (unit_sym_t *)malloc(prog_expcount * sizeof(unit_sym_t));
1234 for (int i = 0; i < prog_expcount; i++) {
1235 memcpy(&prog_exptable[i], cp, sizeof(unit_sym_t));
1236 cp += sizeof(unit_sym_t);
1237 }
1238 }
1239 } else if (memcmp(source, "SBEx", 4) == 0) {
1240 // load an executable
1241 } else {
1242 // signature error
1243 panic("Wrong bytecode signature");
1244 }
1245
1246 // build executor's task
1247 memcpy(&hdr, cp, sizeof(bc_head_t));
1248 cp += sizeof(bc_head_t);
1249
1250 prog_varcount = hdr.var_count;
1251 prog_labcount = hdr.lab_count;
1252 prog_libcount = hdr.lib_count;
1253 prog_symcount = hdr.sym_count;
1254
1255 // create variable-table
1256 if (prog_varcount == 0) {
1257 prog_varcount++;
1258 }
1259 tvar = malloc(sizeof(var_t *) * prog_varcount);
1260 for (int i = 0; i < prog_varcount; i++) {
1261 tvar[i] = v_new();
1262 }
1263 // create label-table
1264 if (prog_labcount) {
1265 tlab = malloc(sizeof(lab_t) * prog_labcount);
1266 for (int i = 0; i < prog_labcount; i++) {
1267 // copy labels from BC
1268 memcpy(&tlab[i].ip, cp, ADDRSZ);
1269 cp += ADDRSZ;
1270 }
1271 }
1272 // build import-lib table
1273 if (prog_libcount) {
1274 prog_libtable = (bc_lib_rec_t *)malloc(prog_libcount * sizeof(bc_lib_rec_t));
1275 for (int i = 0; i < prog_libcount; i++) {
1276 memcpy(&prog_libtable[i], cp, sizeof(bc_lib_rec_t));
1277 cp += sizeof(bc_lib_rec_t);
1278 }
1279 }
1280
1281 // build import-symbol table
1282 if (prog_symcount) {
1283 prog_symtable = (bc_symbol_rec_t *)malloc(prog_symcount * sizeof(bc_symbol_rec_t));
1284 for (int i = 0; i < prog_symcount; i++) {
1285 memcpy(&prog_symtable[i], cp, sizeof(bc_symbol_rec_t));
1286 cp += sizeof(bc_symbol_rec_t);
1287 }
1288 }
1289
1290 // create system stack
1291 prog_stack_alloc = SB_EXEC_STACK_SIZE;
1292 prog_stack = malloc(sizeof(stknode_t) * prog_stack_alloc);
1293 prog_stack_count = 0;
1294 prog_timer = NULL;
1295
1296 // create eval's stack
1297 eval_size = SB_EVAL_STACK_SIZE;
1298 eval_stk = malloc(sizeof(var_t) * eval_size);
1299 memset(eval_stk, 0, sizeof(var_t) * eval_size);
1300 eval_sp = 0;
1301
1302 // initialize the rest tasks globals
1303 prog_error = errNone;
1304 prog_line = 0;
1305 prog_dp = data_org = hdr.data_ip;
1306 prog_length = hdr.bc_count;
1307 prog_source = cp;
1308 prog_ip = 0;
1309
1310 exec_setup_predefined_variables();
1311 if (prog_libcount) {
1312 brun_load_libraries(tid);
1313 }
1314
1315 return tid;
1316}
1317
1318/**
1319 * clean up the current task's (executor's) data
1320 */
1321int exec_close_task() {
1322 if (ctask->bytecode) {
1323 // clean up - format list
1324 free_format();
1325
1326 // clean up - eval stack
1327 free(eval_stk);
1328 eval_size = 0;
1329 eval_sp = 0;
1330
1331 // clean up - prog stack
1332 while (prog_stack_count > 0) {
1333 code_pop_and_free();
1334 }
1335 free(prog_stack);
1336 // clean up - variables
1337 for (int i = 0; i < (int) prog_varcount; i++) {
1338 // do not free imported variables
1339 int shared = -1;
1340 for (int j = 0; j < prog_symcount; j++) {
1341 if (prog_symtable[j].type == stt_variable &&
1342 prog_symtable[j].var_id == i) {
1343 shared = j;
1344 break;
1345 }
1346 }
1347
1348 // free this variable
1349 if (shared == -1) {
1350 v_free(tvar[i]);
1351 v_detach(tvar[i]);
1352 }
1353 }
1354
1355 free(tvar);
1356 ctask->has_sysvars = 0;
1357
1358 // clean up - rest tables
1359 if (prog_expcount) {
1360 free(prog_exptable);
1361 }
1362 if (prog_libcount) {
1363 free(prog_libtable);
1364 }
1365 if (prog_symcount) {
1366 free(prog_symtable);
1367 }
1368 if (prog_labcount) {
1369 free(tlab);
1370 }
1371
1372 // clean up - the rest
1373 free(ctask->bytecode);
1374 ctask->bytecode = NULL;
1375
1376 // cleanup the keyboard map
1377 keymap_free();
1378
1379 // cleanup timers
1380 timer_free(prog_timer);
1381 prog_timer = NULL;
1382 }
1383
1384 if (prog_error != errEnd && prog_error != errNone) {
1385 return 1;
1386 }
1387 return 0;
1388}
1389
1390/**
1391 * close the executor
1392 */
1393int exec_close(int tid) {
1394 int prev_tid, i;
1395
1396 prev_tid = activate_task(tid);
1397
1398 for (i = 0; i < count_tasks(); i++) {
1399 activate_task(i);
1400
1401 if (ctask->status == tsk_ready && ctask->parent == tid) {
1402 exec_close(ctask->tid);
1403 }
1404 }
1405
1406 activate_task(tid);
1407 exec_close_task();
1408 close_task(tid);
1409 activate_task(prev_tid);
1410 return 1;
1411}
1412
1413/**
1414 * update common variables
1415 *
1416 * if dir = 0, source is the unit, dir = 1 source is this program
1417 *
1418 * actually dir has no meaning, but it is more logical (dir=0 always is also correct)
1419 */
1420void exec_sync_variables(int dir) {
1421 int i, tid;
1422 unit_sym_t *us; // unit's symbol data
1423 bc_symbol_rec_t *ps; // program's symbol data
1424 var_t *vp; // variable for swap
1425
1426 tid = ctask->tid;
1427
1428 for (i = 0; i < prog_symcount; i++) {
1429 if (prog_symtable[i].type == stt_variable) {
1430 ps = &prog_symtable[i];
1431 us = &(taskinfo(ps->task_id)->sbe.exec.exptable[ps->exp_idx]);
1432
1433 if (dir == 0) {
1434 activate_task(ps->task_id);
1435 vp = tvar[us->vid];
1436
1437 // pointer assignment (shared var_t pointer)
1438 activate_task(tid);
1439 if (tvar[ps->var_id] != vp) {
1440 v_free(tvar[ps->var_id]);
1441 v_detach(tvar[ps->var_id]);
1442 }
1443 tvar[ps->var_id] = vp;
1444 } else {
1445 activate_task(tid);
1446 vp = tvar[ps->var_id];
1447
1448 activate_task(ps->task_id);
1449 tvar[us->vid] = vp;
1450
1451 activate_task(tid);
1452 }
1453 }
1454 }
1455}
1456
1457/**
1458 * system specific things - before compilation
1459 */
1460void sys_before_comp() {
1461 // setup prefered screen mode variables
1462 if (getenv("SBGRAF")) {
1463 if (getenv("SBGRAF")) {
1464 comp_preproc_grmode(getenv("SBGRAF"));
1465 }
1466 opt_graphics = 2;
1467 }
1468}
1469
1470/**
1471 * execute the code on this task
1472 */
1473int sbasic_exec_task(int tid) {
1474 int prev_tid, success;
1475
1476 prev_tid = activate_task(tid);
1477
1478 bc_loop(0);
1479 success = (prog_error == errNone || prog_error == errEnd);
1480 if (success) {
1481 prog_error = errNone;
1482 }
1483 activate_task(prev_tid);
1484 return success;
1485}
1486
1487/**
1488 * run libraries and main-code
1489 */
1490int sbasic_recursive_exec(int tid) {
1491 int i, success = 1;
1492 int prev_tid;
1493
1494 prev_tid = activate_task(tid);
1495
1496 for (i = 0; i < count_tasks(); i++) {
1497 if (taskinfo(i)->parent == tid) {
1498 // do the same for the childs
1499 activate_task(i);
1500 success = sbasic_recursive_exec(i);
1501 if (!success) {
1502 break;
1503 }
1504 }
1505 }
1506
1507 if (success) {
1508 activate_task(tid);
1509 exec_sync_variables(0);
1510
1511 // run
1512 if (!opt_quiet) {
1513 dev_printf("Initializing #%d (%s) ...\n", ctask->tid, ctask->file);
1514 }
1515 success = sbasic_exec_task(ctask->tid);
1516 }
1517
1518 activate_task(prev_tid);
1519 return success;
1520}
1521
1522/**
1523 * compile the given file into bytecode
1524 */
1525int sbasic_compile(const char *file) {
1526 int comp_rq = 0; // compilation required = 0
1527 int success = 1;
1528
1529 if (strstr(file, ".sbx") == file + strlen(file) - 4) {
1530 return success; // file is an executable
1531 }
1532
1533 if (opt_nosave) {
1534 comp_rq = 1;
1535 } else {
1536 char exename[OS_PATHNAME_SIZE + 1];
1537 char *p;
1538
1539 // executable name
1540 strcpy(exename, file);
1541 p = strrchr(exename, '.');
1542 if (p) {
1543 *p = '\0';
1544 }
1545 strcat(exename, ".sbx");
1546
1547 if ((access(exename, R_OK) == 0)) {
1548 time_t bin_date = 0, src_date = 0;
1549
1550 // compare dates
1551 if ((bin_date = sys_filetime(exename)) == 0L) {
1552 comp_rq = 1;
1553 }
1554 else if ((src_date = sys_filetime(file)) == 0L) {
1555 comp_rq = 1;
1556 }
1557 if (bin_date >= src_date) {
1558 // TODO: check binary version
1559 ;
1560 } else {
1561 comp_rq = 1;
1562 }
1563 } else {
1564 comp_rq = 1;
1565 }
1566 }
1567
1568 // compile it
1569 if (comp_rq) {
1570 sys_before_comp(); // system specific preparations for compilation
1571 success = comp_compile(file);
1572 }
1573 return success;
1574}
1575
1576/**
1577 * initialize executor and run a binary
1578 */
1579int sbasic_exec_prepare(const char *filename) {
1580 int taskId;
1581
1582 v_init_pool();
1583
1584 // load source
1585 if (opt_nosave) {
1586 taskId = brun_create_task(filename, ctask->bytecode, 0);
1587 } else {
1588 taskId = brun_create_task(filename, 0, 0);
1589 }
1590 // reset system
1591 cmd_play_reset();
1592 graph_reset();
1593 return taskId;
1594}
1595
1596/*
1597 * remember the directory location of the running program
1598 */
1599void sbasic_set_bas_dir(const char *bas_file) {
1600 const char *sep = strrchr(bas_file, OS_DIRSEP);
1601 int path_len = sep == NULL ? 0 : (sep - bas_file);
1602 char cwd[OS_PATHNAME_SIZE + 1];
1603
1604 cwd[0] = '\0';
1605 gsb_bas_dir[0] = '\0';
1606 getcwd(cwd, sizeof(cwd) - 1);
1607 char *ch;
1608 for (ch = cwd; *ch != '\0'; ch++) {
1609 if (*ch == '\\') {
1610 *ch = '/';
1611 }
1612 }
1613 if (bas_file[0] == '/' ||
1614 (bas_file[1] == ':' && ((bas_file[2] == '\\') || bas_file[2] == '/'))) {
1615 // full path
1616 strncat(gsb_bas_dir, bas_file, path_len + 1);
1617 } else if (path_len > 0) {
1618 // relative path
1619 // append the non file part of bas_file to cwd
1620 strcat(gsb_bas_dir, cwd);
1621 if (gsb_bas_dir[strlen(gsb_bas_dir) - 1] != '/') {
1622 strcat(gsb_bas_dir, "/");
1623 }
1624 strncat(gsb_bas_dir, bas_file, path_len + 1);
1625 } else {
1626 // in current dir
1627 strcat(gsb_bas_dir, cwd);
1628 if (gsb_bas_dir[strlen(gsb_bas_dir) - 1] != '/') {
1629 strcat(gsb_bas_dir, "/");
1630 }
1631 }
1632}
1633
1634/**
1635 * this is the main 'execute' routine; its work depended on opt_xxx flags
1636 * use it instead of sbasic_main if managers are already initialized
1637 *
1638 * @param file the source file
1639 * @return true on success
1640 */
1641int sbasic_exec(const char *file) {
1642 int success = 0;
1643 int exec_rq = 1;
1644
1645 // init compile-time options
1646 opt_pref_width = 0;
1647 opt_pref_height = 0;
1648 opt_show_page = 0;
1649
1650 // setup global values
1651 gsb_last_line = gsb_last_error = 0;
1652 strlcpy(gsb_last_file, file, sizeof(gsb_last_file));
1653 strcpy(gsb_last_errmsg, "");
1654 sbasic_set_bas_dir(file);
1655 success = sbasic_compile(file);
1656
1657 if (ctask->bc_type == 2) {
1658 // cannot run a unit
1659 exec_rq = 0;
1660 gsb_last_error = 1;
1661 } else if (!success) { // there was some errors; do not continue
1662 exec_rq = 0;
1663 gsb_last_error = 1;
1664 }
1665
1666 if (exec_rq) { // we will run it
1667 // load everything
1668 int exec_tid = sbasic_exec_prepare(file);
1669
1670 dev_init(opt_graphics, 0); // initialize output device for graphics
1671 srand(clock()); // randomize
1672
1673 // run
1674 sbasic_recursive_exec(exec_tid);
1675
1676 // normal exit
1677 if (!opt_quiet) {
1678 inf_done();
1679 }
1680
1681 exec_close(exec_tid); // clean up executor's garbages
1682 dev_restore(); // restore device
1683 }
1684
1685 // return compilation errors as failure
1686 return !success ? 0 : !gsb_last_error;
1687}
1688
1689/**
1690 * this is the main routine; its work depended on opt_xxx flags
1691 *
1692 * @param file the source file
1693 * @return true on success
1694 */
1695int sbasic_main(const char *file) {
1696 int success;
1697
1698 // initialize managers
1699 init_tasks();
1700 unit_mgr_init();
1701 slib_init();
1702
1703 if (prog_error) {
1704 success = 0;
1705 } else {
1706 success = sbasic_exec(file);
1707 }
1708
1709 // clean up managers
1710 slib_close();
1711 unit_mgr_close();
1712 destroy_tasks();
1713
1714 return success;
1715}
1716
1717