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