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