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