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