5 #define _MEM_VEC_FUN_APPEND(Tself, Twhat, mem) \
6 bool GMQCC_WARN Tself##_##mem##_append(Tself *s, Twhat *p, size_t c) \
9 if (s->mem##_count+c >= s->mem##_alloc) { \
10 if (!s->mem##_alloc) { \
11 s->mem##_alloc = c < 16 ? 16 : c; \
13 s->mem##_alloc *= 2; \
14 if (s->mem##_count+c >= s->mem##_alloc) { \
15 s->mem##_alloc = s->mem##_count+c; \
18 reall = (Twhat*)mem_a(sizeof(Twhat) * s->mem##_alloc); \
22 memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
26 memcpy(&s->mem[s->mem##_count], p, c*sizeof(*p)); \
27 s->mem##_count += c; \
31 #define _MEM_VEC_FUN_RESIZE(Tself, Twhat, mem) \
32 bool GMQCC_WARN Tself##_##mem##_resize(Tself *s, size_t c) \
35 if (c > s->mem##_alloc) { \
36 reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
37 if (!reall) { return false; } \
38 memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
45 if (c < (s->mem##_alloc / 2)) { \
46 reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
47 if (!reall) { return false; } \
48 memcpy(reall, s->mem, sizeof(Twhat) * c); \
55 /* darkplaces has (or will have) a 64 bit prog loader
56 * where the 32 bit qc program is autoconverted on load.
57 * Since we may want to support that as well, let's redefine
60 typedef float qcfloat;
61 typedef int32_t qcint;
73 typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1];
74 typedef char qcint_size_is_correct [sizeof(int) == 4 ?1:-1];
86 prog_section statements;
89 prog_section functions;
95 typedef prog_section_both prog_def;
96 typedef prog_section_function prog_function;
97 typedef prog_section_statement prog_statement;
101 VMERR_TEMPSTRING_ALLOC,
106 #define JUMPS_DEFAULT 1000000
108 #define VMXF_DEFAULT 0x0000
109 #define VMXF_TRACE 0x0001
110 #define VMXF_PROFILE 0x0002
114 typedef int (*prog_builtin)(struct qc_program_s *prog);
119 prog_function *function;
122 typedef struct qc_program_s {
125 MEM_VECTOR_MAKE(prog_statement, code);
126 MEM_VECTOR_MAKE(prog_def, defs);
127 MEM_VECTOR_MAKE(prog_def, fields);
128 MEM_VECTOR_MAKE(prog_function, functions);
129 MEM_VECTOR_MAKE(char, strings);
130 MEM_VECTOR_MAKE(qcint, globals);
131 MEM_VECTOR_MAKE(qcint, entitydata);
133 size_t tempstring_start;
134 size_t tempstring_at;
138 MEM_VECTOR_MAKE(size_t, profile);
140 MEM_VECTOR_MAKE(prog_builtin, builtins);
145 bool allowworldwrites;
147 MEM_VECTOR_MAKE(qcint, localstack);
148 MEM_VECTOR_MAKE(qc_exec_stack, stack);
151 int argc; /* current arg count for debugging */
153 MEM_VEC_FUNCTIONS(qc_program, prog_statement, code)
154 MEM_VEC_FUNCTIONS(qc_program, prog_def, defs)
155 MEM_VEC_FUNCTIONS(qc_program, prog_def, fields)
156 MEM_VEC_FUNCTIONS(qc_program, prog_function, functions)
157 MEM_VEC_FUNCTIONS(qc_program, char, strings)
158 _MEM_VEC_FUN_APPEND(qc_program, char, strings)
159 _MEM_VEC_FUN_RESIZE(qc_program, char, strings)
160 MEM_VEC_FUNCTIONS(qc_program, qcint, globals)
161 MEM_VEC_FUNCTIONS(qc_program, qcint, entitydata)
163 MEM_VEC_FUNCTIONS(qc_program, qcint, localstack)
164 _MEM_VEC_FUN_APPEND(qc_program, qcint, localstack)
165 _MEM_VEC_FUN_RESIZE(qc_program, qcint, localstack)
166 MEM_VEC_FUNCTIONS(qc_program, qc_exec_stack, stack)
168 MEM_VEC_FUNCTIONS(qc_program, size_t, profile)
169 _MEM_VEC_FUN_RESIZE(qc_program, size_t, profile)
171 qc_program* prog_load(const char *filename)
177 file = fopen(filename, "rb");
181 if (fread(&header, sizeof(header), 1, file) != 1) {
187 if (header.version != 6) {
188 printf("header says this is a version %i progs, we need version 6\n",
194 prog = (qc_program*)mem_a(sizeof(qc_program));
197 printf("failed to allocate program data\n");
200 memset(prog, 0, sizeof(*prog));
202 prog->entityfields = header.entfield;
204 prog->filename = util_strdup(filename);
207 #define read_data(hdrvar, progvar, type) \
208 if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \
212 prog->progvar##_alloc = header.hdrvar.length; \
213 prog->progvar##_count = header.hdrvar.length; \
214 prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
215 if (!prog->progvar) \
217 if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
218 != header.hdrvar.length) { \
222 #define read_data1(x, y) read_data(x, x, y)
224 read_data (statements, code, prog_statement);
225 read_data1(defs, prog_def);
226 read_data1(fields, prog_def);
227 read_data1(functions, prog_function);
228 read_data1(strings, char);
229 read_data1(globals, qcint);
233 /* profile counters */
234 if (!qc_program_profile_resize(prog, prog->code_count))
237 /* Add tempstring area */
238 prog->tempstring_start = prog->strings_count;
239 prog->tempstring_at = prog->strings_count;
240 if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
246 if (prog->filename) mem_d(prog->filename);
247 if (prog->code) mem_d(prog->code);
248 if (prog->defs) mem_d(prog->defs);
249 if (prog->fields) mem_d(prog->fields);
250 if (prog->functions) mem_d(prog->functions);
251 if (prog->strings) mem_d(prog->strings);
252 if (prog->globals) mem_d(prog->globals);
253 if (prog->entitydata) mem_d(prog->entitydata);
258 void prog_delete(qc_program *prog)
260 if (prog->filename) mem_d(prog->filename);
261 MEM_VECTOR_CLEAR(prog, code);
262 MEM_VECTOR_CLEAR(prog, defs);
263 MEM_VECTOR_CLEAR(prog, fields);
264 MEM_VECTOR_CLEAR(prog, functions);
265 MEM_VECTOR_CLEAR(prog, strings);
266 MEM_VECTOR_CLEAR(prog, globals);
267 MEM_VECTOR_CLEAR(prog, entitydata);
268 MEM_VECTOR_CLEAR(prog, localstack);
269 MEM_VECTOR_CLEAR(prog, stack);
270 MEM_VECTOR_CLEAR(prog, profile);
274 /***********************************************************************
278 char* prog_getstring(qc_program *prog, qcint str)
280 if (str < 0 || str >= prog->strings_count)
281 return "<<<invalid string>>>";
282 return prog->strings + str;
285 prog_def* prog_entfield(qc_program *prog, qcint off)
288 for (i = 0; i < prog->fields_count; ++i) {
289 if (prog->fields[i].offset == off)
290 return (prog->fields + i);
295 prog_def* prog_getdef(qc_program *prog, qcint off)
298 for (i = 0; i < prog->defs_count; ++i) {
299 if (prog->defs[i].offset == off)
300 return (prog->defs + i);
305 qcany* prog_getedict(qc_program *prog, qcint e)
307 return (qcany*)(prog->entitydata + (prog->entityfields + e));
310 qcint prog_tempstring(qc_program *prog, const char *_str)
312 /* we don't access it, but the macro-generated functions don't use
315 char *str = (char*)_str;
317 size_t len = strlen(str);
318 size_t at = prog->tempstring_at;
320 /* when we reach the end we start over */
321 if (at + len >= prog->strings_count)
322 at = prog->tempstring_start;
324 /* when it doesn't fit, reallocate */
325 if (at + len >= prog->strings_count)
327 prog->strings_count = at;
328 if (!qc_program_strings_append(prog, str, len+1)) {
329 prog->vmerror = VMERR_TEMPSTRING_ALLOC;
335 /* when it fits, just copy */
336 memcpy(prog->strings + at, str, len+1);
337 prog->tempstring_at += len+1;
341 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
343 static char spaces[16+1] = " ";
351 def = prog_getdef(prog, glob);
352 value = (qcany*)(&prog->globals[glob]);
355 len = printf("[%s] ", prog_getstring(prog, def->name));
359 len = printf("[#%u] ", glob);
367 len += printf("%i,", value->_int);
370 len += printf("'%g %g %g',", value->vector[0],
375 len += printf("\"%s\",", prog_getstring(prog, value->string));
379 len += printf("%g,", value->_float);
385 spaces[16-len] = ' ';
389 static void prog_print_statement(qc_program *prog, prog_statement *st)
391 if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
392 printf("<illegal instruction %d>\n", st->opcode);
395 printf("%-12s", asm_instr[st->opcode].m);
396 if (st->opcode >= INSTR_IF &&
397 st->opcode <= INSTR_IFNOT)
399 trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
400 printf("%d\n", st->o2.s1);
402 else if (st->opcode >= INSTR_CALL0 &&
403 st->opcode <= INSTR_CALL8)
407 else if (st->opcode == INSTR_GOTO)
409 printf("%i\n", st->o1.s1);
413 int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
417 t[1] = t[2] = TYPE_VECTOR;
420 t[0] = t[2] = TYPE_VECTOR;
423 t[0] = t[1] = TYPE_VECTOR;
429 t[0] = t[1] = t[2] = TYPE_VECTOR;
433 t[0] = t[1] = TYPE_STRING;
436 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
439 t[0] = t[1] = TYPE_STRING; t[2] = -1;
442 if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
443 if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
444 if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
449 static qcint prog_enterfunction(qc_program *prog, prog_function *func)
452 prog_function *cur = NULL;
454 if (prog->stack_count)
455 cur = prog->stack[prog->stack_count-1].function;
458 st.localsp = prog->localstack_count;
459 st.stmt = prog->statement;
464 qcint *globals = prog->globals + cur->firstlocal;
465 if (!qc_program_localstack_append(prog, globals, cur->locals))
467 printf("out of memory\n");
472 if (!qc_program_stack_add(prog, st)) {
473 printf("out of memory\n");
480 static qcint prog_leavefunction(qc_program *prog)
482 qc_exec_stack st = prog->stack[prog->stack_count-1];
483 if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
484 printf("out of memory\n");
488 if (st.localsp != prog->localstack_count) {
489 if (!qc_program_localstack_resize(prog, st.localsp)) {
490 printf("out of memory\n");
498 bool prog_exec(qc_program *prog, prog_function *func, size_t flags, long maxjumps)
503 st = prog->code + prog_enterfunction(prog, func);
510 #define QCVM_PROFILE 0
512 # include "qcvm_execprogram.h"
517 #define QCVM_PROFILE 0
519 # include "qcvm_execprogram.h"
524 #define QCVM_PROFILE 1
526 # include "qcvm_execprogram.h"
529 case (VMXF_TRACE|VMXF_PROFILE):
531 #define QCVM_PROFILE 1
533 # include "qcvm_execprogram.h"
539 prog->localstack_count = 0;
540 prog->stack_count = 0;
544 /***********************************************************************
545 * main for when building the standalone executor
548 #if defined(QCVM_EXECUTOR)
549 int main(int argc, char **argv)
556 printf("usage: %s file\n", argv[0]);
560 prog = prog_load(argv[1]);
562 printf("failed to load program '%s'\n", argv[1]);
566 for (i = 1; i < prog->functions_count; ++i) {
567 const char *name = prog_getstring(prog, prog->functions[i].name);
568 printf("Found function: %s\n", name);
569 if (!strcmp(name, "main"))
574 prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, JUMPS_DEFAULT);
577 printf("No main function found\n");