]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
Moving typedefs and defines from exec.c into an exec.h header
[xonotic/gmqcc.git] / exec.c
1 #include "gmqcc.h"
2
3 #include "exec.h"
4
5 #define QCVM_EXECUTOR
6
7 qc_program* prog_load(const char *filename)
8 {
9     qc_program *prog;
10     prog_header header;
11     FILE *file;
12
13     file = fopen(filename, "rb");
14     if (!file)
15         return NULL;
16
17     if (fread(&header, sizeof(header), 1, file) != 1) {
18         perror("read");
19         fclose(file);
20         return NULL;
21     }
22
23     if (header.version != 6) {
24         printf("header says this is a version %i progs, we need version 6\n",
25                header.version);
26         fclose(file);
27         return NULL;
28     }
29
30     prog = (qc_program*)mem_a(sizeof(qc_program));
31     if (!prog) {
32         fclose(file);
33         printf("failed to allocate program data\n");
34         return NULL;
35     }
36     memset(prog, 0, sizeof(*prog));
37
38     prog->entityfields = header.entfield;
39
40     prog->filename = util_strdup(filename);
41     if (!prog->filename)
42         goto error;
43 #define read_data(hdrvar, progvar, type)                                         \
44     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
45         perror("fseek");                                                         \
46         goto error;                                                              \
47     }                                                                            \
48     prog->progvar##_alloc = header.hdrvar.length;                                \
49     prog->progvar##_count = header.hdrvar.length;                                \
50     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
51     if (!prog->progvar)                                                          \
52         goto error;                                                              \
53     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
54         != header.hdrvar.length) {                                               \
55         perror("read");                                                          \
56         goto error;                                                              \
57     }
58 #define read_data1(x, y) read_data(x, x, y)
59
60     read_data (statements, code, prog_statement);
61     read_data1(defs,             prog_def);
62     read_data1(fields,           prog_def);
63     read_data1(functions,        prog_function);
64     read_data1(strings,          char);
65     read_data1(globals,          qcint);
66
67     fclose(file);
68
69     /* profile counters */
70     if (!qc_program_profile_resize(prog, prog->code_count))
71         goto error;
72
73     /* Add tempstring area */
74     prog->tempstring_start = prog->strings_count;
75     prog->tempstring_at    = prog->strings_count;
76     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
77         goto error;
78
79     return prog;
80
81 error:
82     if (prog->filename)   mem_d(prog->filename);
83     if (prog->code)       mem_d(prog->code);
84     if (prog->defs)       mem_d(prog->defs);
85     if (prog->fields)     mem_d(prog->fields);
86     if (prog->functions)  mem_d(prog->functions);
87     if (prog->strings)    mem_d(prog->strings);
88     if (prog->globals)    mem_d(prog->globals);
89     if (prog->entitydata) mem_d(prog->entitydata);
90     mem_d(prog);
91     return NULL;
92 }
93
94 void prog_delete(qc_program *prog)
95 {
96     if (prog->filename) mem_d(prog->filename);
97     MEM_VECTOR_CLEAR(prog, code);
98     MEM_VECTOR_CLEAR(prog, defs);
99     MEM_VECTOR_CLEAR(prog, fields);
100     MEM_VECTOR_CLEAR(prog, functions);
101     MEM_VECTOR_CLEAR(prog, strings);
102     MEM_VECTOR_CLEAR(prog, globals);
103     MEM_VECTOR_CLEAR(prog, entitydata);
104     MEM_VECTOR_CLEAR(prog, localstack);
105     MEM_VECTOR_CLEAR(prog, stack);
106     MEM_VECTOR_CLEAR(prog, profile);
107     mem_d(prog);
108 }
109
110 /***********************************************************************
111  * VM code
112  */
113
114 char* prog_getstring(qc_program *prog, qcint str)
115 {
116     if (str < 0 || str >= prog->strings_count)
117         return "<<<invalid string>>>";
118     return prog->strings + str;
119 }
120
121 prog_def* prog_entfield(qc_program *prog, qcint off)
122 {
123     size_t i;
124     for (i = 0; i < prog->fields_count; ++i) {
125         if (prog->fields[i].offset == off)
126             return (prog->fields + i);
127     }
128     return NULL;
129 }
130
131 prog_def* prog_getdef(qc_program *prog, qcint off)
132 {
133     size_t i;
134     for (i = 0; i < prog->defs_count; ++i) {
135         if (prog->defs[i].offset == off)
136             return (prog->defs + i);
137     }
138     return NULL;
139 }
140
141 qcany* prog_getedict(qc_program *prog, qcint e)
142 {
143     return (qcany*)(prog->entitydata + (prog->entityfields + e));
144 }
145
146 qcint prog_tempstring(qc_program *prog, const char *_str)
147 {
148     /* we don't access it, but the macro-generated functions don't use
149      * const
150      */
151     char *str = (char*)_str;
152
153     size_t len = strlen(str);
154     size_t at = prog->tempstring_at;
155
156     /* when we reach the end we start over */
157     if (at + len >= prog->strings_count)
158         at = prog->tempstring_start;
159
160     /* when it doesn't fit, reallocate */
161     if (at + len >= prog->strings_count)
162     {
163         prog->strings_count = at;
164         if (!qc_program_strings_append(prog, str, len+1)) {
165             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
166             return 0;
167         }
168         return at;
169     }
170
171     /* when it fits, just copy */
172     memcpy(prog->strings + at, str, len+1);
173     prog->tempstring_at += len+1;
174     return at;
175 }
176
177 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
178 {
179     static char spaces[16+1] = "            ";
180     prog_def *def;
181     qcany    *value;
182     int       len;
183
184     if (!glob)
185         return;
186
187     def = prog_getdef(prog, glob);
188     value = (qcany*)(&prog->globals[glob]);
189
190     if (def) {
191         len = printf("[%s] ", prog_getstring(prog, def->name));
192         vtype = def->type;
193     }
194     else
195         len = printf("[#%u] ", glob);
196
197     switch (vtype) {
198         case TYPE_VOID:
199         case TYPE_ENTITY:
200         case TYPE_FIELD:
201         case TYPE_FUNCTION:
202         case TYPE_POINTER:
203             len += printf("%i,", value->_int);
204             break;
205         case TYPE_VECTOR:
206             len += printf("'%g %g %g',", value->vector[0],
207                                          value->vector[1],
208                                          value->vector[2]);
209             break;
210         case TYPE_STRING:
211             len += printf("\"%s\",", prog_getstring(prog, value->string));
212             break;
213         case TYPE_FLOAT:
214         default:
215             len += printf("%g,", value->_float);
216             break;
217     }
218     if (len < 16) {
219         spaces[16-len] = 0;
220         printf(spaces);
221         spaces[16-len] = ' ';
222     }
223 }
224
225 static void prog_print_statement(qc_program *prog, prog_statement *st)
226 {
227     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
228         printf("<illegal instruction %d>\n", st->opcode);
229         return;
230     }
231     printf("%-12s", asm_instr[st->opcode].m);
232     if (st->opcode >= INSTR_IF &&
233         st->opcode <= INSTR_IFNOT)
234     {
235         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
236         printf("%d\n", st->o2.s1);
237     }
238     else if (st->opcode >= INSTR_CALL0 &&
239              st->opcode <= INSTR_CALL8)
240     {
241         printf("\n");
242     }
243     else if (st->opcode == INSTR_GOTO)
244     {
245         printf("%i\n", st->o1.s1);
246     }
247     else
248     {
249         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
250         switch (st->opcode)
251         {
252             case INSTR_MUL_FV:
253                 t[1] = t[2] = TYPE_VECTOR;
254                 break;
255             case INSTR_MUL_VF:
256                 t[0] = t[2] = TYPE_VECTOR;
257                 break;
258             case INSTR_MUL_V:
259                 t[0] = t[1] = TYPE_VECTOR;
260                 break;
261             case INSTR_ADD_V:
262             case INSTR_SUB_V:
263             case INSTR_EQ_V:
264             case INSTR_NE_V:
265                 t[0] = t[1] = t[2] = TYPE_VECTOR;
266                 break;
267             case INSTR_EQ_S:
268             case INSTR_NE_S:
269                 t[0] = t[1] = TYPE_STRING;
270                 break;
271             case INSTR_STORE_V:
272                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
273                 break;
274             case INSTR_STORE_S:
275                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
276                 break;
277         }
278         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
279         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
280         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
281         printf("\n");
282     }
283 }
284
285 static qcint prog_enterfunction(qc_program *prog, prog_function *func)
286 {
287     qc_exec_stack st;
288     prog_function *cur = NULL;
289
290     if (prog->stack_count)
291         cur = prog->stack[prog->stack_count-1].function;
292
293     /* back up locals */
294     st.localsp  = prog->localstack_count;
295     st.stmt     = prog->statement;
296     st.function = func;
297
298     if (cur)
299     {
300         qcint *globals = prog->globals + cur->firstlocal;
301         if (!qc_program_localstack_append(prog, globals, cur->locals))
302         {
303             printf("out of memory\n");
304             exit(1);
305         }
306     }
307
308     if (!qc_program_stack_add(prog, st)) {
309         printf("out of memory\n");
310         exit(1);
311     }
312
313     return func->entry;
314 }
315
316 static qcint prog_leavefunction(qc_program *prog)
317 {
318     qc_exec_stack st = prog->stack[prog->stack_count-1];
319     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
320         printf("out of memory\n");
321         exit(1);
322     }
323
324     if (st.localsp != prog->localstack_count) {
325         if (!qc_program_localstack_resize(prog, st.localsp)) {
326             printf("out of memory\n");
327             exit(1);
328         }
329     }
330
331     return st.stmt;
332 }
333
334 bool prog_exec(qc_program *prog, prog_function *func, size_t flags, long maxjumps)
335 {
336     long jumpcount = 0;
337     prog_statement *st;
338
339     st = prog->code + prog_enterfunction(prog, func);
340     --st;
341     switch (flags)
342     {
343         default:
344         case 0:
345         {
346 #define QCVM_PROFILE 0
347 #define QCVM_TRACE   0
348 #           include "qcvm_execprogram.h"
349             break;
350         }
351         case (VMXF_TRACE):
352         {
353 #define QCVM_PROFILE 0
354 #define QCVM_TRACE   1
355 #           include "qcvm_execprogram.h"
356             break;
357         }
358         case (VMXF_PROFILE):
359         {
360 #define QCVM_PROFILE 1
361 #define QCVM_TRACE   0
362 #           include "qcvm_execprogram.h"
363             break;
364         }
365         case (VMXF_TRACE|VMXF_PROFILE):
366         {
367 #define QCVM_PROFILE 1
368 #define QCVM_TRACE   1
369 #           include "qcvm_execprogram.h"
370             break;
371         }
372     };
373
374 cleanup:
375     prog->localstack_count = 0;
376     prog->stack_count = 0;
377     return true;
378 }
379
380 /***********************************************************************
381  * main for when building the standalone executor
382  */
383
384 #if defined(QCVM_EXECUTOR)
385 int main(int argc, char **argv)
386 {
387     size_t      i;
388     qcint       fnmain = -1;
389     qc_program *prog;
390
391     if (argc != 2) {
392         printf("usage: %s file\n", argv[0]);
393         exit(1);
394     }
395
396     prog = prog_load(argv[1]);
397     if (!prog) {
398         printf("failed to load program '%s'\n", argv[1]);
399         exit(1);
400     }
401
402     for (i = 1; i < prog->functions_count; ++i) {
403         const char *name = prog_getstring(prog, prog->functions[i].name);
404         printf("Found function: %s\n", name);
405         if (!strcmp(name, "main"))
406             fnmain = (qcint)i;
407     }
408     if (fnmain > 0)
409     {
410         prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, JUMPS_DEFAULT);
411     }
412     else
413         printf("No main function found\n");
414
415     prog_delete(prog);
416     return 0;
417 }
418 #endif