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