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