]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
Uhh I did it again
[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)
55 {
56     qc_program   *prog;
57     prog_header   header;
58     FILE         *file   = file_open(filename, "rb");
59
60     if (!file)
61         return NULL;
62
63     if (file_read(&header, sizeof(header), 1, file) != 1) {
64         loaderror("failed to read header from '%s'", filename);
65         file_close(file);
66         return NULL;
67     }
68
69     if (header.version != 6) {
70         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
71         file_close(file);
72         return NULL;
73     }
74
75     prog = (qc_program*)mem_a(sizeof(qc_program));
76     if (!prog) {
77         file_close(file);
78         printf("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 (file_seek(file, header.hdrvar.offset, SEEK_SET) != 0) {        \
94         loaderror("seek failed");                                      \
95         goto error;                                                    \
96     }                                                                  \
97     if (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     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         printf("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         printf("Trying to free world entity\n");
231         return;
232     }
233     if (e >= (qcint)vec_size(prog->entitypool)) {
234         prog->vmerror++;
235         printf("Trying to free out of bounds entity\n");
236         return;
237     }
238     if (!prog->entitypool[e]) {
239         prog->vmerror++;
240         printf("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         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     fflush(stdout);
459 }
460
461 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
462 {
463     qc_exec_stack st;
464     size_t  parampos;
465     int32_t p;
466
467     /* back up locals */
468     st.localsp  = vec_size(prog->localstack);
469     st.stmt     = prog->statement;
470     st.function = func;
471
472     if (prog->xflags & VMXF_TRACE) {
473         const char *str = prog_getstring(prog, func->name);
474         vec_push(prog->function_stack, str);
475     }
476
477 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
478     if (vec_size(prog->stack))
479     {
480         prog_section_function *cur;
481         cur = prog->stack[vec_size(prog->stack)-1].function;
482         if (cur)
483         {
484             qcint *globals = prog->globals + cur->firstlocal;
485             vec_append(prog->localstack, cur->locals, globals);
486         }
487     }
488 #else
489     {
490         qcint *globals = prog->globals + func->firstlocal;
491         vec_append(prog->localstack, func->locals, globals);
492     }
493 #endif
494
495     /* copy parameters */
496     parampos = func->firstlocal;
497     for (p = 0; p < func->nargs; ++p)
498     {
499         size_t s;
500         for (s = 0; s < func->argsize[p]; ++s) {
501             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
502             ++parampos;
503         }
504     }
505
506     vec_push(prog->stack, st);
507
508     return func->entry;
509 }
510
511 static qcint prog_leavefunction(qc_program *prog)
512 {
513     prog_section_function *prev = NULL;
514     size_t oldsp;
515
516     qc_exec_stack st = vec_last(prog->stack);
517
518     if (prog->xflags & VMXF_TRACE) {
519         if (vec_size(prog->function_stack))
520             vec_pop(prog->function_stack);
521     }
522
523 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
524     if (vec_size(prog->stack) > 1) {
525         prev  = prog->stack[vec_size(prog->stack)-2].function;
526         oldsp = prog->stack[vec_size(prog->stack)-2].localsp;
527     }
528 #else
529     prev  = prog->stack[vec_size(prog->stack)-1].function;
530     oldsp = prog->stack[vec_size(prog->stack)-1].localsp;
531 #endif
532     if (prev) {
533         qcint *globals = prog->globals + prev->firstlocal;
534         memcpy(globals, prog->localstack + oldsp, prev->locals * sizeof(prog->localstack[0]));
535         /* vec_remove(prog->localstack, oldsp, vec_size(prog->localstack)-oldsp); */
536         vec_shrinkto(prog->localstack, oldsp);
537     }
538
539     vec_pop(prog->stack);
540
541     return st.stmt - 1; /* offset the ++st */
542 }
543
544 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
545 {
546     long jumpcount = 0;
547     size_t oldxflags = prog->xflags;
548     prog_section_statement *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 #if defined(QCVM_EXECUTOR)
599 #include <math.h>
600
601 opts_cmd_t opts;
602
603 const char *type_name[TYPE_COUNT] = {
604     "void",
605     "string",
606     "float",
607     "vector",
608     "entity",
609     "field",
610     "function",
611     "pointer",
612     "integer",
613
614     "variant",
615
616     "struct",
617     "union",
618     "array",
619
620     "nil",
621     "noexpr"
622 };
623
624 typedef struct {
625     int         vtype;
626     const char *value;
627 } qcvm_parameter;
628
629 qcvm_parameter *main_params = NULL;
630
631 #define CheckArgs(num) do {                                                    \
632     if (prog->argc != (num)) {                                                 \
633         prog->vmerror++;                                                       \
634         printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
635         __FUNCTION__, prog->argc, (num));                                      \
636         return -1;                                                             \
637     }                                                                          \
638 } while (0)
639
640 #define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
641 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
642 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
643
644 static int qc_print(qc_program *prog)
645 {
646     size_t i;
647     const char *laststr = NULL;
648     for (i = 0; i < (size_t)prog->argc; ++i) {
649         qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
650         laststr = prog_getstring(prog, str->string);
651         printf("%s", laststr);
652     }
653     if (laststr && (prog->xflags & VMXF_TRACE)) {
654         size_t len = strlen(laststr);
655         if (!len || laststr[len-1] != '\n')
656             printf("\n");
657     }
658     return 0;
659 }
660
661 static int qc_error(qc_program *prog)
662 {
663     printf("*** VM raised an error:\n");
664     qc_print(prog);
665     prog->vmerror++;
666     return -1;
667 }
668
669 static int qc_ftos(qc_program *prog)
670 {
671     char buffer[512];
672     qcany *num;
673     qcany str;
674     CheckArgs(1);
675     num = GetArg(0);
676     snprintf(buffer, sizeof(buffer), "%g", num->_float);
677     str.string = prog_tempstring(prog, buffer);
678     Return(str);
679     return 0;
680 }
681
682 static int qc_stof(qc_program *prog)
683 {
684     qcany *str;
685     qcany num;
686     CheckArgs(1);
687     str = GetArg(0);
688     num._float = strtof(prog_getstring(prog, str->string), NULL);
689     Return(num);
690     return 0;
691 }
692
693 static int qc_vtos(qc_program *prog)
694 {
695     char buffer[512];
696     qcany *num;
697     qcany str;
698     CheckArgs(1);
699     num = GetArg(0);
700     snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]);
701     str.string = prog_tempstring(prog, buffer);
702     Return(str);
703     return 0;
704 }
705
706 static int qc_etos(qc_program *prog)
707 {
708     char buffer[512];
709     qcany *num;
710     qcany str;
711     CheckArgs(1);
712     num = GetArg(0);
713     snprintf(buffer, sizeof(buffer), "%i", num->_int);
714     str.string = prog_tempstring(prog, buffer);
715     Return(str);
716     return 0;
717 }
718
719 static int qc_spawn(qc_program *prog)
720 {
721     qcany ent;
722     CheckArgs(0);
723     ent.edict = prog_spawn_entity(prog);
724     Return(ent);
725     return (ent.edict ? 0 : -1);
726 }
727
728 static int qc_kill(qc_program *prog)
729 {
730     qcany *ent;
731     CheckArgs(1);
732     ent = GetArg(0);
733     prog_free_entity(prog, ent->edict);
734     return 0;
735 }
736
737 static int qc_sqrt(qc_program *prog)
738 {
739     qcany *num, out;
740     CheckArgs(1);
741     num = GetArg(0);
742     out._float = sqrt(num->_float);
743     Return(out);
744     return 0;
745 }
746
747 static int qc_vlen(qc_program *prog)
748 {
749     qcany *vec, len;
750     CheckArgs(1);
751     vec = GetArg(0);
752     len._float = sqrt(vec->vector[0] * vec->vector[0] +
753                       vec->vector[1] * vec->vector[1] +
754                       vec->vector[2] * vec->vector[2]);
755     Return(len);
756     return 0;
757 }
758
759 static int qc_normalize(qc_program *prog)
760 {
761     double len;
762     qcany *vec;
763     qcany out;
764     CheckArgs(1);
765     vec = GetArg(0);
766     len = sqrt(vec->vector[0] * vec->vector[0] +
767                vec->vector[1] * vec->vector[1] +
768                vec->vector[2] * vec->vector[2]);
769     if (len)
770         len = 1.0 / len;
771     else
772         len = 0;
773     out.vector[0] = len * vec->vector[0];
774     out.vector[1] = len * vec->vector[1];
775     out.vector[2] = len * vec->vector[2];
776     Return(out);
777     return 0;
778 }
779
780 static int qc_strcat(qc_program *prog)
781 {
782     char  *buffer;
783     size_t len1,   len2;
784     char  *cstr1, *cstr2;
785     qcany *str1,  *str2;
786     qcany  out;
787
788     CheckArgs(2);
789     str1 = GetArg(0);
790     str2 = GetArg(1);
791     cstr1 = prog_getstring(prog, str1->string);
792     cstr2 = prog_getstring(prog, str2->string);
793     len1 = strlen(cstr1);
794     len2 = strlen(cstr2);
795     buffer = (char*)mem_a(len1 + len2 + 1);
796     memcpy(buffer, cstr1, len1);
797     memcpy(buffer+len1, cstr2, len2+1);
798     out.string = prog_tempstring(prog, buffer);
799     mem_d(buffer);
800     Return(out);
801     return 0;
802 }
803
804 static int qc_strcmp(qc_program *prog)
805 {
806     char  *cstr1, *cstr2;
807     qcany *str1,  *str2;
808     qcany out;
809
810     if (prog->argc != 2 && prog->argc != 3) {
811         printf("ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
812                prog->argc);
813         return -1;
814     }
815
816     str1 = GetArg(0);
817     str2 = GetArg(1);
818     cstr1 = prog_getstring(prog, str1->string);
819     cstr2 = prog_getstring(prog, str2->string);
820     if (prog->argc == 3)
821         out._float = strncmp(cstr1, cstr2, GetArg(2)->_float);
822     else
823         out._float = strcmp(cstr1, cstr2);
824     Return(out);
825     return 0;
826 }
827
828 static prog_builtin qc_builtins[] = {
829     NULL,
830     &qc_print,       /*   1   */
831     &qc_ftos,        /*   2   */
832     &qc_spawn,       /*   3   */
833     &qc_kill,        /*   4   */
834     &qc_vtos,        /*   5   */
835     &qc_error,       /*   6   */
836     &qc_vlen,        /*   7   */
837     &qc_etos,        /*   8   */
838     &qc_stof,        /*   9   */
839     &qc_strcat,      /*   10  */
840     &qc_strcmp,      /*   11  */
841     &qc_normalize,   /*   12  */
842     &qc_sqrt         /*   13  */
843 };
844 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
845
846 static const char *arg0 = NULL;
847
848 static void version() {
849     printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
850            GMQCC_VERSION_MAJOR,
851            GMQCC_VERSION_MINOR,
852            GMQCC_VERSION_PATCH,
853            __DATE__,
854            __TIME__
855     );
856 }
857
858 static void usage()
859 {
860     printf("usage: %s [options] [parameters] file\n", arg0);
861     printf("options:\n");
862     printf("  -h, --help         print this message\n"
863            "  -trace             trace the execution\n"
864            "  -profile           perform profiling during execution\n"
865            "  -info              print information from the prog's header\n"
866            "  -disasm            disassemble and exit\n"
867            "  -disasm-func func  disassemble and exit\n"
868            "  -printdefs         list the defs section\n"
869            "  -printfields       list the field section\n"
870            "  -printfuns         list functions information\n"
871            "  -v                 be verbose\n"
872            "  -vv                be even more verbose\n");
873     printf("parameters:\n");
874     printf("  -vector <V>   pass a vector parameter to main()\n"
875            "  -float  <f>   pass a float parameter to main()\n"
876            "  -string <s>   pass a string parameter to main() \n");
877 }
878
879 static void prog_main_setparams(qc_program *prog)
880 {
881     size_t i;
882     qcany *arg;
883
884     for (i = 0; i < vec_size(main_params); ++i) {
885         arg = GetGlobal(OFS_PARM0 + 3*i);
886         arg->vector[0] = 0;
887         arg->vector[1] = 0;
888         arg->vector[2] = 0;
889         switch (main_params[i].vtype) {
890             case TYPE_VECTOR:
891 #ifdef _MSC_VER
892                 (void)sscanf_s(main_params[i].value, " %f %f %f ",
893                                &arg->vector[0],
894                                &arg->vector[1],
895                                &arg->vector[2]);
896 #else
897                 (void)sscanf(main_params[i].value, " %f %f %f ",
898                              &arg->vector[0],
899                              &arg->vector[1],
900                              &arg->vector[2]);
901 #endif
902                 break;
903             case TYPE_FLOAT:
904                 arg->_float = atof(main_params[i].value);
905                 break;
906             case TYPE_STRING:
907                 arg->string = prog_tempstring(prog, main_params[i].value);
908                 break;
909             default:
910                 printf("error: unhandled parameter type: %i\n", main_params[i].vtype);
911                 break;
912         }
913     }
914 }
915
916 void prog_disasm_function(qc_program *prog, size_t id);
917 int main(int argc, char **argv)
918 {
919     size_t      i;
920     qcint       fnmain = -1;
921     qc_program *prog;
922     size_t      xflags = VMXF_DEFAULT;
923     bool        opts_printfields = false;
924     bool        opts_printdefs   = false;
925     bool        opts_printfuns   = false;
926     bool        opts_disasm      = false;
927     bool        opts_info        = false;
928     bool        noexec           = false;
929     const char *progsfile        = NULL;
930     const char **dis_list        = NULL;
931     int         opts_v           = 0;
932
933     arg0 = argv[0];
934
935     if (argc < 2) {
936         usage();
937         exit(1);
938     }
939
940     while (argc > 1) {
941         if (!strcmp(argv[1], "-h") ||
942             !strcmp(argv[1], "-help") ||
943             !strcmp(argv[1], "--help"))
944         {
945             usage();
946             exit(0);
947         }
948         else if (!strcmp(argv[1], "-v")) {
949             ++opts_v;
950             --argc;
951             ++argv;
952         }
953         else if (!strncmp(argv[1], "-vv", 3)) {
954             const char *av = argv[1]+1;
955             for (; *av; ++av) {
956                 if (*av == 'v')
957                     ++opts_v;
958                 else {
959                     usage();
960                     exit(1);
961                 }
962             }
963             --argc;
964             ++argv;
965         }
966         else if (!strcmp(argv[1], "-version") ||
967                  !strcmp(argv[1], "--version"))
968         {
969             version();
970             exit(0);
971         }
972         else if (!strcmp(argv[1], "-trace")) {
973             --argc;
974             ++argv;
975             xflags |= VMXF_TRACE;
976         }
977         else if (!strcmp(argv[1], "-profile")) {
978             --argc;
979             ++argv;
980             xflags |= VMXF_PROFILE;
981         }
982         else if (!strcmp(argv[1], "-info")) {
983             --argc;
984             ++argv;
985             opts_info = true;
986             noexec = true;
987         }
988         else if (!strcmp(argv[1], "-disasm")) {
989             --argc;
990             ++argv;
991             opts_disasm = true;
992             noexec = true;
993         }
994         else if (!strcmp(argv[1], "-disasm-func")) {
995             --argc;
996             ++argv;
997             if (argc <= 1) {
998                 usage();
999                 exit(1);
1000             }
1001             vec_push(dis_list, argv[1]);
1002             --argc;
1003             ++argv;
1004             noexec = true;
1005         }
1006         else if (!strcmp(argv[1], "-printdefs")) {
1007             --argc;
1008             ++argv;
1009             opts_printdefs = true;
1010             noexec = true;
1011         }
1012         else if (!strcmp(argv[1], "-printfuns")) {
1013             --argc;
1014             ++argv;
1015             opts_printfuns = true;
1016             noexec = true;
1017         }
1018         else if (!strcmp(argv[1], "-printfields")) {
1019             --argc;
1020             ++argv;
1021             opts_printfields = true;
1022             noexec = true;
1023         }
1024         else if (!strcmp(argv[1], "-vector") ||
1025                  !strcmp(argv[1], "-string") ||
1026                  !strcmp(argv[1], "-float") )
1027         {
1028             qcvm_parameter p;
1029             if (argv[1][1] == 'f')
1030                 p.vtype = TYPE_FLOAT;
1031             else if (argv[1][1] == 's')
1032                 p.vtype = TYPE_STRING;
1033             else if (argv[1][1] == 'v')
1034                 p.vtype = TYPE_VECTOR;
1035
1036             --argc;
1037             ++argv;
1038             if (argc < 2) {
1039                 usage();
1040                 exit(1);
1041             }
1042             p.value = argv[1];
1043
1044             vec_push(main_params, p);
1045             --argc;
1046             ++argv;
1047         }
1048         else if (!strcmp(argv[1], "--")) {
1049             --argc;
1050             ++argv;
1051             break;
1052         }
1053         else if (argv[1][0] != '-') {
1054             if (progsfile) {
1055                 printf("only 1 program file may be specified\n");
1056                 usage();
1057                 exit(1);
1058             }
1059             progsfile = argv[1];
1060             --argc;
1061             ++argv;
1062         }
1063         else
1064         {
1065             printf("unknown parameter: %s\n", argv[1]);
1066             usage();
1067             exit(1);
1068         }
1069     }
1070
1071     if (argc == 2 && !progsfile) {
1072         progsfile = argv[1];
1073         --argc;
1074         ++argv;
1075     }
1076
1077     if (!progsfile) {
1078         printf("must specify a program to execute\n");
1079         usage();
1080         exit(1);
1081     }
1082
1083     prog = prog_load(progsfile);
1084     if (!prog) {
1085         printf("failed to load program '%s'\n", progsfile);
1086         exit(1);
1087     }
1088
1089     prog->builtins       = qc_builtins;
1090     prog->builtins_count = qc_builtins_count;
1091
1092     if (opts_info) {
1093         printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16);
1094         printf("Entity field space: %u\n", (unsigned int)prog->entityfields);
1095         printf("Globals: %u\n", (unsigned int)vec_size(prog->globals));
1096         printf("Counts:\n"
1097                "      code: %lu\n"
1098                "      defs: %lu\n"
1099                "    fields: %lu\n"
1100                " functions: %lu\n"
1101                "   strings: %lu\n",
1102                (unsigned long)vec_size(prog->code),
1103                (unsigned long)vec_size(prog->defs),
1104                (unsigned long)vec_size(prog->fields),
1105                (unsigned long)vec_size(prog->functions),
1106                (unsigned long)vec_size(prog->strings));
1107     }
1108
1109     if (opts_info) {
1110         prog_delete(prog);
1111         return 0;
1112     }
1113     for (i = 0; i < vec_size(dis_list); ++i) {
1114         size_t k;
1115         printf("Looking for `%s`\n", dis_list[i]);
1116         for (k = 1; k < vec_size(prog->functions); ++k) {
1117             const char *name = prog_getstring(prog, prog->functions[k].name);
1118             if (!strcmp(name, dis_list[i])) {
1119                 prog_disasm_function(prog, k);
1120                 break;
1121             }
1122         }
1123     }
1124     if (opts_disasm) {
1125         for (i = 1; i < vec_size(prog->functions); ++i)
1126             prog_disasm_function(prog, i);
1127         return 0;
1128     }
1129     if (opts_printdefs) {
1130         for (i = 0; i < vec_size(prog->defs); ++i) {
1131             printf("Global: %8s %-16s at %u%s\n",
1132                    type_name[prog->defs[i].type & DEF_TYPEMASK],
1133                    prog_getstring(prog, prog->defs[i].name),
1134                    (unsigned int)prog->defs[i].offset,
1135                    ((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1136         }
1137     }
1138     if (opts_printfields) {
1139         for (i = 0; i < vec_size(prog->fields); ++i) {
1140             printf("Field: %8s %-16s at %u%s\n",
1141                    type_name[prog->fields[i].type],
1142                    prog_getstring(prog, prog->fields[i].name),
1143                    (unsigned int)prog->fields[i].offset,
1144                    ((prog->fields[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1145         }
1146     }
1147     if (opts_printfuns) {
1148         for (i = 0; i < vec_size(prog->functions); ++i) {
1149             int32_t a;
1150             printf("Function: %-16s taking %i parameters:(",
1151                    prog_getstring(prog, prog->functions[i].name),
1152                    (unsigned int)prog->functions[i].nargs);
1153             for (a = 0; a < prog->functions[i].nargs; ++a) {
1154                 printf(" %i", prog->functions[i].argsize[a]);
1155             }
1156             if (opts_v > 1) {
1157                 int32_t start = prog->functions[i].entry;
1158                 if (start < 0)
1159                     printf(") builtin %i\n", (int)-start);
1160                 else {
1161                     size_t funsize = 0;
1162                     prog_section_statement *st = prog->code + start;
1163                     for (;st->opcode != INSTR_DONE; ++st)
1164                         ++funsize;
1165                     printf(") - %lu instructions", (unsigned long)funsize);
1166                     if (opts_v > 2) {
1167                         printf(" - locals: %i + %i\n",
1168                                prog->functions[i].firstlocal,
1169                                prog->functions[i].locals);
1170                     }
1171                     else
1172                         printf("\n");
1173                 }
1174             }
1175             else if (opts_v) {
1176                 printf(") locals: %i + %i\n",
1177                        prog->functions[i].firstlocal,
1178                        prog->functions[i].locals);
1179             }
1180             else
1181                 printf(")\n");
1182         }
1183     }
1184     if (!noexec) {
1185         for (i = 1; i < vec_size(prog->functions); ++i) {
1186             const char *name = prog_getstring(prog, prog->functions[i].name);
1187             if (!strcmp(name, "main"))
1188                 fnmain = (qcint)i;
1189         }
1190         if (fnmain > 0)
1191         {
1192             prog_main_setparams(prog);
1193             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
1194         }
1195         else
1196             printf("No main function found\n");
1197     }
1198
1199     prog_delete(prog);
1200     return 0;
1201 }
1202
1203 void prog_disasm_function(qc_program *prog, size_t id)
1204 {
1205     prog_section_function *fdef = prog->functions + id;
1206     prog_section_statement *st;
1207
1208     if (fdef->entry < 0) {
1209         printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry);
1210         return;
1211     }
1212     else
1213         printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name));
1214
1215     st = prog->code + fdef->entry;
1216     while (st->opcode != INSTR_DONE) {
1217         prog_print_statement(prog, st);
1218         ++st;
1219     }
1220 }
1221 #endif
1222 #else /* !QCVM_LOOP */
1223 /*
1224  * Everything from here on is not including into the compilation of the
1225  * executor.  This is simply code that is #included via #include __FILE__
1226  * see when QCVM_LOOP is defined, the rest of the code above do not get
1227  * re-included.  So this really just acts like one large macro, but it
1228  * sort of isn't, which makes it nicer looking.
1229  */
1230
1231 #define OPA ( (qcany*) (prog->globals + st->o1.u1) )
1232 #define OPB ( (qcany*) (prog->globals + st->o2.u1) )
1233 #define OPC ( (qcany*) (prog->globals + st->o3.u1) )
1234
1235 #define GLOBAL(x) ( (qcany*) (prog->globals + (x)) )
1236
1237 /* to be consistent with current darkplaces behaviour */
1238 #if !defined(FLOAT_IS_TRUE_FOR_INT)
1239 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
1240 #endif
1241
1242 while (1) {
1243     prog_section_function  *newf;
1244     qcany          *ed;
1245     qcany          *ptr;
1246
1247     ++st;
1248
1249 #if QCVM_PROFILE
1250     prog->profile[st - prog->code]++;
1251 #endif
1252
1253 #if QCVM_TRACE
1254     prog_print_statement(prog, st);
1255 #endif
1256
1257     switch (st->opcode)
1258     {
1259         default:
1260             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
1261             goto cleanup;
1262
1263         case INSTR_DONE:
1264         case INSTR_RETURN:
1265             /* TODO: add instruction count to function profile count */
1266             GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
1267             GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
1268             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
1269
1270             st = prog->code + prog_leavefunction(prog);
1271             if (!vec_size(prog->stack))
1272                 goto cleanup;
1273
1274             break;
1275
1276         case INSTR_MUL_F:
1277             OPC->_float = OPA->_float * OPB->_float;
1278             break;
1279         case INSTR_MUL_V:
1280             OPC->_float = OPA->vector[0]*OPB->vector[0] +
1281                           OPA->vector[1]*OPB->vector[1] +
1282                           OPA->vector[2]*OPB->vector[2];
1283             break;
1284         case INSTR_MUL_FV:
1285             OPC->vector[0] = OPA->_float * OPB->vector[0];
1286             OPC->vector[1] = OPA->_float * OPB->vector[1];
1287             OPC->vector[2] = OPA->_float * OPB->vector[2];
1288             break;
1289         case INSTR_MUL_VF:
1290             OPC->vector[0] = OPB->_float * OPA->vector[0];
1291             OPC->vector[1] = OPB->_float * OPA->vector[1];
1292             OPC->vector[2] = OPB->_float * OPA->vector[2];
1293             break;
1294         case INSTR_DIV_F:
1295             if (OPB->_float != 0.0f)
1296                 OPC->_float = OPA->_float / OPB->_float;
1297             else
1298                 OPC->_float = 0;
1299             break;
1300
1301         case INSTR_ADD_F:
1302             OPC->_float = OPA->_float + OPB->_float;
1303             break;
1304         case INSTR_ADD_V:
1305             OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
1306             OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
1307             OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
1308             break;
1309         case INSTR_SUB_F:
1310             OPC->_float = OPA->_float - OPB->_float;
1311             break;
1312         case INSTR_SUB_V:
1313             OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
1314             OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
1315             OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
1316             break;
1317
1318         case INSTR_EQ_F:
1319             OPC->_float = (OPA->_float == OPB->_float);
1320             break;
1321         case INSTR_EQ_V:
1322             OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
1323                            (OPA->vector[1] == OPB->vector[1]) &&
1324                            (OPA->vector[2] == OPB->vector[2]) );
1325             break;
1326         case INSTR_EQ_S:
1327             OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
1328                                   prog_getstring(prog, OPB->string));
1329             break;
1330         case INSTR_EQ_E:
1331             OPC->_float = (OPA->_int == OPB->_int);
1332             break;
1333         case INSTR_EQ_FNC:
1334             OPC->_float = (OPA->function == OPB->function);
1335             break;
1336         case INSTR_NE_F:
1337             OPC->_float = (OPA->_float != OPB->_float);
1338             break;
1339         case INSTR_NE_V:
1340             OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
1341                            (OPA->vector[1] != OPB->vector[1]) ||
1342                            (OPA->vector[2] != OPB->vector[2]) );
1343             break;
1344         case INSTR_NE_S:
1345             OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
1346                                    prog_getstring(prog, OPB->string));
1347             break;
1348         case INSTR_NE_E:
1349             OPC->_float = (OPA->_int != OPB->_int);
1350             break;
1351         case INSTR_NE_FNC:
1352             OPC->_float = (OPA->function != OPB->function);
1353             break;
1354
1355         case INSTR_LE:
1356             OPC->_float = (OPA->_float <= OPB->_float);
1357             break;
1358         case INSTR_GE:
1359             OPC->_float = (OPA->_float >= OPB->_float);
1360             break;
1361         case INSTR_LT:
1362             OPC->_float = (OPA->_float < OPB->_float);
1363             break;
1364         case INSTR_GT:
1365             OPC->_float = (OPA->_float > OPB->_float);
1366             break;
1367
1368         case INSTR_LOAD_F:
1369         case INSTR_LOAD_S:
1370         case INSTR_LOAD_FLD:
1371         case INSTR_LOAD_ENT:
1372         case INSTR_LOAD_FNC:
1373             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1374                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1375                 goto cleanup;
1376             }
1377             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
1378                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1379                           prog->filename,
1380                           OPB->_int);
1381                 goto cleanup;
1382             }
1383             ed = prog_getedict(prog, OPA->edict);
1384             OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
1385             break;
1386         case INSTR_LOAD_V:
1387             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1388                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1389                 goto cleanup;
1390             }
1391             if (OPB->_int < 0 || OPB->_int + 3 > (qcint)prog->entityfields)
1392             {
1393                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1394                           prog->filename,
1395                           OPB->_int + 2);
1396                 goto cleanup;
1397             }
1398             ed = prog_getedict(prog, OPA->edict);
1399             OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
1400             OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
1401             OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
1402             break;
1403
1404         case INSTR_ADDRESS:
1405             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1406                 qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
1407                 goto cleanup;
1408             }
1409             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
1410             {
1411                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1412                           prog->filename,
1413                           OPB->_int);
1414                 goto cleanup;
1415             }
1416
1417             ed = prog_getedict(prog, OPA->edict);
1418             OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
1419             break;
1420
1421         case INSTR_STORE_F:
1422         case INSTR_STORE_S:
1423         case INSTR_STORE_ENT:
1424         case INSTR_STORE_FLD:
1425         case INSTR_STORE_FNC:
1426             OPB->_int = OPA->_int;
1427             break;
1428         case INSTR_STORE_V:
1429             OPB->ivector[0] = OPA->ivector[0];
1430             OPB->ivector[1] = OPA->ivector[1];
1431             OPB->ivector[2] = OPA->ivector[2];
1432             break;
1433
1434         case INSTR_STOREP_F:
1435         case INSTR_STOREP_S:
1436         case INSTR_STOREP_ENT:
1437         case INSTR_STOREP_FLD:
1438         case INSTR_STOREP_FNC:
1439             if (OPB->_int < 0 || OPB->_int >= (qcint)vec_size(prog->entitydata)) {
1440                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1441                 goto cleanup;
1442             }
1443             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1444                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1445                           prog->filename,
1446                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1447                           OPB->_int);
1448             ptr = (qcany*)(prog->entitydata + OPB->_int);
1449             ptr->_int = OPA->_int;
1450             break;
1451         case INSTR_STOREP_V:
1452             if (OPB->_int < 0 || OPB->_int + 2 >= (qcint)vec_size(prog->entitydata)) {
1453                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1454                 goto cleanup;
1455             }
1456             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1457                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1458                           prog->filename,
1459                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1460                           OPB->_int);
1461             ptr = (qcany*)(prog->entitydata + OPB->_int);
1462             ptr->ivector[0] = OPA->ivector[0];
1463             ptr->ivector[1] = OPA->ivector[1];
1464             ptr->ivector[2] = OPA->ivector[2];
1465             break;
1466
1467         case INSTR_NOT_F:
1468             OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
1469             break;
1470         case INSTR_NOT_V:
1471             OPC->_float = !OPA->vector[0] &&
1472                           !OPA->vector[1] &&
1473                           !OPA->vector[2];
1474             break;
1475         case INSTR_NOT_S:
1476             OPC->_float = !OPA->string ||
1477                           !*prog_getstring(prog, OPA->string);
1478             break;
1479         case INSTR_NOT_ENT:
1480             OPC->_float = (OPA->edict == 0);
1481             break;
1482         case INSTR_NOT_FNC:
1483             OPC->_float = !OPA->function;
1484             break;
1485
1486         case INSTR_IF:
1487             /* this is consistent with darkplaces' behaviour */
1488             if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1489             {
1490                 st += st->o2.s1 - 1;    /* offset the s++ */
1491                 if (++jumpcount >= maxjumps)
1492                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1493             }
1494             break;
1495         case INSTR_IFNOT:
1496             if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1497             {
1498                 st += st->o2.s1 - 1;    /* offset the s++ */
1499                 if (++jumpcount >= maxjumps)
1500                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1501             }
1502             break;
1503
1504         case INSTR_CALL0:
1505         case INSTR_CALL1:
1506         case INSTR_CALL2:
1507         case INSTR_CALL3:
1508         case INSTR_CALL4:
1509         case INSTR_CALL5:
1510         case INSTR_CALL6:
1511         case INSTR_CALL7:
1512         case INSTR_CALL8:
1513             prog->argc = st->opcode - INSTR_CALL0;
1514             if (!OPA->function)
1515                 qcvmerror(prog, "NULL function in `%s`", prog->filename);
1516
1517             if(!OPA->function || OPA->function >= (qcint)vec_size(prog->functions))
1518             {
1519                 qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
1520                 goto cleanup;
1521             }
1522
1523             newf = &prog->functions[OPA->function];
1524             newf->profile++;
1525
1526             prog->statement = (st - prog->code) + 1;
1527
1528             if (newf->entry < 0)
1529             {
1530                 /* negative statements are built in functions */
1531                 qcint builtinnumber = -newf->entry;
1532                 if (builtinnumber < (qcint)prog->builtins_count && prog->builtins[builtinnumber])
1533                     prog->builtins[builtinnumber](prog);
1534                 else
1535                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
1536                               builtinnumber, prog->filename);
1537             }
1538             else
1539                 st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
1540             if (prog->vmerror)
1541                 goto cleanup;
1542             break;
1543
1544         case INSTR_STATE:
1545             qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
1546             break;
1547
1548         case INSTR_GOTO:
1549             st += st->o1.s1 - 1;    /* offset the s++ */
1550             if (++jumpcount == 10000000)
1551                 qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1552             break;
1553
1554         case INSTR_AND:
1555             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
1556                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1557             break;
1558         case INSTR_OR:
1559             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
1560                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1561             break;
1562
1563         case INSTR_BITAND:
1564             OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
1565             break;
1566         case INSTR_BITOR:
1567             OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
1568             break;
1569     }
1570 }
1571
1572 #undef QCVM_PROFILE
1573 #undef QCVM_TRACE
1574 #endif /* !QCVM_LOOP */