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