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