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