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