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