]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
More function flatening
[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 int qc_floor(qc_program *prog)
829 {
830     qcany *num, out;
831     CheckArgs(1);
832     num = GetArg(0);
833     out._float = floor(num->_float);
834     Return(out);
835     return 0;
836 }
837
838 static prog_builtin qc_builtins[] = {
839     NULL,
840     &qc_print,       /*   1   */
841     &qc_ftos,        /*   2   */
842     &qc_spawn,       /*   3   */
843     &qc_kill,        /*   4   */
844     &qc_vtos,        /*   5   */
845     &qc_error,       /*   6   */
846     &qc_vlen,        /*   7   */
847     &qc_etos,        /*   8   */
848     &qc_stof,        /*   9   */
849     &qc_strcat,      /*   10  */
850     &qc_strcmp,      /*   11  */
851     &qc_normalize,   /*   12  */
852     &qc_sqrt,        /*   13  */
853     &qc_floor        /*   14  */
854 };
855 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
856
857 static const char *arg0 = NULL;
858
859 static void version() {
860     printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
861            GMQCC_VERSION_MAJOR,
862            GMQCC_VERSION_MINOR,
863            GMQCC_VERSION_PATCH,
864            __DATE__,
865            __TIME__
866     );
867 }
868
869 static void usage()
870 {
871     printf("usage: %s [options] [parameters] file\n", arg0);
872     printf("options:\n");
873     printf("  -h, --help         print this message\n"
874            "  -trace             trace the execution\n"
875            "  -profile           perform profiling during execution\n"
876            "  -info              print information from the prog's header\n"
877            "  -disasm            disassemble and exit\n"
878            "  -disasm-func func  disassemble and exit\n"
879            "  -printdefs         list the defs section\n"
880            "  -printfields       list the field section\n"
881            "  -printfuns         list functions information\n"
882            "  -v                 be verbose\n"
883            "  -vv                be even more verbose\n");
884     printf("parameters:\n");
885     printf("  -vector <V>   pass a vector parameter to main()\n"
886            "  -float  <f>   pass a float parameter to main()\n"
887            "  -string <s>   pass a string parameter to main() \n");
888 }
889
890 static void prog_main_setparams(qc_program *prog)
891 {
892     size_t i;
893     qcany *arg;
894
895     for (i = 0; i < vec_size(main_params); ++i) {
896         arg = GetGlobal(OFS_PARM0 + 3*i);
897         arg->vector[0] = 0;
898         arg->vector[1] = 0;
899         arg->vector[2] = 0;
900         switch (main_params[i].vtype) {
901             case TYPE_VECTOR:
902 #ifdef _MSC_VER
903                 (void)sscanf_s(main_params[i].value, " %f %f %f ",
904                                &arg->vector[0],
905                                &arg->vector[1],
906                                &arg->vector[2]);
907 #else
908                 (void)sscanf(main_params[i].value, " %f %f %f ",
909                              &arg->vector[0],
910                              &arg->vector[1],
911                              &arg->vector[2]);
912 #endif
913                 break;
914             case TYPE_FLOAT:
915                 arg->_float = atof(main_params[i].value);
916                 break;
917             case TYPE_STRING:
918                 arg->string = prog_tempstring(prog, main_params[i].value);
919                 break;
920             default:
921                 fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype);
922                 break;
923         }
924     }
925 }
926
927 void prog_disasm_function(qc_program *prog, size_t id);
928 int main(int argc, char **argv)
929 {
930     size_t      i;
931     qcint       fnmain = -1;
932     qc_program *prog;
933     size_t      xflags = VMXF_DEFAULT;
934     bool        opts_printfields = false;
935     bool        opts_printdefs   = false;
936     bool        opts_printfuns   = false;
937     bool        opts_disasm      = false;
938     bool        opts_info        = false;
939     bool        noexec           = false;
940     const char *progsfile        = NULL;
941     const char **dis_list        = NULL;
942     int         opts_v           = 0;
943
944     arg0 = argv[0];
945
946     if (argc < 2) {
947         usage();
948         exit(1);
949     }
950
951     while (argc > 1) {
952         if (!strcmp(argv[1], "-h") ||
953             !strcmp(argv[1], "-help") ||
954             !strcmp(argv[1], "--help"))
955         {
956             usage();
957             exit(0);
958         }
959         else if (!strcmp(argv[1], "-v")) {
960             ++opts_v;
961             --argc;
962             ++argv;
963         }
964         else if (!strncmp(argv[1], "-vv", 3)) {
965             const char *av = argv[1]+1;
966             for (; *av; ++av) {
967                 if (*av == 'v')
968                     ++opts_v;
969                 else {
970                     usage();
971                     exit(1);
972                 }
973             }
974             --argc;
975             ++argv;
976         }
977         else if (!strcmp(argv[1], "-version") ||
978                  !strcmp(argv[1], "--version"))
979         {
980             version();
981             exit(0);
982         }
983         else if (!strcmp(argv[1], "-trace")) {
984             --argc;
985             ++argv;
986             xflags |= VMXF_TRACE;
987         }
988         else if (!strcmp(argv[1], "-profile")) {
989             --argc;
990             ++argv;
991             xflags |= VMXF_PROFILE;
992         }
993         else if (!strcmp(argv[1], "-info")) {
994             --argc;
995             ++argv;
996             opts_info = true;
997             noexec = true;
998         }
999         else if (!strcmp(argv[1], "-disasm")) {
1000             --argc;
1001             ++argv;
1002             opts_disasm = true;
1003             noexec = true;
1004         }
1005         else if (!strcmp(argv[1], "-disasm-func")) {
1006             --argc;
1007             ++argv;
1008             if (argc <= 1) {
1009                 usage();
1010                 exit(1);
1011             }
1012             vec_push(dis_list, argv[1]);
1013             --argc;
1014             ++argv;
1015             noexec = true;
1016         }
1017         else if (!strcmp(argv[1], "-printdefs")) {
1018             --argc;
1019             ++argv;
1020             opts_printdefs = true;
1021             noexec = true;
1022         }
1023         else if (!strcmp(argv[1], "-printfuns")) {
1024             --argc;
1025             ++argv;
1026             opts_printfuns = true;
1027             noexec = true;
1028         }
1029         else if (!strcmp(argv[1], "-printfields")) {
1030             --argc;
1031             ++argv;
1032             opts_printfields = true;
1033             noexec = true;
1034         }
1035         else if (!strcmp(argv[1], "-vector") ||
1036                  !strcmp(argv[1], "-string") ||
1037                  !strcmp(argv[1], "-float") )
1038         {
1039             qcvm_parameter p;
1040             if (argv[1][1] == 'f')
1041                 p.vtype = TYPE_FLOAT;
1042             else if (argv[1][1] == 's')
1043                 p.vtype = TYPE_STRING;
1044             else if (argv[1][1] == 'v')
1045                 p.vtype = TYPE_VECTOR;
1046
1047             --argc;
1048             ++argv;
1049             if (argc < 2) {
1050                 usage();
1051                 exit(1);
1052             }
1053             p.value = argv[1];
1054
1055             vec_push(main_params, p);
1056             --argc;
1057             ++argv;
1058         }
1059         else if (!strcmp(argv[1], "--")) {
1060             --argc;
1061             ++argv;
1062             break;
1063         }
1064         else if (argv[1][0] != '-') {
1065             if (progsfile) {
1066                 fprintf(stderr, "only 1 program file may be specified\n");
1067                 usage();
1068                 exit(1);
1069             }
1070             progsfile = argv[1];
1071             --argc;
1072             ++argv;
1073         }
1074         else
1075         {
1076             fprintf(stderr, "unknown parameter: %s\n", argv[1]);
1077             usage();
1078             exit(1);
1079         }
1080     }
1081
1082     if (argc == 2 && !progsfile) {
1083         progsfile = argv[1];
1084         --argc;
1085         ++argv;
1086     }
1087
1088     if (!progsfile) {
1089         fprintf(stderr, "must specify a program to execute\n");
1090         usage();
1091         exit(1);
1092     }
1093
1094     prog = prog_load(progsfile, noexec);
1095     if (!prog) {
1096         fprintf(stderr, "failed to load program '%s'\n", progsfile);
1097         exit(1);
1098     }
1099
1100     prog->builtins       = qc_builtins;
1101     prog->builtins_count = qc_builtins_count;
1102
1103     if (opts_info) {
1104         printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16);
1105         printf("Entity field space: %u\n", (unsigned int)prog->entityfields);
1106         printf("Globals: %u\n", (unsigned int)vec_size(prog->globals));
1107         printf("Counts:\n"
1108                "      code: %lu\n"
1109                "      defs: %lu\n"
1110                "    fields: %lu\n"
1111                " functions: %lu\n"
1112                "   strings: %lu\n",
1113                (unsigned long)vec_size(prog->code),
1114                (unsigned long)vec_size(prog->defs),
1115                (unsigned long)vec_size(prog->fields),
1116                (unsigned long)vec_size(prog->functions),
1117                (unsigned long)vec_size(prog->strings));
1118     }
1119
1120     if (opts_info) {
1121         prog_delete(prog);
1122         return 0;
1123     }
1124     for (i = 0; i < vec_size(dis_list); ++i) {
1125         size_t k;
1126         printf("Looking for `%s`\n", dis_list[i]);
1127         for (k = 1; k < vec_size(prog->functions); ++k) {
1128             const char *name = prog_getstring(prog, prog->functions[k].name);
1129             if (!strcmp(name, dis_list[i])) {
1130                 prog_disasm_function(prog, k);
1131                 break;
1132             }
1133         }
1134     }
1135     if (opts_disasm) {
1136         for (i = 1; i < vec_size(prog->functions); ++i)
1137             prog_disasm_function(prog, i);
1138         return 0;
1139     }
1140     if (opts_printdefs) {
1141         for (i = 0; i < vec_size(prog->defs); ++i) {
1142             printf("Global: %8s %-16s at %u%s",
1143                    type_name[prog->defs[i].type & DEF_TYPEMASK],
1144                    prog_getstring(prog, prog->defs[i].name),
1145                    (unsigned int)prog->defs[i].offset,
1146                    ((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1147             if (opts_v) {
1148                 switch (prog->defs[i].type & DEF_TYPEMASK) {
1149                     case TYPE_FLOAT:
1150                         printf(" [init: %g]", ((qcany*)(prog->globals + prog->defs[i].offset))->_float);
1151                         break;
1152                     case TYPE_INTEGER:
1153                         printf(" [init: %i]", (int)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
1154                         break;
1155                     case TYPE_ENTITY:
1156                     case TYPE_FUNCTION:
1157                     case TYPE_FIELD:
1158                     case TYPE_POINTER:
1159                         printf(" [init: %u]", (unsigned)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
1160                         break;
1161                     case TYPE_STRING:
1162                         printf(" [init: `%s`]", prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string ));
1163                         break;
1164                     default:
1165                         break;
1166                 }
1167             }
1168             printf("\n");
1169         }
1170     }
1171     if (opts_printfields) {
1172         for (i = 0; i < vec_size(prog->fields); ++i) {
1173             printf("Field: %8s %-16s at %u%s\n",
1174                    type_name[prog->fields[i].type],
1175                    prog_getstring(prog, prog->fields[i].name),
1176                    (unsigned int)prog->fields[i].offset,
1177                    ((prog->fields[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1178         }
1179     }
1180     if (opts_printfuns) {
1181         for (i = 0; i < vec_size(prog->functions); ++i) {
1182             int32_t a;
1183             printf("Function: %-16s taking %i parameters:(",
1184                    prog_getstring(prog, prog->functions[i].name),
1185                    (unsigned int)prog->functions[i].nargs);
1186             for (a = 0; a < prog->functions[i].nargs; ++a) {
1187                 printf(" %i", prog->functions[i].argsize[a]);
1188             }
1189             if (opts_v > 1) {
1190                 int32_t start = prog->functions[i].entry;
1191                 if (start < 0)
1192                     printf(") builtin %i\n", (int)-start);
1193                 else {
1194                     size_t funsize = 0;
1195                     prog_section_statement *st = prog->code + start;
1196                     for (;st->opcode != INSTR_DONE; ++st)
1197                         ++funsize;
1198                     printf(") - %lu instructions", (unsigned long)funsize);
1199                     if (opts_v > 2) {
1200                         printf(" - locals: %i + %i\n",
1201                                prog->functions[i].firstlocal,
1202                                prog->functions[i].locals);
1203                     }
1204                     else
1205                         printf("\n");
1206                 }
1207             }
1208             else if (opts_v) {
1209                 printf(") locals: %i + %i\n",
1210                        prog->functions[i].firstlocal,
1211                        prog->functions[i].locals);
1212             }
1213             else
1214                 printf(")\n");
1215         }
1216     }
1217     if (!noexec) {
1218         for (i = 1; i < vec_size(prog->functions); ++i) {
1219             const char *name = prog_getstring(prog, prog->functions[i].name);
1220             if (!strcmp(name, "main"))
1221                 fnmain = (qcint)i;
1222         }
1223         if (fnmain > 0)
1224         {
1225             prog_main_setparams(prog);
1226             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
1227         }
1228         else
1229             fprintf(stderr, "No main function found\n");
1230     }
1231
1232     prog_delete(prog);
1233     return 0;
1234 }
1235
1236 void prog_disasm_function(qc_program *prog, size_t id)
1237 {
1238     prog_section_function *fdef = prog->functions + id;
1239     prog_section_statement *st;
1240
1241     if (fdef->entry < 0) {
1242         printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry);
1243         return;
1244     }
1245     else
1246         printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name));
1247
1248     st = prog->code + fdef->entry;
1249     while (st->opcode != INSTR_DONE) {
1250         prog_print_statement(prog, st);
1251         ++st;
1252     }
1253 }
1254 #endif
1255 #else /* !QCVM_LOOP */
1256 /*
1257  * Everything from here on is not including into the compilation of the
1258  * executor.  This is simply code that is #included via #include __FILE__
1259  * see when QCVM_LOOP is defined, the rest of the code above do not get
1260  * re-included.  So this really just acts like one large macro, but it
1261  * sort of isn't, which makes it nicer looking.
1262  */
1263
1264 #define OPA ( (qcany*) (prog->globals + st->o1.u1) )
1265 #define OPB ( (qcany*) (prog->globals + st->o2.u1) )
1266 #define OPC ( (qcany*) (prog->globals + st->o3.u1) )
1267
1268 #define GLOBAL(x) ( (qcany*) (prog->globals + (x)) )
1269
1270 /* to be consistent with current darkplaces behaviour */
1271 #if !defined(FLOAT_IS_TRUE_FOR_INT)
1272 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
1273 #endif
1274
1275 while (1) {
1276     prog_section_function  *newf;
1277     qcany          *ed;
1278     qcany          *ptr;
1279
1280     ++st;
1281
1282 #if QCVM_PROFILE
1283     prog->profile[st - prog->code]++;
1284 #endif
1285
1286 #if QCVM_TRACE
1287     prog_print_statement(prog, st);
1288 #endif
1289
1290     switch (st->opcode)
1291     {
1292         default:
1293             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
1294             goto cleanup;
1295
1296         case INSTR_DONE:
1297         case INSTR_RETURN:
1298             /* TODO: add instruction count to function profile count */
1299             GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
1300             GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
1301             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
1302
1303             st = prog->code + prog_leavefunction(prog);
1304             if (!vec_size(prog->stack))
1305                 goto cleanup;
1306
1307             break;
1308
1309         case INSTR_MUL_F:
1310             OPC->_float = OPA->_float * OPB->_float;
1311             break;
1312         case INSTR_MUL_V:
1313             OPC->_float = OPA->vector[0]*OPB->vector[0] +
1314                           OPA->vector[1]*OPB->vector[1] +
1315                           OPA->vector[2]*OPB->vector[2];
1316             break;
1317         case INSTR_MUL_FV:
1318         {
1319             qcfloat f = OPA->_float;
1320             OPC->vector[0] = f * OPB->vector[0];
1321             OPC->vector[1] = f * OPB->vector[1];
1322             OPC->vector[2] = f * OPB->vector[2];
1323             break;
1324         }
1325         case INSTR_MUL_VF:
1326         {
1327             qcfloat f = OPB->_float;
1328             OPC->vector[0] = f * OPA->vector[0];
1329             OPC->vector[1] = f * OPA->vector[1];
1330             OPC->vector[2] = f * OPA->vector[2];
1331             break;
1332         }
1333         case INSTR_DIV_F:
1334             if (OPB->_float != 0.0f)
1335                 OPC->_float = OPA->_float / OPB->_float;
1336             else
1337                 OPC->_float = 0;
1338             break;
1339
1340         case INSTR_ADD_F:
1341             OPC->_float = OPA->_float + OPB->_float;
1342             break;
1343         case INSTR_ADD_V:
1344             OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
1345             OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
1346             OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
1347             break;
1348         case INSTR_SUB_F:
1349             OPC->_float = OPA->_float - OPB->_float;
1350             break;
1351         case INSTR_SUB_V:
1352             OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
1353             OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
1354             OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
1355             break;
1356
1357         case INSTR_EQ_F:
1358             OPC->_float = (OPA->_float == OPB->_float);
1359             break;
1360         case INSTR_EQ_V:
1361             OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
1362                            (OPA->vector[1] == OPB->vector[1]) &&
1363                            (OPA->vector[2] == OPB->vector[2]) );
1364             break;
1365         case INSTR_EQ_S:
1366             OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
1367                                   prog_getstring(prog, OPB->string));
1368             break;
1369         case INSTR_EQ_E:
1370             OPC->_float = (OPA->_int == OPB->_int);
1371             break;
1372         case INSTR_EQ_FNC:
1373             OPC->_float = (OPA->function == OPB->function);
1374             break;
1375         case INSTR_NE_F:
1376             OPC->_float = (OPA->_float != OPB->_float);
1377             break;
1378         case INSTR_NE_V:
1379             OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
1380                            (OPA->vector[1] != OPB->vector[1]) ||
1381                            (OPA->vector[2] != OPB->vector[2]) );
1382             break;
1383         case INSTR_NE_S:
1384             OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
1385                                    prog_getstring(prog, OPB->string));
1386             break;
1387         case INSTR_NE_E:
1388             OPC->_float = (OPA->_int != OPB->_int);
1389             break;
1390         case INSTR_NE_FNC:
1391             OPC->_float = (OPA->function != OPB->function);
1392             break;
1393
1394         case INSTR_LE:
1395             OPC->_float = (OPA->_float <= OPB->_float);
1396             break;
1397         case INSTR_GE:
1398             OPC->_float = (OPA->_float >= OPB->_float);
1399             break;
1400         case INSTR_LT:
1401             OPC->_float = (OPA->_float < OPB->_float);
1402             break;
1403         case INSTR_GT:
1404             OPC->_float = (OPA->_float > OPB->_float);
1405             break;
1406
1407         case INSTR_LOAD_F:
1408         case INSTR_LOAD_S:
1409         case INSTR_LOAD_FLD:
1410         case INSTR_LOAD_ENT:
1411         case INSTR_LOAD_FNC:
1412             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1413                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1414                 goto cleanup;
1415             }
1416             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
1417                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1418                           prog->filename,
1419                           OPB->_int);
1420                 goto cleanup;
1421             }
1422             ed = prog_getedict(prog, OPA->edict);
1423             OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
1424             break;
1425         case INSTR_LOAD_V:
1426             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1427                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1428                 goto cleanup;
1429             }
1430             if (OPB->_int < 0 || OPB->_int + 3 > (qcint)prog->entityfields)
1431             {
1432                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1433                           prog->filename,
1434                           OPB->_int + 2);
1435                 goto cleanup;
1436             }
1437             ed = prog_getedict(prog, OPA->edict);
1438             ptr = (qcany*)( ((qcint*)ed) + OPB->_int );
1439             OPC->ivector[0] = ptr->ivector[0];
1440             OPC->ivector[1] = ptr->ivector[1];
1441             OPC->ivector[2] = ptr->ivector[2];
1442             break;
1443
1444         case INSTR_ADDRESS:
1445             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1446                 qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
1447                 goto cleanup;
1448             }
1449             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
1450             {
1451                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1452                           prog->filename,
1453                           OPB->_int);
1454                 goto cleanup;
1455             }
1456
1457             ed = prog_getedict(prog, OPA->edict);
1458             OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
1459             break;
1460
1461         case INSTR_STORE_F:
1462         case INSTR_STORE_S:
1463         case INSTR_STORE_ENT:
1464         case INSTR_STORE_FLD:
1465         case INSTR_STORE_FNC:
1466             OPB->_int = OPA->_int;
1467             break;
1468         case INSTR_STORE_V:
1469             OPB->ivector[0] = OPA->ivector[0];
1470             OPB->ivector[1] = OPA->ivector[1];
1471             OPB->ivector[2] = OPA->ivector[2];
1472             break;
1473
1474         case INSTR_STOREP_F:
1475         case INSTR_STOREP_S:
1476         case INSTR_STOREP_ENT:
1477         case INSTR_STOREP_FLD:
1478         case INSTR_STOREP_FNC:
1479             if (OPB->_int < 0 || OPB->_int >= (qcint)vec_size(prog->entitydata)) {
1480                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1481                 goto cleanup;
1482             }
1483             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1484                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1485                           prog->filename,
1486                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1487                           OPB->_int);
1488             ptr = (qcany*)(prog->entitydata + OPB->_int);
1489             ptr->_int = OPA->_int;
1490             break;
1491         case INSTR_STOREP_V:
1492             if (OPB->_int < 0 || OPB->_int + 2 >= (qcint)vec_size(prog->entitydata)) {
1493                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1494                 goto cleanup;
1495             }
1496             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1497                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1498                           prog->filename,
1499                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1500                           OPB->_int);
1501             ptr = (qcany*)(prog->entitydata + OPB->_int);
1502             ptr->ivector[0] = OPA->ivector[0];
1503             ptr->ivector[1] = OPA->ivector[1];
1504             ptr->ivector[2] = OPA->ivector[2];
1505             break;
1506
1507         case INSTR_NOT_F:
1508             OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
1509             break;
1510         case INSTR_NOT_V:
1511             OPC->_float = !OPA->vector[0] &&
1512                           !OPA->vector[1] &&
1513                           !OPA->vector[2];
1514             break;
1515         case INSTR_NOT_S:
1516             OPC->_float = !OPA->string ||
1517                           !*prog_getstring(prog, OPA->string);
1518             break;
1519         case INSTR_NOT_ENT:
1520             OPC->_float = (OPA->edict == 0);
1521             break;
1522         case INSTR_NOT_FNC:
1523             OPC->_float = !OPA->function;
1524             break;
1525
1526         case INSTR_IF:
1527             /* this is consistent with darkplaces' behaviour */
1528             if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1529             {
1530                 st += st->o2.s1 - 1;    /* offset the s++ */
1531                 if (++jumpcount >= maxjumps)
1532                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1533             }
1534             break;
1535         case INSTR_IFNOT:
1536             if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1537             {
1538                 st += st->o2.s1 - 1;    /* offset the s++ */
1539                 if (++jumpcount >= maxjumps)
1540                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1541             }
1542             break;
1543
1544         case INSTR_CALL0:
1545         case INSTR_CALL1:
1546         case INSTR_CALL2:
1547         case INSTR_CALL3:
1548         case INSTR_CALL4:
1549         case INSTR_CALL5:
1550         case INSTR_CALL6:
1551         case INSTR_CALL7:
1552         case INSTR_CALL8:
1553             prog->argc = st->opcode - INSTR_CALL0;
1554             if (!OPA->function)
1555                 qcvmerror(prog, "NULL function in `%s`", prog->filename);
1556
1557             if(!OPA->function || OPA->function >= (qcint)vec_size(prog->functions))
1558             {
1559                 qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
1560                 goto cleanup;
1561             }
1562
1563             newf = &prog->functions[OPA->function];
1564             newf->profile++;
1565
1566             prog->statement = (st - prog->code) + 1;
1567
1568             if (newf->entry < 0)
1569             {
1570                 /* negative statements are built in functions */
1571                 qcint builtinnumber = -newf->entry;
1572                 if (builtinnumber < (qcint)prog->builtins_count && prog->builtins[builtinnumber])
1573                     prog->builtins[builtinnumber](prog);
1574                 else
1575                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
1576                               builtinnumber, prog->filename);
1577             }
1578             else
1579                 st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
1580             if (prog->vmerror)
1581                 goto cleanup;
1582             break;
1583
1584         case INSTR_STATE:
1585             qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
1586             break;
1587
1588         case INSTR_GOTO:
1589             st += st->o1.s1 - 1;    /* offset the s++ */
1590             if (++jumpcount == 10000000)
1591                 qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1592             break;
1593
1594         case INSTR_AND:
1595             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
1596                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1597             break;
1598         case INSTR_OR:
1599             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
1600                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1601             break;
1602
1603         case INSTR_BITAND:
1604             OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
1605             break;
1606         case INSTR_BITOR:
1607             OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
1608             break;
1609     }
1610 }
1611
1612 #undef QCVM_PROFILE
1613 #undef QCVM_TRACE
1614 #endif /* !QCVM_LOOP */