]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.cpp
ir: fix generation of multi-op vinstrs
[xonotic/gmqcc.git] / exec.cpp
1 #ifndef QCVM_LOOP
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6
7 #include "gmqcc.h"
8
9 static void loaderror(const char *fmt, ...)
10 {
11     int     err = errno;
12     va_list ap;
13     va_start(ap, fmt);
14     vprintf(fmt, ap);
15     va_end(ap);
16     printf(": %s\n", util_strerror(err));
17 }
18
19 static void qcvmerror(qc_program_t *prog, const char *fmt, ...)
20 {
21     va_list ap;
22
23     prog->vmerror++;
24
25     va_start(ap, fmt);
26     vprintf(fmt, ap);
27     va_end(ap);
28     putchar('\n');
29 }
30
31 qc_program::qc_program(const char *name, uint16_t crc, size_t entfields)
32     : filename(name)
33     , crc16(crc)
34     , entityfields(entfields)
35 {}
36
37 qc_program_t* prog_load(const char *filename, bool skipversion)
38 {
39     prog_header_t header;
40     qc_program_t *prog;
41     FILE *file = fopen(filename, "rb");
42
43     /* we need all those in order to support INSTR_STATE: */
44     bool            has_self      = false,
45                     has_time      = false,
46                     has_think     = false,
47                     has_nextthink = false,
48                     has_frame     = false;
49
50     if (!file)
51         return nullptr;
52
53     if (fread(&header, sizeof(header), 1, file) != 1) {
54         loaderror("failed to read header from '%s'", filename);
55         fclose(file);
56         return nullptr;
57     }
58
59     util_swap_header(header);
60
61     if (!skipversion && header.version != 6) {
62         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
63         fclose(file);
64         return nullptr;
65     }
66
67     prog = new qc_program(filename, header.crc16, header.entfield);
68
69 #define read_data(hdrvar, progvar, reserved)                           \
70     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {            \
71         loaderror("seek failed");                                      \
72         goto error;                                                    \
73     }                                                                  \
74     prog->progvar.resize(header.hdrvar.length + reserved);             \
75     if (fread(                                                         \
76             &prog->progvar[0],                                         \
77             sizeof(prog->progvar[0]),                                  \
78             header.hdrvar.length,                                      \
79             file                                                       \
80         )!= header.hdrvar.length                                       \
81     ) {                                                                \
82         loaderror("read failed");                                      \
83         goto error;                                                    \
84     }
85 #define read_data1(x)    read_data(x, x, 0)
86 #define read_data2(x, y) read_data(x, x, y)
87
88     read_data (statements, code, 0);
89     read_data1(defs);
90     read_data1(fields);
91     read_data1(functions);
92     read_data1(strings);
93     read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */
94
95     util_swap_statements(prog->code);
96     util_swap_defs_fields(prog->defs);
97     util_swap_defs_fields(prog->fields);
98     util_swap_functions(prog->functions);
99     util_swap_globals(prog->globals);
100
101     fclose(file);
102
103     /* profile counters */
104     prog->profile.resize(prog->code.size());
105     memset(&prog->profile[0], 0, sizeof(prog->profile[0]) * prog->profile.size());
106
107     /* Add tempstring area */
108     prog->tempstring_start = prog->strings.size();
109     prog->tempstring_at = prog->strings.size();
110
111     prog->strings.resize(prog->strings.size() + 16*1024, '\0');
112
113     /* spawn the world entity */
114     prog->entitypool.emplace_back(true);
115     prog->entitydata.resize(prog->entityfields);
116     if (prog->entitydata.size())
117         memset(prog->entitydata.data(), 0, sizeof(prog->entitydata[0]) * prog->entityfields);
118     prog->entities = 1;
119
120     /* cache some globals and fields from names */
121     for (auto &it : prog->defs) {
122         const char *name = prog_getstring(prog, it.name);
123         if (!strcmp(name, "self")) {
124             prog->cached_globals.self = it.offset;
125             has_self = true;
126         }
127         else if (!strcmp(name, "time")) {
128             prog->cached_globals.time = it.offset;
129             has_time = true;
130         }
131     }
132     for (auto &it : prog->fields) {
133         const char *name = prog_getstring(prog, it.name);
134         if (!strcmp(name, "think")) {
135             prog->cached_fields.think = it.offset;
136             has_think = true;
137         }
138         else if (!strcmp(name, "nextthink")) {
139             prog->cached_fields.nextthink = it.offset;
140             has_nextthink = true;
141         }
142         else if (!strcmp(name, "frame")) {
143             prog->cached_fields.frame  = it.offset;
144             has_frame = true;
145         }
146     }
147     if (has_self && has_time && has_think && has_nextthink && has_frame)
148         prog->supports_state = true;
149
150     return prog;
151
152 error:
153     delete prog;
154
155     fclose(file);
156     return nullptr;
157 }
158
159 void prog_delete(qc_program_t *prog)
160 {
161     delete prog;
162 }
163
164 /***********************************************************************
165  * VM code
166  */
167
168 const char* prog_getstring(qc_program_t *prog, qcint_t str) {
169     /* cast for return required for C++ */
170     if (str < 0 || str >= (qcint_t)prog->strings.size())
171         return  "<<<invalid string>>>";
172
173     return &prog->strings[0] + str;
174 }
175
176 prog_section_def_t* prog_entfield(qc_program_t *prog, qcint_t off) {
177     for (auto &it : prog->fields)
178         if (it.offset == off)
179             return &it;
180     return nullptr;
181 }
182
183 prog_section_def_t* prog_getdef(qc_program_t *prog, qcint_t off)
184 {
185     for (auto &it : prog->defs)
186         if (it.offset == off)
187             return &it;
188     return nullptr;
189 }
190
191 qcany_t* prog_getedict(qc_program_t *prog, qcint_t e) {
192     if (e >= (qcint_t)prog->entitypool.size()) {
193         prog->vmerror++;
194         fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
195         e = 0;
196     }
197     return (qcany_t*)&prog->entitydata[prog->entityfields * e];
198 }
199
200 static qcint_t prog_spawn_entity(qc_program_t *prog) {
201     char  *data;
202     qcint_t  e;
203     for (e = 0; e < (qcint_t)prog->entitypool.size(); ++e) {
204         if (!prog->entitypool[e]) {
205             data = (char*)&prog->entitydata[prog->entityfields * e];
206             memset(data, 0, prog->entityfields * sizeof(qcint_t));
207             return e;
208         }
209     }
210     prog->entitypool.emplace_back(true);
211     prog->entities++;
212     size_t sz = prog->entitydata.size();
213     prog->entitydata.resize(sz + prog->entityfields);
214     data = (char*)&prog->entitydata[sz];
215     memset(data, 0, sz * sizeof(qcint_t));
216
217     return e;
218 }
219
220 static void prog_free_entity(qc_program_t *prog, qcint_t e) {
221     if (!e) {
222         prog->vmerror++;
223         fprintf(stderr, "Trying to free world entity\n");
224         return;
225     }
226     if (e >= (qcint_t)prog->entitypool.size()) {
227         prog->vmerror++;
228         fprintf(stderr, "Trying to free out of bounds entity\n");
229         return;
230     }
231     if (!prog->entitypool[e]) {
232         prog->vmerror++;
233         fprintf(stderr, "Double free on entity\n");
234         return;
235     }
236     prog->entitypool[e] = false;
237 }
238
239 qcint_t prog_tempstring(qc_program_t *prog, const char *str) {
240     size_t len = strlen(str);
241     size_t at = prog->tempstring_at;
242
243     /* when we reach the end we start over */
244     if (at + len >= prog->strings.size())
245         at = prog->tempstring_start;
246
247     /* when it doesn't fit, reallocate */
248     if (at + len >= prog->strings.size())
249     {
250         prog->strings.resize(prog->strings.size() + len+1);
251         memcpy(&prog->strings[0] + at, str, len+1);
252         return at;
253     }
254
255     /* when it fits, just copy */
256     memcpy(&prog->strings[0] + at, str, len+1);
257     prog->tempstring_at += len+1;
258     return at;
259 }
260
261 static size_t print_escaped_string(const char *str, size_t maxlen) {
262     size_t len = 2;
263     putchar('"');
264     --maxlen; /* because we're lazy and have escape sequences */
265     while (*str) {
266         if (len >= maxlen) {
267             putchar('.');
268             putchar('.');
269             putchar('.');
270             len += 3;
271             break;
272         }
273         switch (*str) {
274             case '\a': len += 2; putchar('\\'); putchar('a'); break;
275             case '\b': len += 2; putchar('\\'); putchar('b'); break;
276             case '\r': len += 2; putchar('\\'); putchar('r'); break;
277             case '\n': len += 2; putchar('\\'); putchar('n'); break;
278             case '\t': len += 2; putchar('\\'); putchar('t'); break;
279             case '\f': len += 2; putchar('\\'); putchar('f'); break;
280             case '\v': len += 2; putchar('\\'); putchar('v'); break;
281             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
282             case '"':  len += 2; putchar('\\'); putchar('"'); break;
283             default:
284                 ++len;
285                 putchar(*str);
286                 break;
287         }
288         ++str;
289     }
290     putchar('"');
291     return len;
292 }
293
294 static void trace_print_global(qc_program_t *prog, unsigned int glob, int vtype) {
295     static char spaces[28+1] = "                            ";
296     prog_section_def_t *def;
297     qcany_t    *value;
298     int       len;
299
300     if (!glob) {
301         if ((len = printf("<null>,")) == -1)
302             len = 0;
303
304         goto done;
305     }
306
307     def = prog_getdef(prog, glob);
308     value = (qcany_t*)(&prog->globals[glob]);
309
310     len = printf("[@%u] ", glob);
311     if (def) {
312         const char *name = prog_getstring(prog, def->name);
313         if (name[0] == '#')
314             len += printf("$");
315         else
316             len += printf("%s ", name);
317         vtype = def->type & DEF_TYPEMASK;
318     }
319
320     switch (vtype) {
321         case TYPE_VOID:
322         case TYPE_ENTITY:
323         case TYPE_FIELD:
324         case TYPE_FUNCTION:
325         case TYPE_POINTER:
326             len += printf("(%i),", value->_int);
327             break;
328         case TYPE_VECTOR:
329             len += printf("'%g %g %g',", value->vector[0],
330                                          value->vector[1],
331                                          value->vector[2]);
332             break;
333         case TYPE_STRING:
334             if (value->string)
335                 len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5);
336             else
337                 len += printf("(null)");
338             len += printf(",");
339             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
340             break;
341         case TYPE_FLOAT:
342         default:
343             len += printf("%g,", value->_float);
344             break;
345     }
346 done:
347     if (len < (int)sizeof(spaces)-1) {
348         spaces[sizeof(spaces)-1-len] = 0;
349         fputs(spaces, stdout);
350         spaces[sizeof(spaces)-1-len] = ' ';
351     }
352 }
353
354 static void prog_print_statement(qc_program_t *prog, prog_section_statement_t *st) {
355     if (st->opcode >= VINSTR_END) {
356         printf("<illegal instruction %d>\n", st->opcode);
357         return;
358     }
359     if ((prog->xflags & VMXF_TRACE) && !prog->function_stack.empty()) {
360         size_t i;
361         for (i = 0; i < prog->function_stack.size(); ++i)
362             printf("->");
363         printf("%s:", prog->function_stack.back());
364     }
365     printf(" <> %-12s", util_instr_str[st->opcode]);
366     if (st->opcode >= INSTR_IF &&
367         st->opcode <= INSTR_IFNOT)
368     {
369         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
370         printf("%d\n", st->o2.s1);
371     }
372     else if (st->opcode >= INSTR_CALL0 &&
373              st->opcode <= INSTR_CALL8)
374     {
375         trace_print_global(prog, st->o1.u1, TYPE_FUNCTION);
376         printf("\n");
377     }
378     else if (st->opcode == INSTR_GOTO)
379     {
380         printf("%i\n", st->o1.s1);
381     }
382     else
383     {
384         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
385         switch (st->opcode)
386         {
387             case INSTR_MUL_FV:
388                 t[1] = t[2] = TYPE_VECTOR;
389                 break;
390             case INSTR_MUL_VF:
391                 t[0] = t[2] = TYPE_VECTOR;
392                 break;
393             case INSTR_MUL_V:
394                 t[0] = t[1] = TYPE_VECTOR;
395                 break;
396             case INSTR_ADD_V:
397             case INSTR_SUB_V:
398             case INSTR_EQ_V:
399             case INSTR_NE_V:
400                 t[0] = t[1] = t[2] = TYPE_VECTOR;
401                 break;
402             case INSTR_EQ_S:
403             case INSTR_NE_S:
404                 t[0] = t[1] = TYPE_STRING;
405                 break;
406             case INSTR_STORE_F:
407             case INSTR_STOREP_F:
408                 t[2] = -1;
409                 break;
410             case INSTR_STORE_V:
411                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
412                 break;
413             case INSTR_STORE_S:
414                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
415                 break;
416             case INSTR_STORE_ENT:
417                 t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
418                 break;
419             case INSTR_STORE_FLD:
420                 t[0] = t[1] = TYPE_FIELD; t[2] = -1;
421                 break;
422             case INSTR_STORE_FNC:
423                 t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
424                 break;
425             case INSTR_STOREP_V:
426                 t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
427                 break;
428             case INSTR_STOREP_S:
429                 t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
430                 break;
431             case INSTR_STOREP_ENT:
432                 t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
433                 break;
434             case INSTR_STOREP_FLD:
435                 t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
436                 break;
437             case INSTR_STOREP_FNC:
438                 t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
439                 break;
440         }
441         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
442         else           printf("(none),          ");
443         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
444         else           printf("(none),          ");
445         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
446         else           printf("(none)");
447         printf("\n");
448     }
449 }
450
451 static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *func) {
452     qc_exec_stack_t st;
453     size_t  parampos;
454     int32_t p;
455
456     /* back up locals */
457     st.localsp  = prog->localstack.size();
458     st.stmt     = prog->statement;
459     st.function = func;
460
461     if (prog->xflags & VMXF_TRACE) {
462         const char *str = prog_getstring(prog, func->name);
463         prog->function_stack.emplace_back(str);
464     }
465
466 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
467     if (prog->stack.size())
468     {
469         prog_section_function_t *cur;
470         cur = prog->stack.back().function;
471         if (cur)
472         {
473             qcint_t *globals = &prog->globals[0] + cur->firstlocal;
474             prog->localstack.insert(prog->localstack.end(), globals, globals + cur->locals);
475         }
476     }
477 #else
478     {
479         qcint_t *globals = &prog->globals[0] + func->firstlocal;
480         prog->localstack.insert(prog->localstack.end(), globals, globals + func->locals);
481     }
482 #endif
483
484     /* copy parameters */
485     parampos = func->firstlocal;
486     for (p = 0; p < func->nargs; ++p)
487     {
488         size_t s;
489         for (s = 0; s < func->argsize[p]; ++s) {
490             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
491             ++parampos;
492         }
493     }
494
495     prog->stack.emplace_back(st);
496
497     return func->entry;
498 }
499
500 static qcint_t prog_leavefunction(qc_program_t *prog) {
501     prog_section_function_t *prev = nullptr;
502     size_t oldsp;
503
504     qc_exec_stack_t st = prog->stack.back();
505
506     if (prog->xflags & VMXF_TRACE) {
507         if (!prog->function_stack.empty())
508             prog->function_stack.pop_back();
509     }
510
511 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
512     if (prog->stack.size() > 1) {
513         prev  = prog->stack[prog->stack.size()-2].function;
514         oldsp = prog->stack[prog->stack.size()-2].localsp;
515     }
516 #else
517     prev  = prog->stack[prog->stack.size()-1].function;
518     oldsp = prog->stack[prog->stack.size()-1].localsp;
519 #endif
520     if (prev) {
521         if (prev->locals) {
522             qcint_t *globals = &prog->globals[0] + prev->firstlocal;
523             memcpy(globals, &prog->localstack[oldsp], prev->locals * sizeof(prog->localstack[0]));
524         }
525         prog->localstack.resize(oldsp);
526     }
527
528     prog->stack.pop_back();
529
530     return st.stmt - 1; /* offset the ++st */
531 }
532
533 bool prog_exec(qc_program_t *prog, prog_section_function_t *func, size_t flags, long maxjumps) {
534     long jumpcount = 0;
535     size_t oldxflags = prog->xflags;
536     prog_section_statement_t *st;
537
538     prog->vmerror = 0;
539     prog->xflags = flags;
540
541     st = &prog->code[0] + prog_enterfunction(prog, func);
542     --st;
543     switch (flags)
544     {
545         default:
546         case 0:
547         {
548 #define QCVM_LOOP    1
549 #define QCVM_PROFILE 0
550 #define QCVM_TRACE   0
551 #           include __FILE__
552         }
553         case (VMXF_TRACE):
554         {
555 #define QCVM_PROFILE 0
556 #define QCVM_TRACE   1
557 #           include __FILE__
558         }
559         case (VMXF_PROFILE):
560         {
561 #define QCVM_PROFILE 1
562 #define QCVM_TRACE   0
563 #           include __FILE__
564         }
565         case (VMXF_TRACE|VMXF_PROFILE):
566         {
567 #define QCVM_PROFILE 1
568 #define QCVM_TRACE   1
569 #           include __FILE__
570         }
571     };
572
573 cleanup:
574     prog->xflags = oldxflags;
575     prog->localstack.clear();
576     prog->stack.clear();
577     if (prog->vmerror)
578         return false;
579     return true;
580 }
581
582 /***********************************************************************
583  * main for when building the standalone executor
584  */
585
586 #include <math.h>
587
588 const char *type_name[TYPE_COUNT] = {
589     "void",
590     "string",
591     "float",
592     "vector",
593     "entity",
594     "field",
595     "function",
596     "pointer",
597     "integer",
598
599     "variant",
600
601     "struct",
602     "union",
603     "array",
604
605     "nil",
606     "noexpr"
607 };
608
609 struct qcvm_parameter {
610     int         vtype;
611     const char *value;
612 };
613
614 static std::vector<qcvm_parameter> main_params;
615
616 #define CheckArgs(num) do {                                                    \
617     if (prog->argc != (num)) {                                                 \
618         prog->vmerror++;                                                       \
619         fprintf(stderr, "ERROR: invalid number of arguments for %s: %i, expected %i\n", \
620         __func__, prog->argc, (num));                                      \
621         return -1;                                                             \
622     }                                                                          \
623 } while (0)
624
625 #define GetGlobal(idx) ((qcany_t*)(&prog->globals[0] + (idx)))
626 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
627 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
628
629 static int qc_print(qc_program_t *prog) {
630     size_t i;
631     const char *laststr = nullptr;
632     for (i = 0; i < (size_t)prog->argc; ++i) {
633         qcany_t *str = (qcany_t*)(&prog->globals[0] + OFS_PARM0 + 3*i);
634         laststr = prog_getstring(prog, str->string);
635         printf("%s", laststr);
636     }
637     if (laststr && (prog->xflags & VMXF_TRACE)) {
638         size_t len = strlen(laststr);
639         if (!len || laststr[len-1] != '\n')
640             printf("\n");
641     }
642     return 0;
643 }
644
645 static int qc_error(qc_program_t *prog) {
646     fprintf(stderr, "*** VM raised an error:\n");
647     qc_print(prog);
648     prog->vmerror++;
649     return -1;
650 }
651
652 static int qc_ftos(qc_program_t *prog) {
653     char buffer[512];
654     qcany_t *num;
655     qcany_t str;
656     CheckArgs(1);
657     num = GetArg(0);
658     util_snprintf(buffer, sizeof(buffer), "%g", num->_float);
659     str.string = prog_tempstring(prog, buffer);
660     Return(str);
661     return 0;
662 }
663
664 static int qc_stof(qc_program_t *prog) {
665     qcany_t *str;
666     qcany_t num;
667     CheckArgs(1);
668     str = GetArg(0);
669     num._float = (float)strtod(prog_getstring(prog, str->string), nullptr);
670     Return(num);
671     return 0;
672 }
673
674 static int qc_stov(qc_program_t *prog) {
675     qcany_t *str;
676     qcany_t num;
677     CheckArgs(1);
678     str = GetArg(0);
679     (void)util_sscanf(prog_getstring(prog, str->string), " ' %f %f %f ' ",
680                       &num.vector[0],
681                       &num.vector[1],
682                       &num.vector[2]);
683     Return(num);
684     return 0;
685 }
686
687 static int qc_vtos(qc_program_t *prog) {
688     char buffer[512];
689     qcany_t *num;
690     qcany_t str;
691     CheckArgs(1);
692     num = GetArg(0);
693     util_snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]);
694     str.string = prog_tempstring(prog, buffer);
695     Return(str);
696     return 0;
697 }
698
699 static int qc_etos(qc_program_t *prog) {
700     char buffer[512];
701     qcany_t *num;
702     qcany_t str;
703     CheckArgs(1);
704     num = GetArg(0);
705     util_snprintf(buffer, sizeof(buffer), "%i", num->_int);
706     str.string = prog_tempstring(prog, buffer);
707     Return(str);
708     return 0;
709 }
710
711 static int qc_spawn(qc_program_t *prog) {
712     qcany_t ent;
713     CheckArgs(0);
714     ent.edict = prog_spawn_entity(prog);
715     Return(ent);
716     return (ent.edict ? 0 : -1);
717 }
718
719 static int qc_kill(qc_program_t *prog) {
720     qcany_t *ent;
721     CheckArgs(1);
722     ent = GetArg(0);
723     prog_free_entity(prog, ent->edict);
724     return 0;
725 }
726
727 static int qc_sqrt(qc_program_t *prog) {
728     qcany_t *num, out;
729     CheckArgs(1);
730     num = GetArg(0);
731     out._float = sqrt(num->_float);
732     Return(out);
733     return 0;
734 }
735
736 static int qc_vlen(qc_program_t *prog) {
737     qcany_t *vec, len;
738     CheckArgs(1);
739     vec = GetArg(0);
740     len._float = sqrt(vec->vector[0] * vec->vector[0] +
741                       vec->vector[1] * vec->vector[1] +
742                       vec->vector[2] * vec->vector[2]);
743     Return(len);
744     return 0;
745 }
746
747 static int qc_normalize(qc_program_t *prog) {
748     double len;
749     qcany_t *vec;
750     qcany_t out;
751     CheckArgs(1);
752     vec = GetArg(0);
753     len = sqrt(vec->vector[0] * vec->vector[0] +
754                vec->vector[1] * vec->vector[1] +
755                vec->vector[2] * vec->vector[2]);
756     if (len)
757         len = 1.0 / len;
758     else
759         len = 0;
760     out.vector[0] = len * vec->vector[0];
761     out.vector[1] = len * vec->vector[1];
762     out.vector[2] = len * vec->vector[2];
763     Return(out);
764     return 0;
765 }
766
767 static int qc_strcat(qc_program_t *prog) {
768     char  *buffer;
769     size_t len1,   len2;
770     qcany_t *str1,  *str2;
771     qcany_t  out;
772
773     const char *cstr1;
774     const char *cstr2;
775
776     CheckArgs(2);
777     str1 = GetArg(0);
778     str2 = GetArg(1);
779     cstr1 = prog_getstring(prog, str1->string);
780     cstr2 = prog_getstring(prog, str2->string);
781     len1 = strlen(cstr1);
782     len2 = strlen(cstr2);
783     buffer = (char*)mem_a(len1 + len2 + 1);
784     memcpy(buffer, cstr1, len1);
785     memcpy(buffer+len1, cstr2, len2+1);
786     out.string = prog_tempstring(prog, buffer);
787     mem_d(buffer);
788     Return(out);
789     return 0;
790 }
791
792 static int qc_strcmp(qc_program_t *prog) {
793     qcany_t *str1,  *str2;
794     qcany_t out;
795
796     const char *cstr1;
797     const char *cstr2;
798
799     if (prog->argc != 2 && prog->argc != 3) {
800         fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
801                prog->argc);
802         return -1;
803     }
804
805     str1 = GetArg(0);
806     str2 = GetArg(1);
807     cstr1 = prog_getstring(prog, str1->string);
808     cstr2 = prog_getstring(prog, str2->string);
809     if (prog->argc == 3)
810         out._float = strncmp(cstr1, cstr2, GetArg(2)->_float);
811     else
812         out._float = strcmp(cstr1, cstr2);
813     Return(out);
814     return 0;
815 }
816
817 static int qc_floor(qc_program_t *prog) {
818     qcany_t *num, out;
819     CheckArgs(1);
820     num = GetArg(0);
821     out._float = floor(num->_float);
822     Return(out);
823     return 0;
824 }
825
826 static int qc_pow(qc_program_t *prog) {
827     qcany_t *base, *exp, out;
828     CheckArgs(2);
829     base = GetArg(0);
830     exp = GetArg(1);
831     out._float = powf(base->_float, exp->_float);
832     Return(out);
833     return 0;
834 }
835
836 static prog_builtin_t qc_builtins[] = {
837     nullptr,
838     &qc_print,       /*   1   */
839     &qc_ftos,        /*   2   */
840     &qc_spawn,       /*   3   */
841     &qc_kill,        /*   4   */
842     &qc_vtos,        /*   5   */
843     &qc_error,       /*   6   */
844     &qc_vlen,        /*   7   */
845     &qc_etos,        /*   8   */
846     &qc_stof,        /*   9   */
847     &qc_strcat,      /*   10  */
848     &qc_strcmp,      /*   11  */
849     &qc_normalize,   /*   12  */
850     &qc_sqrt,        /*   13  */
851     &qc_floor,       /*   14  */
852     &qc_pow,         /*   15  */
853     &qc_stov         /*   16  */
854 };
855
856 static const char *arg0 = nullptr;
857
858 static void version(void) {
859     printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
860            GMQCC_VERSION_MAJOR,
861            GMQCC_VERSION_MINOR,
862            GMQCC_VERSION_PATCH,
863            __DATE__,
864            __TIME__
865     );
866 }
867
868 static void usage(void) {
869     printf("usage: %s [options] [parameters] file\n", arg0);
870     printf("options:\n");
871     printf("  -h, --help         print this message\n"
872            "  -trace             trace the execution\n"
873            "  -profile           perform profiling during execution\n"
874            "  -info              print information from the prog's header\n"
875            "  -disasm            disassemble and exit\n"
876            "  -disasm-func func  disassemble and exit\n"
877            "  -printdefs         list the defs section\n"
878            "  -printfields       list the field section\n"
879            "  -printfuns         list functions information\n"
880            "  -v                 be verbose\n"
881            "  -vv                be even more verbose\n");
882     printf("parameters:\n");
883     printf("  -vector <V>   pass a vector parameter to main()\n"
884            "  -float  <f>   pass a float parameter to main()\n"
885            "  -string <s>   pass a string parameter to main() \n");
886 }
887
888 static void prog_main_setparams(qc_program_t *prog) {
889     size_t i;
890     qcany_t *arg;
891
892     for (i = 0; i < main_params.size(); ++i) {
893         arg = GetGlobal(OFS_PARM0 + 3*i);
894         arg->vector[0] = 0;
895         arg->vector[1] = 0;
896         arg->vector[2] = 0;
897         switch (main_params[i].vtype) {
898             case TYPE_VECTOR:
899                 (void)util_sscanf(main_params[i].value, " %f %f %f ",
900                                        &arg->vector[0],
901                                        &arg->vector[1],
902                                        &arg->vector[2]);
903                 break;
904             case TYPE_FLOAT:
905                 arg->_float = atof(main_params[i].value);
906                 break;
907             case TYPE_STRING:
908                 arg->string = prog_tempstring(prog, main_params[i].value);
909                 break;
910             default:
911                 fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype);
912                 break;
913         }
914     }
915 }
916
917 static void prog_disasm_function(qc_program_t *prog, size_t id);
918
919 int main(int argc, char **argv) {
920     size_t      i;
921     qcint_t       fnmain = -1;
922     qc_program_t *prog;
923     size_t      xflags = VMXF_DEFAULT;
924     bool        opts_printfields = false;
925     bool        opts_printdefs   = false;
926     bool        opts_printfuns   = false;
927     bool        opts_disasm      = false;
928     bool        opts_info        = false;
929     bool        noexec           = false;
930     const char *progsfile        = nullptr;
931     int         opts_v           = 0;
932     std::vector<const char*> dis_list;
933
934     arg0 = argv[0];
935
936     if (argc < 2) {
937         usage();
938         exit(EXIT_FAILURE);
939     }
940
941     while (argc > 1) {
942         if (!strcmp(argv[1], "-h") ||
943             !strcmp(argv[1], "-help") ||
944             !strcmp(argv[1], "--help"))
945         {
946             usage();
947             exit(EXIT_SUCCESS);
948         }
949         else if (!strcmp(argv[1], "-v")) {
950             ++opts_v;
951             --argc;
952             ++argv;
953         }
954         else if (!strncmp(argv[1], "-vv", 3)) {
955             const char *av = argv[1]+1;
956             for (; *av; ++av) {
957                 if (*av == 'v')
958                     ++opts_v;
959                 else {
960                     usage();
961                     exit(EXIT_FAILURE);
962                 }
963             }
964             --argc;
965             ++argv;
966         }
967         else if (!strcmp(argv[1], "-version") ||
968                  !strcmp(argv[1], "--version"))
969         {
970             version();
971             exit(EXIT_SUCCESS);
972         }
973         else if (!strcmp(argv[1], "-trace")) {
974             --argc;
975             ++argv;
976             xflags |= VMXF_TRACE;
977         }
978         else if (!strcmp(argv[1], "-profile")) {
979             --argc;
980             ++argv;
981             xflags |= VMXF_PROFILE;
982         }
983         else if (!strcmp(argv[1], "-info")) {
984             --argc;
985             ++argv;
986             opts_info = true;
987             noexec = true;
988         }
989         else if (!strcmp(argv[1], "-disasm")) {
990             --argc;
991             ++argv;
992             opts_disasm = true;
993             noexec = true;
994         }
995         else if (!strcmp(argv[1], "-disasm-func")) {
996             --argc;
997             ++argv;
998             if (argc <= 1) {
999                 usage();
1000                 exit(EXIT_FAILURE);
1001             }
1002             dis_list.emplace_back(argv[1]);
1003             --argc;
1004             ++argv;
1005             noexec = true;
1006         }
1007         else if (!strcmp(argv[1], "-printdefs")) {
1008             --argc;
1009             ++argv;
1010             opts_printdefs = true;
1011             noexec = true;
1012         }
1013         else if (!strcmp(argv[1], "-printfuns")) {
1014             --argc;
1015             ++argv;
1016             opts_printfuns = true;
1017             noexec = true;
1018         }
1019         else if (!strcmp(argv[1], "-printfields")) {
1020             --argc;
1021             ++argv;
1022             opts_printfields = true;
1023             noexec = true;
1024         }
1025         else if (!strcmp(argv[1], "-vector") ||
1026                  !strcmp(argv[1], "-string") ||
1027                  !strcmp(argv[1], "-float") )
1028         {
1029             qcvm_parameter p;
1030             if (argv[1][1] == 'f')
1031                 p.vtype = TYPE_FLOAT;
1032             else if (argv[1][1] == 's')
1033                 p.vtype = TYPE_STRING;
1034             else if (argv[1][1] == 'v')
1035                 p.vtype = TYPE_VECTOR;
1036             else
1037                 p.vtype = TYPE_VOID;
1038
1039             --argc;
1040             ++argv;
1041             if (argc < 2) {
1042                 usage();
1043                 exit(EXIT_FAILURE);
1044             }
1045             p.value = argv[1];
1046
1047             main_params.emplace_back(p);
1048             --argc;
1049             ++argv;
1050         }
1051         else if (!strcmp(argv[1], "--")) {
1052             --argc;
1053             ++argv;
1054             break;
1055         }
1056         else if (argv[1][0] != '-') {
1057             if (progsfile) {
1058                 fprintf(stderr, "only 1 program file may be specified\n");
1059                 usage();
1060                 exit(EXIT_FAILURE);
1061             }
1062             progsfile = argv[1];
1063             --argc;
1064             ++argv;
1065         }
1066         else
1067         {
1068             fprintf(stderr, "unknown parameter: %s\n", argv[1]);
1069             usage();
1070             exit(EXIT_FAILURE);
1071         }
1072     }
1073
1074     if (argc == 2 && !progsfile) {
1075         progsfile = argv[1];
1076         --argc;
1077         ++argv;
1078     }
1079
1080     if (!progsfile) {
1081         fprintf(stderr, "must specify a program to execute\n");
1082         usage();
1083         exit(EXIT_FAILURE);
1084     }
1085
1086     prog = prog_load(progsfile, noexec);
1087     if (!prog) {
1088         fprintf(stderr, "failed to load program '%s'\n", progsfile);
1089         exit(EXIT_FAILURE);
1090     }
1091
1092     prog->builtins       = qc_builtins;
1093     prog->builtins_count = GMQCC_ARRAY_COUNT(qc_builtins);
1094
1095     if (opts_info) {
1096         printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16);
1097         printf("Entity field space: %u\n", (unsigned int)prog->entityfields);
1098         printf("Globals: %zu\n", prog->globals.size());
1099         printf("Counts:\n"
1100                "      code: %zu\n"
1101                "      defs: %zu\n"
1102                "    fields: %zu\n"
1103                " functions: %zu\n"
1104                "   strings: %zu\n",
1105                prog->code.size(),
1106                prog->defs.size(),
1107                prog->fields.size(),
1108                prog->functions.size(),
1109                prog->strings.size());
1110     }
1111
1112     if (opts_info) {
1113         prog_delete(prog);
1114         return 0;
1115     }
1116     for (i = 0; i < dis_list.size(); ++i) {
1117         size_t k;
1118         printf("Looking for `%s`\n", dis_list[i]);
1119         for (k = 1; k < prog->functions.size(); ++k) {
1120             const char *name = prog_getstring(prog, prog->functions[k].name);
1121             if (!strcmp(name, dis_list[i])) {
1122                 prog_disasm_function(prog, k);
1123                 break;
1124             }
1125         }
1126     }
1127     if (opts_disasm) {
1128         for (i = 1; i < prog->functions.size(); ++i)
1129             prog_disasm_function(prog, i);
1130         return 0;
1131     }
1132     if (opts_printdefs) {
1133         const char *getstring = nullptr;
1134         for (auto &it : prog->defs) {
1135             printf("Global: %8s %-16s at %u%s",
1136                    type_name[it.type & DEF_TYPEMASK],
1137                    prog_getstring(prog, it.name),
1138                    (unsigned int)it.offset,
1139                    ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1140             if (opts_v) {
1141                 switch (it.type & DEF_TYPEMASK) {
1142                     case TYPE_FLOAT:
1143                         printf(" [init: %g]", ((qcany_t*)(&prog->globals[0] + it.offset))->_float);
1144                         break;
1145                     case TYPE_INTEGER:
1146                         printf(" [init: %i]", (int)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int ));
1147                         break;
1148                     case TYPE_ENTITY:
1149                     case TYPE_FUNCTION:
1150                     case TYPE_FIELD:
1151                     case TYPE_POINTER:
1152                         printf(" [init: %u]", (unsigned)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int ));
1153                         break;
1154                     case TYPE_STRING:
1155                         getstring = prog_getstring(prog, ((qcany_t*)(&prog->globals[0] + it.offset))->string);
1156                         printf(" [init: `");
1157                         print_escaped_string(getstring, strlen(getstring));
1158                         printf("`]\n");
1159                         break;
1160                     default:
1161                         break;
1162                 }
1163             }
1164             printf("\n");
1165         }
1166     }
1167     if (opts_printfields) {
1168         for (auto &it : prog->fields) {
1169             printf("Field: %8s %-16s at %d%s\n",
1170                    type_name[it.type],
1171                    prog_getstring(prog, it.name),
1172                    it.offset,
1173                    ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1174         }
1175     }
1176     if (opts_printfuns) {
1177         for (auto &it : prog->functions) {
1178             int32_t a;
1179             printf("Function: %-16s taking %u parameters:(",
1180                    prog_getstring(prog, it.name),
1181                    (unsigned int)it.nargs);
1182             for (a = 0; a < it.nargs; ++a) {
1183                 printf(" %i", it.argsize[a]);
1184             }
1185             if (opts_v > 1) {
1186                 int32_t start = it.entry;
1187                 if (start < 0)
1188                     printf(") builtin %i\n", (int)-start);
1189                 else {
1190                     size_t funsize = 0;
1191                     prog_section_statement_t *st = &prog->code[0] + start;
1192                     for (;st->opcode != INSTR_DONE; ++st)
1193                         ++funsize;
1194                     printf(") - %zu instructions", funsize);
1195                     if (opts_v > 2) {
1196                         printf(" - locals: %i + %i\n",
1197                                it.firstlocal,
1198                                it.locals);
1199                     }
1200                     else
1201                         printf("\n");
1202                 }
1203             }
1204             else if (opts_v) {
1205                 printf(") locals: %i + %i\n",
1206                        it.firstlocal,
1207                        it.locals);
1208             }
1209             else
1210                 printf(")\n");
1211         }
1212     }
1213     if (!noexec) {
1214         for (i = 1; i < prog->functions.size(); ++i) {
1215             const char *name = prog_getstring(prog, prog->functions[i].name);
1216             if (!strcmp(name, "main"))
1217                 fnmain = (qcint_t)i;
1218         }
1219         if (fnmain > 0)
1220         {
1221             prog_main_setparams(prog);
1222             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
1223         }
1224         else
1225             fprintf(stderr, "No main function found\n");
1226     }
1227
1228     prog_delete(prog);
1229     return 0;
1230 }
1231
1232 static void prog_disasm_function(qc_program_t *prog, size_t id) {
1233     prog_section_function_t *fdef = &prog->functions[0] + id;
1234     prog_section_statement_t *st;
1235
1236     if (fdef->entry < 0) {
1237         printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry);
1238         return;
1239     }
1240     else
1241         printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name));
1242
1243     st = &prog->code[0] + fdef->entry;
1244     while (st->opcode != INSTR_DONE) {
1245         prog_print_statement(prog, st);
1246         ++st;
1247     }
1248 }
1249 #else /* !QCVM_LOOP */
1250 /*
1251  * Everything from here on is not including into the compilation of the
1252  * executor.  This is simply code that is #included via #include __FILE__
1253  * see when QCVM_LOOP is defined, the rest of the code above do not get
1254  * re-included.  So this really just acts like one large macro, but it
1255  * sort of isn't, which makes it nicer looking.
1256  */
1257
1258 #define OPA ( (qcany_t*) (&prog->globals[0] + st->o1.u1) )
1259 #define OPB ( (qcany_t*) (&prog->globals[0] + st->o2.u1) )
1260 #define OPC ( (qcany_t*) (&prog->globals[0] + st->o3.u1) )
1261
1262 #define GLOBAL(x) ( (qcany_t*) (&prog->globals[0] + (x)) )
1263
1264 /* to be consistent with current darkplaces behaviour */
1265 #if !defined(FLOAT_IS_TRUE_FOR_INT)
1266 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
1267 #endif
1268
1269 while (prog->vmerror == 0) {
1270     prog_section_function_t  *newf;
1271     qcany_t          *ed;
1272     qcany_t          *ptr;
1273
1274     ++st;
1275
1276 #if QCVM_PROFILE
1277     prog->profile[st - &prog->code[0]]++;
1278 #endif
1279
1280 #if QCVM_TRACE
1281     prog_print_statement(prog, st);
1282 #endif
1283
1284     switch (st->opcode)
1285     {
1286         default:
1287             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename.c_str());
1288             goto cleanup;
1289
1290         case INSTR_DONE:
1291         case INSTR_RETURN:
1292             /* TODO: add instruction count to function profile count */
1293             GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
1294             GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
1295             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
1296
1297             st = &prog->code[0] + prog_leavefunction(prog);
1298             if (prog->stack.empty())
1299                 goto cleanup;
1300
1301             break;
1302
1303         case INSTR_MUL_F:
1304             OPC->_float = OPA->_float * OPB->_float;
1305             break;
1306         case INSTR_MUL_V:
1307             OPC->_float = OPA->vector[0]*OPB->vector[0] +
1308                           OPA->vector[1]*OPB->vector[1] +
1309                           OPA->vector[2]*OPB->vector[2];
1310             break;
1311         case INSTR_MUL_FV:
1312         {
1313             qcfloat_t f = OPA->_float;
1314             OPC->vector[0] = f * OPB->vector[0];
1315             OPC->vector[1] = f * OPB->vector[1];
1316             OPC->vector[2] = f * OPB->vector[2];
1317             break;
1318         }
1319         case INSTR_MUL_VF:
1320         {
1321             qcfloat_t f = OPB->_float;
1322             OPC->vector[0] = f * OPA->vector[0];
1323             OPC->vector[1] = f * OPA->vector[1];
1324             OPC->vector[2] = f * OPA->vector[2];
1325             break;
1326         }
1327         case INSTR_DIV_F:
1328             if (OPB->_float != 0.0f)
1329                 OPC->_float = OPA->_float / OPB->_float;
1330             else
1331                 OPC->_float = 0;
1332             break;
1333
1334         case INSTR_ADD_F:
1335             OPC->_float = OPA->_float + OPB->_float;
1336             break;
1337         case INSTR_ADD_V:
1338             OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
1339             OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
1340             OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
1341             break;
1342         case INSTR_SUB_F:
1343             OPC->_float = OPA->_float - OPB->_float;
1344             break;
1345         case INSTR_SUB_V:
1346             OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
1347             OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
1348             OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
1349             break;
1350
1351         case INSTR_EQ_F:
1352             OPC->_float = (OPA->_float == OPB->_float);
1353             break;
1354         case INSTR_EQ_V:
1355             OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
1356                            (OPA->vector[1] == OPB->vector[1]) &&
1357                            (OPA->vector[2] == OPB->vector[2]) );
1358             break;
1359         case INSTR_EQ_S:
1360             OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
1361                                   prog_getstring(prog, OPB->string));
1362             break;
1363         case INSTR_EQ_E:
1364             OPC->_float = (OPA->_int == OPB->_int);
1365             break;
1366         case INSTR_EQ_FNC:
1367             OPC->_float = (OPA->function == OPB->function);
1368             break;
1369         case INSTR_NE_F:
1370             OPC->_float = (OPA->_float != OPB->_float);
1371             break;
1372         case INSTR_NE_V:
1373             OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
1374                            (OPA->vector[1] != OPB->vector[1]) ||
1375                            (OPA->vector[2] != OPB->vector[2]) );
1376             break;
1377         case INSTR_NE_S:
1378             OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
1379                                    prog_getstring(prog, OPB->string));
1380             break;
1381         case INSTR_NE_E:
1382             OPC->_float = (OPA->_int != OPB->_int);
1383             break;
1384         case INSTR_NE_FNC:
1385             OPC->_float = (OPA->function != OPB->function);
1386             break;
1387
1388         case INSTR_LE:
1389             OPC->_float = (OPA->_float <= OPB->_float);
1390             break;
1391         case INSTR_GE:
1392             OPC->_float = (OPA->_float >= OPB->_float);
1393             break;
1394         case INSTR_LT:
1395             OPC->_float = (OPA->_float < OPB->_float);
1396             break;
1397         case INSTR_GT:
1398             OPC->_float = (OPA->_float > OPB->_float);
1399             break;
1400
1401         case INSTR_LOAD_F:
1402         case INSTR_LOAD_S:
1403         case INSTR_LOAD_FLD:
1404         case INSTR_LOAD_ENT:
1405         case INSTR_LOAD_FNC:
1406             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1407                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
1408                 goto cleanup;
1409             }
1410             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
1411                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1412                           prog->filename.c_str(),
1413                           OPB->_int);
1414                 goto cleanup;
1415             }
1416             ed = prog_getedict(prog, OPA->edict);
1417             OPC->_int = ((qcany_t*)( ((qcint_t*)ed) + OPB->_int ))->_int;
1418             break;
1419         case INSTR_LOAD_V:
1420             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1421                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
1422                 goto cleanup;
1423             }
1424             if (OPB->_int < 0 || OPB->_int + 3 > (qcint_t)prog->entityfields)
1425             {
1426                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1427                           prog->filename.c_str(),
1428                           OPB->_int + 2);
1429                 goto cleanup;
1430             }
1431             ed = prog_getedict(prog, OPA->edict);
1432             ptr = (qcany_t*)( ((qcint_t*)ed) + OPB->_int );
1433             OPC->ivector[0] = ptr->ivector[0];
1434             OPC->ivector[1] = ptr->ivector[1];
1435             OPC->ivector[2] = ptr->ivector[2];
1436             break;
1437
1438         case INSTR_ADDRESS:
1439             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1440                 qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename.c_str(), OPA->edict);
1441                 goto cleanup;
1442             }
1443             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
1444             {
1445                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1446                           prog->filename.c_str(),
1447                           OPB->_int);
1448                 goto cleanup;
1449             }
1450
1451             ed = prog_getedict(prog, OPA->edict);
1452             OPC->_int = ((qcint_t*)ed) - prog->entitydata.data() + OPB->_int;
1453             break;
1454
1455         case INSTR_STORE_F:
1456         case INSTR_STORE_S:
1457         case INSTR_STORE_ENT:
1458         case INSTR_STORE_FLD:
1459         case INSTR_STORE_FNC:
1460             OPB->_int = OPA->_int;
1461             break;
1462         case INSTR_STORE_V:
1463             OPB->ivector[0] = OPA->ivector[0];
1464             OPB->ivector[1] = OPA->ivector[1];
1465             OPB->ivector[2] = OPA->ivector[2];
1466             break;
1467
1468         case INSTR_STOREP_F:
1469         case INSTR_STOREP_S:
1470         case INSTR_STOREP_ENT:
1471         case INSTR_STOREP_FLD:
1472         case INSTR_STOREP_FNC:
1473             if (OPB->_int < 0 || OPB->_int >= (qcint_t)prog->entitydata.size()) {
1474                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
1475                 goto cleanup;
1476             }
1477             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
1478                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1479                           prog->filename.c_str(),
1480                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1481                           OPB->_int);
1482             ptr = (qcany_t*)&prog->entitydata[OPB->_int];
1483             ptr->_int = OPA->_int;
1484             break;
1485         case INSTR_STOREP_V:
1486             if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)prog->entitydata.size()) {
1487                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
1488                 goto cleanup;
1489             }
1490             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
1491                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1492                           prog->filename.c_str(),
1493                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1494                           OPB->_int);
1495             ptr = (qcany_t*)&prog->entitydata[OPB->_int];
1496             ptr->ivector[0] = OPA->ivector[0];
1497             ptr->ivector[1] = OPA->ivector[1];
1498             ptr->ivector[2] = OPA->ivector[2];
1499             break;
1500
1501         case INSTR_NOT_F:
1502             OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
1503             break;
1504         case INSTR_NOT_V:
1505             OPC->_float = !OPA->vector[0] &&
1506                           !OPA->vector[1] &&
1507                           !OPA->vector[2];
1508             break;
1509         case INSTR_NOT_S:
1510             OPC->_float = !OPA->string ||
1511                           !*prog_getstring(prog, OPA->string);
1512             break;
1513         case INSTR_NOT_ENT:
1514             OPC->_float = (OPA->edict == 0);
1515             break;
1516         case INSTR_NOT_FNC:
1517             OPC->_float = !OPA->function;
1518             break;
1519
1520         case INSTR_IF:
1521             /* this is consistent with darkplaces' behaviour */
1522             if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1523             {
1524                 st += st->o2.s1 - 1;    /* offset the s++ */
1525                 if (++jumpcount >= maxjumps)
1526                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1527             }
1528             break;
1529         case INSTR_IFNOT:
1530             if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1531             {
1532                 st += st->o2.s1 - 1;    /* offset the s++ */
1533                 if (++jumpcount >= maxjumps)
1534                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1535             }
1536             break;
1537
1538         case INSTR_CALL0:
1539         case INSTR_CALL1:
1540         case INSTR_CALL2:
1541         case INSTR_CALL3:
1542         case INSTR_CALL4:
1543         case INSTR_CALL5:
1544         case INSTR_CALL6:
1545         case INSTR_CALL7:
1546         case INSTR_CALL8:
1547             prog->argc = st->opcode - INSTR_CALL0;
1548             if (!OPA->function)
1549                 qcvmerror(prog, "nullptr function in `%s`", prog->filename.c_str());
1550
1551             if(!OPA->function || OPA->function >= (qcint_t)prog->functions.size())
1552             {
1553                 qcvmerror(prog, "CALL outside the program in `%s`", prog->filename.c_str());
1554                 goto cleanup;
1555             }
1556
1557             newf = &prog->functions[OPA->function];
1558             newf->profile++;
1559
1560             prog->statement = (st - &prog->code[0]) + 1;
1561
1562             if (newf->entry < 0)
1563             {
1564                 /* negative statements are built in functions */
1565                 qcint_t builtinnumber = -newf->entry;
1566                 if (builtinnumber < (qcint_t)prog->builtins_count && prog->builtins[builtinnumber])
1567                     prog->builtins[builtinnumber](prog);
1568                 else
1569                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
1570                               builtinnumber, prog->filename.c_str());
1571             }
1572             else
1573                 st = &prog->code[0] + prog_enterfunction(prog, newf) - 1; /* offset st++ */
1574             if (prog->vmerror)
1575                 goto cleanup;
1576             break;
1577
1578         case INSTR_STATE:
1579         {
1580             qcfloat_t *nextthink;
1581             qcfloat_t *time;
1582             qcfloat_t *frame;
1583             if (!prog->supports_state) {
1584                 qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename.c_str());
1585                 goto cleanup;
1586             }
1587             ed = prog_getedict(prog, prog->globals[prog->cached_globals.self]);
1588             ((qcint_t*)ed)[prog->cached_fields.think] = OPB->function;
1589
1590             frame     = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.frame];
1591             *frame    = OPA->_float;
1592             nextthink = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.nextthink];
1593             time      = (qcfloat_t*)(&prog->globals[0] + prog->cached_globals.time);
1594             *nextthink = *time + 0.1;
1595             break;
1596         }
1597
1598         case INSTR_GOTO:
1599             st += st->o1.s1 - 1;    /* offset the s++ */
1600             if (++jumpcount == 10000000)
1601                 qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1602             break;
1603
1604         case INSTR_AND:
1605             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
1606                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1607             break;
1608         case INSTR_OR:
1609             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
1610                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1611             break;
1612
1613         case INSTR_BITAND:
1614             OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
1615             break;
1616         case INSTR_BITOR:
1617             OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
1618             break;
1619     }
1620 }
1621
1622 #undef QCVM_PROFILE
1623 #undef QCVM_TRACE
1624 #endif /* !QCVM_LOOP */