quakeforge-old/common/pr_exec.c
Joseph Carter 9d3ffb99f8 Yanster's evil hack for Mega2k support. It IS an evil hack. But it's
also workable until we get progs with 32 bit limits standardized and
implemented in our tree.  If someone feels psychotic please work on that.
2000-03-26 14:27:59 +00:00

764 lines
14 KiB
C

/*
pr_exec.c
(description)
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 1999,2000 contributors of the QuakeForge project
Please see the file "AUTHORS" for a list of contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#include <ctype.h>
#include <quakedef.h>
#include <qtypes.h>
#include <net.h>
#include <cmd.h>
#include <sys.h>
#include <console.h>
#include <protocol.h>
#include <server.h>
#include <phys.h>
#include <mathlib.h>
#include <world.h>
#include <crc.h>
typedef struct
{
int s;
dfunction_t *f;
} prstack_t;
#define MAX_STACK_DEPTH 32
prstack_t pr_stack[MAX_STACK_DEPTH];
int pr_depth;
#define LOCALSTACK_SIZE 2048
int localstack[LOCALSTACK_SIZE];
int localstack_used;
qboolean pr_trace;
dfunction_t *pr_xfunction;
int pr_xstatement;
int pr_argc;
char *pr_opnames[] =
{
"DONE",
"MUL_F",
"MUL_V",
"MUL_FV",
"MUL_VF",
"DIV",
"ADD_F",
"ADD_V",
"SUB_F",
"SUB_V",
"EQ_F",
"EQ_V",
"EQ_S",
"EQ_E",
"EQ_FNC",
"NE_F",
"NE_V",
"NE_S",
"NE_E",
"NE_FNC",
"LE",
"GE",
"LT",
"GT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"ADDRESS",
"STORE_F",
"STORE_V",
"STORE_S",
"STORE_ENT",
"STORE_FLD",
"STORE_FNC",
"STOREP_F",
"STOREP_V",
"STOREP_S",
"STOREP_ENT",
"STOREP_FLD",
"STOREP_FNC",
"RETURN",
"NOT_F",
"NOT_V",
"NOT_S",
"NOT_ENT",
"NOT_FNC",
"IF",
"IFNOT",
"CALL0",
"CALL1",
"CALL2",
"CALL3",
"CALL4",
"CALL5",
"CALL6",
"CALL7",
"CALL8",
"STATE",
"GOTO",
"AND",
"OR",
"BITAND",
"BITOR"
};
char *PR_GlobalString (int ofs);
char *PR_GlobalStringNoContents (int ofs);
//=============================================================================
/*
=================
PR_PrintStatement
=================
*/
void PR_PrintStatement (dstatement_t *s)
{
int i;
if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0]))
{
Con_Printf ("%s ", pr_opnames[s->op]);
i = strlen(pr_opnames[s->op]);
for ( ; i<10 ; i++)
Con_Printf (" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT)
Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b);
else if (s->op == OP_GOTO)
{
Con_Printf ("branch %i",s->a);
}
else if ( (unsigned)(s->op - OP_STORE_F) < 6)
{
Con_Printf ("%s",PR_GlobalString(s->a));
Con_Printf ("%s", PR_GlobalStringNoContents(s->b));
}
else
{
if (s->a)
Con_Printf ("%s",PR_GlobalString(s->a));
if (s->b)
Con_Printf ("%s",PR_GlobalString(s->b));
if (s->c)
Con_Printf ("%s", PR_GlobalStringNoContents(s->c));
}
Con_Printf ("\n");
}
/*
============
PR_StackTrace
============
*/
void PR_StackTrace (void)
{
dfunction_t *f;
int i;
if (pr_depth == 0)
{
Con_Printf ("<NO STACK>\n");
return;
}
pr_stack[pr_depth].f = pr_xfunction;
for (i=pr_depth ; i>=0 ; i--)
{
f = pr_stack[i].f;
if (!f)
{
Con_Printf ("<NO FUNCTION>\n");
}
else
Con_Printf ("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name));
}
}
/*
============
PR_Profile_f
============
*/
void PR_Profile_f (void)
{
dfunction_t *f, *best;
int max;
int num;
int i;
num = 0;
do
{
max = 0;
best = NULL;
for (i=0 ; i<progs->numfunctions ; i++)
{
f = &pr_functions[i];
if (f->profile > max)
{
max = f->profile;
best = f;
}
}
if (best)
{
if (num < 10)
Con_Printf ("%7i %s\n", best->profile, PR_GetString(best->s_name));
num++;
best->profile = 0;
}
} while (best);
}
/*
============
PR_RunError
Aborts the currently executing function
============
*/
void PR_RunError (char *error, ...)
{
va_list argptr;
char string[1024];
va_start (argptr, error);
vsnprintf (string, sizeof(string) , error, argptr);
va_end (argptr);
PR_PrintStatement (pr_statements + pr_xstatement);
PR_StackTrace ();
Con_Printf ("%s\n", string);
pr_depth = 0; // dump the stack so {SV|Host}_Error can shutdown functions
#ifdef QUAKEWORLD
SV_Error ("Program error");
#else
Host_Error ("Program error");
#endif
}
/*
============================================================================
PR_ExecuteProgram
The interpretation main loop
============================================================================
*/
/*
====================
PR_EnterFunction
Returns the new program statement counter
====================
*/
int PR_EnterFunction (dfunction_t *f)
{
int i, j, c, o;
pr_stack[pr_depth].s = pr_xstatement;
pr_stack[pr_depth].f = pr_xfunction;
pr_depth++;
if (pr_depth >= MAX_STACK_DEPTH)
PR_RunError ("stack overflow");
// save off any locals that the new function steps on
c = f->locals;
if (localstack_used + c > LOCALSTACK_SIZE)
PR_RunError ("PR_ExecuteProgram: locals stack overflow\n");
for (i=0 ; i < c ; i++)
localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
localstack_used += c;
// copy parameters
o = f->parm_start;
for (i=0 ; i<f->numparms ; i++)
{
for (j=0 ; j<f->parm_size[i] ; j++)
{
((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
o++;
}
}
pr_xfunction = f;
return f->first_statement - 1; // offset the s++
}
/*
====================
PR_LeaveFunction
====================
*/
int PR_LeaveFunction (void)
{
int i, c;
if (pr_depth <= 0)
#ifdef QUAKEWORLD
SV_Error ("prog stack underflow");
#else
Sys_Error ("prog stack underflow");
#endif
// restore locals from the stack
c = pr_xfunction->locals;
localstack_used -= c;
if (localstack_used < 0)
PR_RunError ("PR_ExecuteProgram: locals stack underflow\n");
for (i=0 ; i < c ; i++)
((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];
// up stack
pr_depth--;
pr_xfunction = pr_stack[pr_depth].f;
return pr_stack[pr_depth].s;
}
/*
====================
PR_ExecuteProgram
====================
*/
void PR_ExecuteProgram (func_t fnum)
{
eval_t *a=0, *b=0, *c=0;
int s;
dstatement_t *st=0;
dfunction_t *f, *newf;
int runaway;
int i;
edict_t *ed;
int exitdepth;
eval_t *ptr;
if (!fnum || fnum >= progs->numfunctions)
{
if (pr_global_struct->self)
ED_Print (PROG_TO_EDICT(pr_global_struct->self));
#ifdef QUAKEWORLD
SV_Error ("PR_ExecuteProgram: NULL function");
#else
Host_Error ("PR_ExecuteProgram: NULL function");
#endif
}
f = &pr_functions[fnum];
runaway = 100000;
pr_trace = false;
// make a stack frame
exitdepth = pr_depth;
s = PR_EnterFunction (f);
while (1)
{
int ofsa, ofsb, ofsc;
s++; // next statement
st = &pr_statements[s];
// FIXME: offsets bigger than 32767 result in negative indexes into
// pr_globals[]. This is a hack to make the 15 bit limit
// temporarily 16 bit in this one case. It should go away
// when 32 bit limits are implemented in a new progs.dat
// version. (Fix by Yanster)
if (st->a & 0x8000) ofsa = (int)st->a + 0xFFFF; else ofsa = st->a;
if (st->b & 0x8000) ofsb = (int)st->b + 0xFFFF; else ofsb = st->b;
if (st->c & 0x8000) ofsc = (int)st->c + 0xFFFF; else ofsc = st->c;
// End of Evil Hack
a = (eval_t *)&pr_globals[st->a];
b = (eval_t *)&pr_globals[st->b];
c = (eval_t *)&pr_globals[st->c];
if (--runaway == 0)
PR_RunError ("runaway loop error");
pr_xfunction->profile++;
pr_xstatement = s;
if (pr_trace)
PR_PrintStatement (st);
switch (st->op)
{
case OP_ADD_F:
c->_float = a->_float + b->_float;
break;
case OP_ADD_V:
c->vector[0] = a->vector[0] + b->vector[0];
c->vector[1] = a->vector[1] + b->vector[1];
c->vector[2] = a->vector[2] + b->vector[2];
break;
case OP_SUB_F:
c->_float = a->_float - b->_float;
break;
case OP_SUB_V:
c->vector[0] = a->vector[0] - b->vector[0];
c->vector[1] = a->vector[1] - b->vector[1];
c->vector[2] = a->vector[2] - b->vector[2];
break;
case OP_MUL_F:
c->_float = a->_float * b->_float;
break;
case OP_MUL_V:
c->_float = a->vector[0]*b->vector[0]
+ a->vector[1]*b->vector[1]
+ a->vector[2]*b->vector[2];
break;
case OP_MUL_FV:
c->vector[0] = a->_float * b->vector[0];
c->vector[1] = a->_float * b->vector[1];
c->vector[2] = a->_float * b->vector[2];
break;
case OP_MUL_VF:
c->vector[0] = b->_float * a->vector[0];
c->vector[1] = b->_float * a->vector[1];
c->vector[2] = b->_float * a->vector[2];
break;
case OP_DIV_F:
c->_float = a->_float / b->_float;
break;
case OP_MOD_F:
c->_float = (int)a->_float % (int)b->_float;
break;
case OP_BITAND:
c->_float = (int)a->_float & (int)b->_float;
break;
case OP_BITOR:
c->_float = (int)a->_float | (int)b->_float;
break;
case OP_GE:
c->_float = a->_float >= b->_float;
break;
case OP_LE:
c->_float = a->_float <= b->_float;
break;
case OP_GT:
c->_float = a->_float > b->_float;
break;
case OP_LT:
c->_float = a->_float < b->_float;
break;
case OP_AND:
c->_float = a->_float && b->_float;
break;
case OP_OR:
c->_float = a->_float || b->_float;
break;
case OP_NOT_F:
c->_float = !a->_float;
break;
case OP_NOT_V:
c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
break;
case OP_NOT_S:
c->_float = !a->string || !*PR_GetString(a->string);
break;
case OP_NOT_FNC:
c->_float = !a->function;
break;
case OP_NOT_ENT:
c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
break;
case OP_EQ_F:
c->_float = a->_float == b->_float;
break;
case OP_EQ_V:
c->_float = (a->vector[0] == b->vector[0]) &&
(a->vector[1] == b->vector[1]) &&
(a->vector[2] == b->vector[2]);
break;
case OP_EQ_S:
c->_float = !strcmp(PR_GetString(a->string), PR_GetString(b->string));
break;
case OP_EQ_E:
c->_float = a->_int == b->_int;
break;
case OP_EQ_FNC:
c->_float = a->function == b->function;
break;
case OP_NE_F:
c->_float = a->_float != b->_float;
break;
case OP_NE_V:
c->_float = (a->vector[0] != b->vector[0]) ||
(a->vector[1] != b->vector[1]) ||
(a->vector[2] != b->vector[2]);
break;
case OP_NE_S:
c->_float = strcmp(PR_GetString(a->string), PR_GetString(b->string));
break;
case OP_NE_E:
c->_float = a->_int != b->_int;
break;
case OP_NE_FNC:
c->_float = a->function != b->function;
break;
//==================
case OP_STORE_F:
case OP_STORE_ENT:
case OP_STORE_FLD: // integers
case OP_STORE_S:
case OP_STORE_FNC: // pointers
b->_int = a->_int;
break;
case OP_STORE_V:
b->vector[0] = a->vector[0];
b->vector[1] = a->vector[1];
b->vector[2] = a->vector[2];
break;
case OP_STOREP_F:
case OP_STOREP_ENT:
case OP_STOREP_FLD: // integers
case OP_STOREP_S:
case OP_STOREP_FNC: // pointers
ptr = (eval_t *)((byte *)sv.edicts + b->_int);
ptr->_int = a->_int;
break;
case OP_STOREP_V:
ptr = (eval_t *)((byte *)sv.edicts + b->_int);
ptr->vector[0] = a->vector[0];
ptr->vector[1] = a->vector[1];
ptr->vector[2] = a->vector[2];
break;
case OP_ADDRESS:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
PR_RunError ("assignment to world entity");
c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
break;
case OP_LOAD_F:
case OP_LOAD_FLD:
case OP_LOAD_ENT:
case OP_LOAD_S:
case OP_LOAD_FNC:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
a = (eval_t *)((int *)&ed->v + b->_int);
c->_int = a->_int;
break;
case OP_LOAD_V:
ed = PROG_TO_EDICT(a->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // make sure it's in range
#endif
a = (eval_t *)((int *)&ed->v + b->_int);
c->vector[0] = a->vector[0];
c->vector[1] = a->vector[1];
c->vector[2] = a->vector[2];
break;
//==================
case OP_IFNOT:
if (!a->_int)
s += st->b - 1; // offset the s++
break;
case OP_IF:
if (a->_int)
s += st->b - 1; // offset the s++
break;
case OP_GOTO:
s += st->a - 1; // offset the s++
break;
case OP_CALL0:
case OP_CALL1:
case OP_CALL2:
case OP_CALL3:
case OP_CALL4:
case OP_CALL5:
case OP_CALL6:
case OP_CALL7:
case OP_CALL8:
pr_argc = st->op - OP_CALL0;
if (!a->function)
PR_RunError ("NULL function");
newf = &pr_functions[a->function];
if (newf->first_statement < 0)
{ // negative statements are built in functions
i = -newf->first_statement;
if (i >= pr_numbuiltins)
PR_RunError ("Bad builtin call number");
pr_builtins[i] ();
break;
}
s = PR_EnterFunction (newf);
break;
case OP_DONE:
case OP_RETURN:
pr_globals[OFS_RETURN] = pr_globals[st->a];
pr_globals[OFS_RETURN+1] = pr_globals[st->a+1];
pr_globals[OFS_RETURN+2] = pr_globals[st->a+2];
s = PR_LeaveFunction ();
if (pr_depth == exitdepth)
return; // all done
break;
case OP_STATE:
ed = PROG_TO_EDICT(pr_global_struct->self);
#ifdef QUAKEWORLD
ed->v.nextthink = pr_global_struct->time + 0.1;
#else
# ifdef FPS_20
ed->v.nextthink = pr_global_struct->time + 0.05;
# else
ed->v.nextthink = pr_global_struct->time + 0.1;
# endif
#endif
if (a->_float != ed->v.frame)
{
ed->v.frame = a->_float;
}
ed->v.think = b->function;
break;
default:
PR_RunError ("Bad opcode %i", st->op);
}
}
}
/*----------------------*/
#ifdef QUAKEWORLD
char *pr_strtbl[MAX_PRSTR];
int num_prstr;
#endif
char *PR_GetString(int num)
{
#ifdef QUAKEWORLD
if (num < 0) {
//Con_DPrintf("GET:%d == %s\n", num, pr_strtbl[-num]);
return pr_strtbl[-num];
}
#endif
return pr_strings + num;
}
int PR_SetString(char *s)
{
#ifdef QUAKEWORLD /* XoXus: moved this back to include 'i' */
int i;
if (s - pr_strings < 0) {
for (i = 0; i <= num_prstr; i++)
if (pr_strtbl[i] == s)
break;
if (i < num_prstr)
return -i;
if (num_prstr == MAX_PRSTR - 1)
Sys_Error("MAX_PRSTR");
num_prstr++;
pr_strtbl[num_prstr] = s;
//Con_DPrintf("SET:%d == %s\n", -num_prstr, s);
return -num_prstr;
}
#endif
return (int)(s - pr_strings);
}