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