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