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