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