]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_exec.c
Merge branch 'master' into blub/dp1
[divverent/darkplaces.git] / prvm_exec.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "progsvm.h"
23
24 char *prvm_opnames[] =
25 {
26 "^5DONE",
27
28 "MUL_F",
29 "MUL_V",
30 "MUL_FV",
31 "MUL_VF",
32
33 "DIV",
34
35 "ADD_F",
36 "ADD_V",
37
38 "SUB_F",
39 "SUB_V",
40
41 "^2EQ_F",
42 "^2EQ_V",
43 "^2EQ_S",
44 "^2EQ_E",
45 "^2EQ_FNC",
46
47 "^2NE_F",
48 "^2NE_V",
49 "^2NE_S",
50 "^2NE_E",
51 "^2NE_FNC",
52
53 "^2LE",
54 "^2GE",
55 "^2LT",
56 "^2GT",
57
58 "^6FIELD_F",
59 "^6FIELD_V",
60 "^6FIELD_S",
61 "^6FIELD_ENT",
62 "^6FIELD_FLD",
63 "^6FIELD_FNC",
64
65 "^1ADDRESS",
66
67 "STORE_F",
68 "STORE_V",
69 "STORE_S",
70 "STORE_ENT",
71 "STORE_FLD",
72 "STORE_FNC",
73
74 "^1STOREP_F",
75 "^1STOREP_V",
76 "^1STOREP_S",
77 "^1STOREP_ENT",
78 "^1STOREP_FLD",
79 "^1STOREP_FNC",
80
81 "^5RETURN",
82
83 "^2NOT_F",
84 "^2NOT_V",
85 "^2NOT_S",
86 "^2NOT_ENT",
87 "^2NOT_FNC",
88
89 "^5IF",
90 "^5IFNOT",
91
92 "^3CALL0",
93 "^3CALL1",
94 "^3CALL2",
95 "^3CALL3",
96 "^3CALL4",
97 "^3CALL5",
98 "^3CALL6",
99 "^3CALL7",
100 "^3CALL8",
101
102 "^1STATE",
103
104 "^5GOTO",
105
106 "^2AND",
107 "^2OR",
108
109 "BITAND",
110 "BITOR",
111 "OP_MULSTORE_F",
112 "OP_MULSTORE_V",
113 "OP_MULSTOREP_F",
114 "OP_MULSTOREP_V",
115
116 "OP_DIVSTORE_F",        //70
117 "OP_DIVSTOREP_F",
118
119 "OP_ADDSTORE_F",
120 "OP_ADDSTORE_V",
121 "OP_ADDSTOREP_F",
122 "OP_ADDSTOREP_V",
123
124 "OP_SUBSTORE_F",
125 "OP_SUBSTORE_V",
126 "OP_SUBSTOREP_F",
127 "OP_SUBSTOREP_V",
128
129 "OP_FETCH_GBL_F",       //80
130 "OP_FETCH_GBL_V",
131 "OP_FETCH_GBL_S",
132 "OP_FETCH_GBL_E",
133 "OP_FETCH_GBL_FNC",
134
135 "OP_CSTATE",
136 "OP_CWSTATE",
137
138 "OP_THINKTIME",
139
140 "OP_BITSET",
141 "OP_BITSETP",
142 "OP_BITCLR",            //90
143 "OP_BITCLRP",
144
145 "OP_RAND0",
146 "OP_RAND1",
147 "OP_RAND2",
148 "OP_RANDV0",
149 "OP_RANDV1",
150 "OP_RANDV2",
151
152 "OP_SWITCH_F",
153 "OP_SWITCH_V",
154 "OP_SWITCH_S",  //100
155 "OP_SWITCH_E",
156 "OP_SWITCH_FNC",
157
158 "OP_CASE",
159 "OP_CASERANGE",
160
161
162
163
164
165         //the rest are added
166         //mostly they are various different ways of adding two vars with conversions.
167
168 "OP_CALL1H",
169 "OP_CALL2H",
170 "OP_CALL3H",
171 "OP_CALL4H",
172 "OP_CALL5H",
173 "OP_CALL6H",            //110
174 "OP_CALL7H",
175 "OP_CALL8H",
176
177
178 "OP_STORE_I",
179 "OP_STORE_IF",
180 "OP_STORE_FI",
181
182 "OP_ADD_I",
183 "OP_ADD_FI",
184 "OP_ADD_IF",            //110
185
186 "OP_SUB_I",
187 "OP_SUB_FI",
188 "OP_SUB_IF",
189
190 "OP_CONV_ITOF",
191 "OP_CONV_FTOI",
192 "OP_CP_ITOF",
193 "OP_CP_FTOI",
194 "OP_LOAD_I",
195 "OP_STOREP_I",
196 "OP_STOREP_IF", //120
197 "OP_STOREP_FI",
198
199 "OP_BITAND_I",
200 "OP_BITOR_I",
201
202 "OP_MUL_I",
203 "OP_DIV_I",
204 "OP_EQ_I",
205 "OP_NE_I",
206
207 "OP_IFNOTS",
208 "OP_IFS",
209
210 "OP_NOT_I",             //130
211
212 "OP_DIV_VF",
213
214 "OP_XOR_I",
215 "OP_RSHIFT_I",
216 "OP_LSHIFT_I",
217
218 "OP_GLOBALADDRESS",
219 "OP_POINTER_ADD",       //32 bit pointers
220
221 "OP_LOADA_F",
222 "OP_LOADA_V",
223 "OP_LOADA_S",
224 "OP_LOADA_ENT", //140
225 "OP_LOADA_FLD",
226 "OP_LOADA_FNC",
227 "OP_LOADA_I",
228
229 "OP_STORE_P",
230 "OP_LOAD_P",
231
232 "OP_LOADP_F",
233 "OP_LOADP_V",
234 "OP_LOADP_S",
235 "OP_LOADP_ENT",
236 "OP_LOADP_FLD", //150
237 "OP_LOADP_FNC",
238 "OP_LOADP_I",
239
240 "OP_LE_I",
241 "OP_GE_I",
242 "OP_LT_I",
243 "OP_GT_I",
244
245 "OP_LE_IF",
246 "OP_GE_IF",
247 "OP_LT_IF",
248 "OP_GT_IF",             //160
249
250 "OP_LE_FI",
251 "OP_GE_FI",
252 "OP_LT_FI",
253 "OP_GT_FI",
254
255 "OP_EQ_IF",
256 "OP_EQ_FI",
257
258         //-------------------------------------
259         //string manipulation.
260 "OP_ADD_SF",    //(char*)c = (char*)a + (float)b
261 "OP_SUB_S",     //(float)c = (char*)a - (char*)b
262 "OP_STOREP_C",//(float)c = *(char*)b = (float)a
263 "OP_LOADP_C",   //(float)c = *(char*)                                   //170
264         //-------------------------------------
265
266
267 "OP_MUL_IF",
268 "OP_MUL_FI",
269 "OP_MUL_VI",
270 "OP_MUL_IV",
271 "OP_DIV_IF",
272 "OP_DIV_FI",
273 "OP_BITAND_IF",
274 "OP_BITOR_IF",
275 "OP_BITAND_FI",
276 "OP_BITOR_FI",          //180
277 "OP_AND_I",
278 "OP_OR_I",
279 "OP_AND_IF",
280 "OP_OR_IF",
281 "OP_AND_FI",
282 "OP_OR_FI",
283 "OP_NE_IF",
284 "OP_NE_FI",
285
286 //erm... FTEQCC doesn't make use of these... These are for DP.
287 "OP_GSTOREP_I",
288 "OP_GSTOREP_F",         //190
289 "OP_GSTOREP_ENT",
290 "OP_GSTOREP_FLD",               // integers
291 "OP_GSTOREP_S",
292 "OP_GSTOREP_FNC",               // pointers
293 "OP_GSTOREP_V",
294 "OP_GADDRESS",
295 "OP_GLOAD_I",
296 "OP_GLOAD_F",
297 "OP_GLOAD_FLD",
298 "OP_GLOAD_ENT",         //200
299 "OP_GLOAD_S",
300 "OP_GLOAD_FNC",
301 "OP_BOUNDCHECK",
302
303 //back to ones that we do use.
304 "OP_STOREP_P",
305 "OP_PUSH",      //push 4octets onto the local-stack (which is ALWAYS poped on function return). Returns a pointer.
306 "OP_POP",               //pop those ones that were pushed (don't over do it). Needs assembler.
307
308 "^5OP_IF_I",
309 "^5OP_IFNOT_I",
310
311 "OP_NUMOPS"
312 };
313
314 char *PRVM_GlobalString (int ofs);
315 char *PRVM_GlobalStringNoContents (int ofs);
316 extern ddef_t *PRVM_ED_FieldAtOfs(int ofs);
317
318
319 //=============================================================================
320
321 /*
322 =================
323 PRVM_PrintStatement
324 =================
325 */
326 extern cvar_t prvm_statementprofiling;
327 void PRVM_PrintStatement (dstatement_t *s)
328 {
329         size_t i;
330         int opnum = (int)(s - prog->statements);
331
332         Con_Printf("s%i: ", opnum);
333         if( prog->statement_linenums )
334                 Con_Printf( "%s:%i: ", PRVM_GetString( prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
335
336         if (prvm_statementprofiling.integer)
337                 Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
338
339         if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
340         {
341                 Con_Printf("%s ",  prvm_opnames[s->op]);
342                 i = strlen(prvm_opnames[s->op]);
343                 // don't count a preceding color tag when padding the name
344                 if (prvm_opnames[s->op][0] == STRING_COLOR_TAG)
345                         i -= 2;
346                 for ( ; i<10 ; i++)
347                         Con_Print(" ");
348         }
349         if (s->op == OP_IF || s->op == OP_IFNOT)
350                 Con_Printf("%s, s%i",PRVM_GlobalString((unsigned short) s->a),(signed short)s->b + opnum);
351         else if (s->op == OP_GOTO)
352                 Con_Printf("s%i",(signed short)s->a + opnum);
353         else if ( (unsigned)(s->op - OP_STORE_F) < 6)
354         {
355                 Con_Print(PRVM_GlobalString((unsigned short) s->a));
356                 Con_Print(", ");
357                 Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b));
358         }
359         else if (s->op == OP_ADDRESS || (unsigned)(s->op - OP_LOAD_F) < 6)
360         {
361                 if (s->a)
362                         Con_Print(PRVM_GlobalString((unsigned short) s->a));
363                 if (s->b)
364                 {
365                         Con_Print(", ");
366                         Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b));
367                 }
368                 if (s->c)
369                 {
370                         Con_Print(", ");
371                         Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c));
372                 }
373         }
374         else
375         {
376                 if (s->a)
377                         Con_Print(PRVM_GlobalString((unsigned short) s->a));
378                 if (s->b)
379                 {
380                         Con_Print(", ");
381                         Con_Print(PRVM_GlobalString((unsigned short) s->b));
382                 }
383                 if (s->c)
384                 {
385                         if(!s->b) // added for debugging --blub
386                                 Con_Print(", (nil)");
387                         Con_Print(", ");
388                         Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c));
389                 }
390         }
391         Con_Print("\n");
392 }
393
394 void PRVM_PrintFunctionStatements (const char *name)
395 {
396         int i, firststatement, endstatement;
397         mfunction_t *func;
398         func = PRVM_ED_FindFunction (name);
399         if (!func)
400         {
401                 Con_Printf("%s progs: no function named %s\n", PRVM_NAME, name);
402                 return;
403         }
404         firststatement = func->first_statement;
405         if (firststatement < 0)
406         {
407                 Con_Printf("%s progs: function %s is builtin #%i\n", PRVM_NAME, name, -firststatement);
408                 return;
409         }
410
411         // find the end statement
412         endstatement = prog->progs->numstatements;
413         for (i = 0;i < prog->progs->numfunctions;i++)
414                 if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
415                         endstatement = prog->functions[i].first_statement;
416
417         // now print the range of statements
418         Con_Printf("%s progs: disassembly of function %s (statements %i-%i, locals %i-%i):\n", PRVM_NAME, name, firststatement, endstatement, func->parm_start, func->parm_start + func->locals - 1);
419         for (i = firststatement;i < endstatement;i++)
420         {
421                 PRVM_PrintStatement(prog->statements + i);
422                 prog->statement_profile[i] = 0;
423         }
424 }
425
426 /*
427 ============
428 PRVM_PrintFunction_f
429
430 ============
431 */
432 void PRVM_PrintFunction_f (void)
433 {
434         if (Cmd_Argc() != 3)
435         {
436                 Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
437                 return;
438         }
439
440         PRVM_Begin;
441         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
442                 return;
443
444         PRVM_PrintFunctionStatements(Cmd_Argv(2));
445
446         PRVM_End;
447 }
448
449 /*
450 ============
451 PRVM_StackTrace
452 ============
453 */
454 void PRVM_StackTrace (void)
455 {
456         mfunction_t     *f;
457         int                     i;
458
459         prog->stack[prog->depth].s = prog->xstatement;
460         prog->stack[prog->depth].f = prog->xfunction;
461         for (i = prog->depth;i > 0;i--)
462         {
463                 f = prog->stack[i].f;
464
465                 if (!f)
466                         Con_Print("<NULL FUNCTION>\n");
467                 else
468                         Con_Printf("%12s : %s : statement %i\n", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement);
469         }
470 }
471
472 void PRVM_ShortStackTrace(char *buf, size_t bufsize)
473 {
474         mfunction_t     *f;
475         int                     i;
476
477         if(prog)
478         {
479                 dpsnprintf(buf, bufsize, "(%s) ", prog->name);
480         }
481         else
482         {
483                 strlcpy(buf, "<NO PROG>", bufsize);
484                 return;
485         }
486
487         prog->stack[prog->depth].s = prog->xstatement;
488         prog->stack[prog->depth].f = prog->xfunction;
489         for (i = prog->depth;i > 0;i--)
490         {
491                 f = prog->stack[i].f;
492
493                 if(strlcat(buf,
494                         f
495                                 ? va("%s:%s(%i) ", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement)
496                                 : "<NULL> ",
497                         bufsize
498                 ) >= bufsize)
499                         break;
500         }
501 }
502
503
504 void PRVM_CallProfile (void)
505 {
506         mfunction_t *f, *best;
507         int i;
508         double max;
509         double sum;
510
511         Con_Printf( "%s Call Profile:\n", PRVM_NAME );
512
513         sum = 0;
514         do
515         {
516                 max = 0;
517                 best = NULL;
518                 for (i=0 ; i<prog->progs->numfunctions ; i++)
519                 {
520                         f = &prog->functions[i];
521                         if (max < f->totaltime)
522                         {
523                                 max = f->totaltime;
524                                 best = f;
525                         }
526                 }
527                 if (best)
528                 {
529                         sum += best->totaltime;
530                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(best->s_name));
531                         best->totaltime = 0;
532                 }
533         } while (best);
534
535         Con_Printf("Total time since last profile reset: %9.4f\n", Sys_DoubleTime() - prog->starttime);
536         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
537
538         prog->starttime = Sys_DoubleTime();
539 }
540
541 void PRVM_Profile (int maxfunctions, int mininstructions, int sortby)
542 {
543         mfunction_t *f, *best;
544         int i, num;
545         double max;
546
547         Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", PRVM_NAME );
548         //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
549
550         num = 0;
551         do
552         {
553                 max = 0;
554                 best = NULL;
555                 for (i=0 ; i<prog->progs->numfunctions ; i++)
556                 {
557                         f = &prog->functions[i];
558                         if(sortby)
559                         {
560                                 if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
561                                 {
562                                         max = f->profile_total + f->builtinsprofile_total + f->callcount;
563                                         best = f;
564                                 }
565                         }
566                         else
567                         {
568                                 if (max < f->profile + f->builtinsprofile + f->callcount)
569                                 {
570                                         max = f->profile + f->builtinsprofile + f->callcount;
571                                         best = f;
572                                 }
573                         }
574                 }
575                 if (best)
576                 {
577                         if (num < maxfunctions && max >= mininstructions)
578                         {
579                                 if (best->first_statement < 0)
580                                         Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(best->s_name));
581                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
582                                 else
583                                         Con_Printf("%11.0f %11.0f %11.0f %11.0f %11.0f %6.2f%% %s\n", best->callcount, best->profile, best->builtinsprofile, best->profile_total, best->builtinsprofile_total, (best->profile + best->builtinsprofile) * 100.0 / (best->profile_total + best->builtinsprofile_total), PRVM_GetString(best->s_name));
584                         }
585                         num++;
586                         best->profile = 0;
587                         best->builtinsprofile = 0;
588                         best->profile_total = 0;
589                         best->builtinsprofile_total = 0;
590                         best->callcount = 0;
591                 }
592         } while (best);
593 }
594
595 /*
596 ============
597 PRVM_CallProfile_f
598
599 ============
600 */
601 void PRVM_CallProfile_f (void)
602 {
603         if (Cmd_Argc() != 2)
604         {
605                 Con_Print("prvm_callprofile <program name>\n");
606                 return;
607         }
608
609         PRVM_Begin;
610         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
611                 return;
612
613         PRVM_CallProfile();
614
615         PRVM_End;
616 }
617
618 /*
619 ============
620 PRVM_Profile_f
621
622 ============
623 */
624 void PRVM_Profile_f (void)
625 {
626         int howmany;
627
628         howmany = 1<<30;
629         if (Cmd_Argc() == 3)
630                 howmany = atoi(Cmd_Argv(2));
631         else if (Cmd_Argc() != 2)
632         {
633                 Con_Print("prvm_profile <program name>\n");
634                 return;
635         }
636
637         PRVM_Begin;
638         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
639                 return;
640
641         PRVM_Profile(howmany, 1, 0);
642
643         PRVM_End;
644 }
645
646 void PRVM_ChildProfile_f (void)
647 {
648         int howmany;
649
650         howmany = 1<<30;
651         if (Cmd_Argc() == 3)
652                 howmany = atoi(Cmd_Argv(2));
653         else if (Cmd_Argc() != 2)
654         {
655                 Con_Print("prvm_childprofile <program name>\n");
656                 return;
657         }
658
659         PRVM_Begin;
660         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
661                 return;
662
663         PRVM_Profile(howmany, 1, 1);
664
665         PRVM_End;
666 }
667
668 void PRVM_CrashAll(void)
669 {
670         int i;
671         prvm_prog_t *oldprog = prog;
672
673         for(i = 0; i < PRVM_MAXPROGS; i++)
674         {
675                 if(!PRVM_ProgLoaded(i))
676                         continue;
677                 PRVM_SetProg(i);
678                 PRVM_Crash();
679         }
680
681         prog = oldprog;
682 }
683
684 void PRVM_PrintState(void)
685 {
686         int i;
687         if(prog->statestring)
688         {
689                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
690         }
691         if (prog->xfunction)
692         {
693                 for (i = -7; i <= 0;i++)
694                         if (prog->xstatement + i >= prog->xfunction->first_statement)
695                                 PRVM_PrintStatement (prog->statements + prog->xstatement + i);
696         }
697         else
698                 Con_Print("null function executing??\n");
699         PRVM_StackTrace ();
700 }
701
702 extern sizebuf_t vm_tempstringsbuf;
703 extern cvar_t prvm_errordump;
704 void Host_Savegame_to (const char *name);
705 void PRVM_Crash(void)
706 {
707         if (prog == NULL)
708                 return;
709
710         prog->funcoffsets.SV_Shutdown = 0; // don't call SV_Shutdown on crash
711
712         if( prog->depth > 0 )
713         {
714                 Con_Printf("QuakeC crash report for %s:\n", PRVM_NAME);
715                 PRVM_PrintState();
716         }
717
718         if(prvm_errordump.integer)
719         {
720                 // make a savegame
721                 Host_Savegame_to(va("crash-%s.dmp", PRVM_NAME));
722         }
723
724         // dump the stack so host_error can shutdown functions
725         prog->depth = 0;
726         prog->localstack_used = 0;
727
728         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
729         vm_tempstringsbuf.cursize = 0;
730
731         // reset the prog pointer
732         prog = NULL;
733 }
734
735 /*
736 ============================================================================
737 PRVM_ExecuteProgram
738
739 The interpretation main loop
740 ============================================================================
741 */
742
743 /*
744 ====================
745 PRVM_EnterFunction
746
747 Returns the new program statement counter
748 ====================
749 */
750 int PRVM_EnterFunction (mfunction_t *f)
751 {
752         int             i, j, c, o;
753
754         if (!f)
755                 PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s", PRVM_NAME);
756
757         prog->stack[prog->depth].s = prog->xstatement;
758         prog->stack[prog->depth].f = prog->xfunction;
759         prog->stack[prog->depth].profile_acc = -f->profile;
760         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
761         prog->depth++;
762         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
763                 PRVM_ERROR ("stack overflow");
764
765 // save off any locals that the new function steps on
766         c = f->locals;
767         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
768                 PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s", PRVM_NAME);
769
770         for (i=0 ; i < c ; i++)
771                 prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.generic)[f->parm_start + i];
772         prog->localstack_used += c;
773
774 // copy parameters
775         o = f->parm_start;
776         for (i=0 ; i<f->numparms ; i++)
777         {
778                 for (j=0 ; j<f->parm_size[i] ; j++)
779                 {
780                         ((int *)prog->globals.generic)[o] = ((int *)prog->globals.generic)[OFS_PARM0+i*3+j];
781                         o++;
782                 }
783         }
784
785         ++f->recursion;
786         prog->xfunction = f;
787         return f->first_statement - 1;  // offset the s++
788 }
789
790 /*
791 ====================
792 PRVM_LeaveFunction
793 ====================
794 */
795 int PRVM_LeaveFunction (void)
796 {
797         int             i, c;
798         mfunction_t *f;
799
800         if (prog->depth <= 0)
801                 PRVM_ERROR ("prog stack underflow in %s", PRVM_NAME);
802
803         if (!prog->xfunction)
804                 PRVM_ERROR ("PR_LeaveFunction: NULL function in %s", PRVM_NAME);
805 // restore locals from the stack
806         c = prog->xfunction->locals;
807         prog->localstack_used -= c;
808         if (prog->localstack_used < 0)
809                 PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s", PRVM_NAME);
810
811         for (i=0 ; i < c ; i++)
812                 ((int *)prog->globals.generic)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
813
814 // up stack
815         prog->depth--;
816         f = prog->xfunction;
817         --f->recursion;
818         prog->xfunction = prog->stack[prog->depth].f;
819         prog->stack[prog->depth].profile_acc += f->profile;
820         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
821         if(prog->depth > 0)
822         {
823                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
824                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
825         }
826         if(!f->recursion)
827         {
828                 // if f is already on the call stack...
829                 // we cannot add this profile data to it now
830                 // or we would add it more than once
831                 // so, let's only add to the function's profile if it is the outermost call
832                 f->profile_total += prog->stack[prog->depth].profile_acc;
833                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
834         }
835         
836         return prog->stack[prog->depth].s;
837 }
838
839 void PRVM_Init_Exec(void)
840 {
841         // dump the stack
842         prog->depth = 0;
843         prog->localstack_used = 0;
844         // reset the string table
845         // nothing here yet
846 }
847
848 void CStateOp(prvm_prog_t *prog, float vara, float varb, mfunction_t *currentFunc)
849 {
850         // This function should not be called...
851         PRVM_ERROR("CStateOp not implemented.\n");
852 }
853
854 void CWStateOp(prvm_prog_t *prog, float vara, float varb, mfunction_t *currentFunc)
855 {
856         // This function should not be called...
857         PRVM_ERROR("CWStateOp not implemented.\n");
858 }
859
860 void ThinkTimeOp(prvm_prog_t *prog, prvm_edict_t *ent, float varb)
861 {
862         ent->fields.server->nextthink = prog->globals.server->time + varb;
863 }
864
865 #define OPA ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->a])
866 #define OPB ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->b])
867 #define OPC ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->c])
868 extern cvar_t prvm_traceqc;
869 extern cvar_t prvm_statementprofiling;
870 extern sizebuf_t vm_tempstringsbuf;
871 extern qboolean prvm_runawaycheck;
872
873 #ifdef PROFILING
874 /*
875 ====================
876 MVM_ExecuteProgram
877 ====================
878 */
879 void MVM_ExecuteProgram (func_t fnum, const char *errormessage)
880 {
881         dstatement_t    *st, *startst;
882         mfunction_t     *f, *newf;
883         prvm_edict_t    *ed;
884         prvm_eval_t     *ptr;
885         int             jumpcount, cachedpr_trace, exitdepth;
886         int             restorevm_tempstringsbuf_cursize;
887         double  calltime;
888
889         calltime = Sys_DoubleTime();
890
891         if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions)
892         {
893                 if (prog->globaloffsets.self >= 0 && PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict)
894                         PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict), NULL);
895                 PRVM_ERROR ("MVM_ExecuteProgram: %s", errormessage);
896         }
897
898         f = &prog->functions[fnum];
899
900         // after executing this function, delete all tempstrings it created
901         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
902
903         prog->trace = prvm_traceqc.integer;
904
905         // we know we're done when pr_depth drops to this
906         exitdepth = prog->depth;
907
908 // make a stack frame
909         st = &prog->statements[PRVM_EnterFunction (f)];
910         // save the starting statement pointer for profiling
911         // (when the function exits or jumps, the (st - startst) integer value is
912         // added to the function's profile counter)
913         startst = st;
914         // instead of counting instructions, we count jumps
915         jumpcount = 0;
916         // add one to the callcount of this function because otherwise engine-called functions aren't counted
917         prog->xfunction->callcount++;
918
919 chooseexecprogram:
920         cachedpr_trace = prog->trace;
921         if (prvm_statementprofiling.integer || prog->trace)
922         {
923 #define PRVMSLOWINTERPRETER 1
924 #include "prvm_execprogram.h"
925 #undef PRVMSLOWINTERPRETER
926         }
927         else
928         {
929 #include "prvm_execprogram.h"
930         }
931
932 cleanup:
933         if (developer_insane.integer && vm_tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
934                 Con_DPrintf("MVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
935         // delete tempstrings created by this function
936         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
937
938         f->totaltime += (Sys_DoubleTime() - calltime);
939
940         SV_FlushBroadcastMessages();
941 }
942
943 /*
944 ====================
945 CLVM_ExecuteProgram
946 ====================
947 */
948 void CLVM_ExecuteProgram (func_t fnum, const char *errormessage)
949 {
950         dstatement_t    *st, *startst;
951         mfunction_t     *f, *newf;
952         prvm_edict_t    *ed;
953         prvm_eval_t     *ptr;
954         int             jumpcount, cachedpr_trace, exitdepth;
955         int             restorevm_tempstringsbuf_cursize;
956         double  calltime;
957
958         calltime = Sys_DoubleTime();
959
960         if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions)
961         {
962                 if (prog->globaloffsets.self >= 0 && PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict)
963                         PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict), NULL);
964                 PRVM_ERROR ("CLVM_ExecuteProgram: %s", errormessage);
965         }
966
967         f = &prog->functions[fnum];
968
969         // after executing this function, delete all tempstrings it created
970         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
971
972         prog->trace = prvm_traceqc.integer;
973
974         // we know we're done when pr_depth drops to this
975         exitdepth = prog->depth;
976
977 // make a stack frame
978         st = &prog->statements[PRVM_EnterFunction (f)];
979         // save the starting statement pointer for profiling
980         // (when the function exits or jumps, the (st - startst) integer value is
981         // added to the function's profile counter)
982         startst = st;
983         // instead of counting instructions, we count jumps
984         jumpcount = 0;
985         // add one to the callcount of this function because otherwise engine-called functions aren't counted
986         prog->xfunction->callcount++;
987
988 chooseexecprogram:
989         cachedpr_trace = prog->trace;
990         if (prvm_statementprofiling.integer || prog->trace)
991         {
992 #define PRVMSLOWINTERPRETER 1
993 #include "prvm_execprogram.h"
994 #undef PRVMSLOWINTERPRETER
995         }
996         else
997         {
998 #include "prvm_execprogram.h"
999         }
1000
1001 cleanup:
1002         if (developer_insane.integer && vm_tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1003                 Con_DPrintf("CLVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
1004         // delete tempstrings created by this function
1005         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1006
1007         f->totaltime += (Sys_DoubleTime() - calltime);
1008
1009         SV_FlushBroadcastMessages();
1010 }
1011 #endif
1012
1013 /*
1014 ====================
1015 SVVM_ExecuteProgram
1016 ====================
1017 */
1018 void SVVM_ExecuteProgram (func_t fnum, const char *errormessage)
1019 {
1020         dstatement_t    *st, *startst;
1021         mfunction_t     *f, *newf;
1022         prvm_edict_t    *ed;
1023         prvm_eval_t     *ptr;
1024         int             jumpcount, cachedpr_trace, exitdepth;
1025         int             restorevm_tempstringsbuf_cursize;
1026         double  calltime;
1027
1028         calltime = Sys_DoubleTime();
1029
1030         if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions)
1031         {
1032                 if (prog->globaloffsets.self >= 0 && PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict)
1033                         PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict), NULL);
1034                 PRVM_ERROR ("SVVM_ExecuteProgram: %s", errormessage);
1035         }
1036
1037         f = &prog->functions[fnum];
1038
1039         // after executing this function, delete all tempstrings it created
1040         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1041
1042         prog->trace = prvm_traceqc.integer;
1043
1044         // we know we're done when pr_depth drops to this
1045         exitdepth = prog->depth;
1046
1047 // make a stack frame
1048         st = &prog->statements[PRVM_EnterFunction (f)];
1049         // save the starting statement pointer for profiling
1050         // (when the function exits or jumps, the (st - startst) integer value is
1051         // added to the function's profile counter)
1052         startst = st;
1053         // instead of counting instructions, we count jumps
1054         jumpcount = 0;
1055         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1056         prog->xfunction->callcount++;
1057
1058 chooseexecprogram:
1059         cachedpr_trace = prog->trace;
1060         if (prvm_statementprofiling.integer || prog->trace)
1061         {
1062 #define PRVMSLOWINTERPRETER 1
1063 #include "prvm_execprogram.h"
1064 #undef PRVMSLOWINTERPRETER
1065         }
1066         else
1067         {
1068 #include "prvm_execprogram.h"
1069         }
1070
1071 cleanup:
1072         if (developer_insane.integer && vm_tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1073                 Con_DPrintf("SVVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
1074         // delete tempstrings created by this function
1075         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1076
1077         f->totaltime += (Sys_DoubleTime() - calltime);
1078
1079         SV_FlushBroadcastMessages();
1080 }