This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/prvm/vm_exec.c

1121 lines
29 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
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 the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "engine.h"
#include "progsvm.h"
char *prvm_opnames[] =
{
"^5DONE",
"MUL_F",
"MUL_V",
"MUL_FV",
"MUL_VF",
"DIV",
"ADD_F",
"ADD_V",
"SUB_F",
"SUB_V",
"^2EQ_F",
"^2EQ_V",
"^2EQ_S",
"^2EQ_E",
"^2EQ_FNC",
"^2NE_F",
"^2NE_V",
"^2NE_S",
"^2NE_E",
"^2NE_FNC",
"^2LE",
"^2GE",
"^2LT",
"^2GT",
"^6FIELD_F",
"^6FIELD_V",
"^6FIELD_S",
"^6FIELD_ENT",
"^6FIELD_FLD",
"^6FIELD_FNC",
"^1ADDRESS",
"STORE_F",
"STORE_V",
"STORE_S",
"STORE_ENT",
"STORE_FLD",
"STORE_FNC",
"^1STOREP_F",
"^1STOREP_V",
"^1STOREP_S",
"^1STOREP_ENT",
"^1STOREP_FLD",
"^1STOREP_FNC",
"^5RETURN",
"^2NOT_F",
"^2NOT_V",
"^2NOT_S",
"^2NOT_ENT",
"^2NOT_FNC",
"^5IF",
"^5IFNOT",
"^3CALL0",
"^3CALL1",
"^3CALL2",
"^3CALL3",
"^3CALL4",
"^3CALL5",
"^3CALL6",
"^3CALL7",
"^3CALL8",
"^1STATE",
"^5GOTO",
"^2AND",
"^2OR",
"BITAND",
"BITOR"
};
char *PRVM_GlobalString (int ofs);
char *PRVM_GlobalStringNoContents (int ofs);
//=============================================================================
/*
=================
PRVM_PrintStatement
=================
*/
extern cvar_t *prvm_statementprofiling;
void PRVM_PrintStatement (dstatement_t *s)
{
size_t i;
int opnum = (int)(s - prog->statements);
Msg("s%i: ", opnum);
if( prog->statement_linenums )
Msg( "%s:%i: ", PRVM_GetString( prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
if (prvm_statementprofiling->value)
Msg("%7.0f ", prog->statement_profile[s - prog->statements]);
if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
{
Msg("%s ", prvm_opnames[s->op]);
i = strlen(prvm_opnames[s->op]);
// don't count a preceding color tag when padding the name
if (prvm_opnames[s->op][0] == STRING_COLOR_TAG)
i -= 2;
for ( ; i<10 ; i++)
Con_Print(" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT)
Msg("%s, s%i",PRVM_GlobalString((unsigned short) s->a),(signed short)s->b + opnum);
else if (s->op == OP_GOTO)
Msg("s%i",(signed short)s->a + opnum);
else if ( (unsigned)(s->op - OP_STORE_F) < 6)
{
Con_Print(PRVM_GlobalString((unsigned short) s->a));
Con_Print(", ");
Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b));
}
else if (s->op == OP_ADDRESS || (unsigned)(s->op - OP_LOAD_F) < 6)
{
if (s->a)
Con_Print(PRVM_GlobalString((unsigned short) s->a));
if (s->b)
{
Con_Print(", ");
Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b));
}
if (s->c)
{
Con_Print(", ");
Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c));
}
}
else
{
if (s->a)
Con_Print(PRVM_GlobalString((unsigned short) s->a));
if (s->b)
{
Con_Print(", ");
Con_Print(PRVM_GlobalString((unsigned short) s->b));
}
if (s->c)
{
Con_Print(", ");
Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c));
}
}
Con_Print("\n");
}
void PRVM_PrintFunctionStatements (const char *name)
{
int i, firststatement, endstatement;
mfunction_t *func;
func = PRVM_ED_FindFunction (name);
if (!func)
{
Msg("%s progs: no function named %s\n", PRVM_NAME, name);
return;
}
firststatement = func->first_statement;
if (firststatement < 0)
{
Msg("%s progs: function %s is builtin #%i\n", PRVM_NAME, name, -firststatement);
return;
}
// find the end statement
endstatement = prog->progs->numstatements;
for (i = 0;i < prog->progs->numfunctions;i++)
if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
endstatement = prog->functions[i].first_statement;
// now print the range of statements
Msg("%s progs: disassembly of function %s (statements %i-%i):\n", PRVM_NAME, name, firststatement, endstatement);
for (i = firststatement;i < endstatement;i++)
{
PRVM_PrintStatement(prog->statements + i);
prog->statement_profile[i] = 0;
}
}
/*
============
PRVM_PrintFunction_f
============
*/
void PRVM_PrintFunction_f (void)
{
if (Cmd_Argc() != 3)
{
Msg("usage: prvm_printfunction <program name> <function name>\n");
return;
}
PRVM_Begin;
if(!PRVM_SetProgFromString(Cmd_Argv(1)))
return;
PRVM_PrintFunctionStatements(Cmd_Argv(2));
PRVM_End;
}
/*
============
PRVM_StackTrace
============
*/
void PRVM_StackTrace (void)
{
mfunction_t *f;
int i;
prog->stack[prog->depth].s = prog->xstatement;
prog->stack[prog->depth].f = prog->xfunction;
for (i = prog->depth;i > 0;i--)
{
f = prog->stack[i].f;
if (!f)
Con_Print("<NULL FUNCTION>\n");
else
Msg("%12s : %s : statement %i\n", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement);
}
}
void PRVM_Profile (int maxfunctions, int mininstructions)
{
mfunction_t *f, *best;
int i, num;
double max;
Msg( "%s Profile:\n[CallCount] [Statements] [BuiltinCost]\n", PRVM_NAME );
num = 0;
do
{
max = 0;
best = NULL;
for (i=0 ; i<prog->progs->numfunctions ; i++)
{
f = &prog->functions[i];
if (max < f->profile + f->builtinsprofile + f->callcount)
{
max = f->profile + f->builtinsprofile + f->callcount;
best = f;
}
}
if (best)
{
if (num < maxfunctions && max >= mininstructions)
{
if (best->first_statement < 0)
Msg("%9.0f ----- builtin ----- %s\n", best->callcount, PRVM_GetString(best->s_name));
else
Msg("%9.0f %9.0f %9.0f %s\n", best->callcount, best->profile, best->builtinsprofile, PRVM_GetString(best->s_name));
}
num++;
best->profile = 0;
best->builtinsprofile = 0;
best->callcount = 0;
}
} while (best);
}
/*
============
PRVM_Profile_f
============
*/
void PRVM_Profile_f (void)
{
int howmany;
howmany = 1<<30;
if (Cmd_Argc() == 3)
howmany = atoi(Cmd_Argv(2));
else if (Cmd_Argc() != 2)
{
Con_Print("prvm_profile <program name>\n");
return;
}
PRVM_Begin;
if(!PRVM_SetProgFromString(Cmd_Argv(1)))
return;
PRVM_Profile(howmany, 1);
PRVM_End;
}
void PRVM_CrashAll()
{
int i;
prvm_prog_t *oldprog = prog;
for(i = 0; i < PRVM_MAXPROGS; i++)
{
if(!PRVM_ProgLoaded(i))
continue;
PRVM_SetProg(i);
PRVM_Crash();
}
prog = oldprog;
}
void PRVM_PrintState(void)
{
int i;
if (prog->xfunction)
{
for (i = -7; i <= 0;i++)
if (prog->xstatement + i >= prog->xfunction->first_statement)
PRVM_PrintStatement (prog->statements + prog->xstatement + i);
}
else
Con_Print("null function executing??\n");
PRVM_StackTrace ();
}
void PRVM_Crash()
{
if (prog == NULL)
return;
if( prog->depth > 0 )
{
Msg("QuakeC crash report for %s:\n", PRVM_NAME);
PRVM_PrintState();
}
// dump the stack so host_error can shutdown functions
prog->depth = 0;
prog->localstack_used = 0;
// reset the prog pointer
prog = NULL;
}
/*
============================================================================
PRVM_ExecuteProgram
The interpretation main loop
============================================================================
*/
/*
====================
PRVM_EnterFunction
Returns the new program statement counter
====================
*/
int PRVM_EnterFunction (mfunction_t *f)
{
int i, j, c, o;
if (!f)
PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s", PRVM_NAME);
prog->stack[prog->depth].s = prog->xstatement;
prog->stack[prog->depth].f = prog->xfunction;
prog->depth++;
if (prog->depth >=PRVM_MAX_STACK_DEPTH)
PRVM_ERROR ("stack overflow");
// save off any locals that the new function steps on
c = f->locals;
if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s", PRVM_NAME);
for (i=0 ; i < c ; i++)
prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.generic)[f->parm_start + i];
prog->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 *)prog->globals.generic)[o] = ((int *)prog->globals.generic)[OFS_PARM0+i*3+j];
o++;
}
}
prog->xfunction = f;
return f->first_statement - 1; // offset the s++
}
/*
====================
PRVM_LeaveFunction
====================
*/
int PRVM_LeaveFunction (void)
{
int i, c;
if (prog->depth <= 0)
PRVM_ERROR ("prog stack underflow in %s", PRVM_NAME);
if (!prog->xfunction)
PRVM_ERROR ("PR_LeaveFunction: NULL function in %s", PRVM_NAME);
// restore locals from the stack
c = prog->xfunction->locals;
prog->localstack_used -= c;
if (prog->localstack_used < 0)
PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s", PRVM_NAME);
for (i=0 ; i < c ; i++)
((int *)prog->globals.generic)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
// up stack
prog->depth--;
prog->xfunction = prog->stack[prog->depth].f;
return prog->stack[prog->depth].s;
}
void PRVM_Init_Exec(void)
{
// dump the stack
prog->depth = 0;
prog->localstack_used = 0;
// reset the string table
// nothing here yet
}
/*
====================
PRVM_ExecuteProgram
====================
*/
// LordHavoc: optimized
#define OPA ((prvm_eval_t *)&prog->globals.generic[(word) st->a])
#define OPB ((prvm_eval_t *)&prog->globals.generic[(word) st->b])
#define OPC ((prvm_eval_t *)&prog->globals.generic[(word) st->c])
extern cvar_t *prvm_boundscheck;
extern cvar_t *prvm_traceqc;
extern cvar_t *prvm_statementprofiling;
extern int PRVM_ED_FindFieldOffset (const char *field);
extern ddef_t* PRVM_ED_FindGlobal(const char *name);
void PRVM_ExecuteProgram (func_t fnum, const char *errormessage)
{
dstatement_t *st, *startst;
mfunction_t *f, *newf;
prvm_edict_t *ed;
prvm_eval_t *ptr;
int jumpcount, cachedpr_trace, exitdepth;
if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions)
{
if (prog->self && PRVM_G_INT(prog->self->ofs))
PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs)));
PRVM_ERROR ("PRVM_ExecuteProgram: %s", errormessage);
}
f = &prog->functions[fnum];
prog->trace = prvm_traceqc->value;
// we know we're done when pr_depth drops to this
exitdepth = prog->depth;
// make a stack frame
st = &prog->statements[PRVM_EnterFunction (f)];
// save the starting statement pointer for profiling
// (when the function exits or jumps, the (st - startst) integer value is
// added to the function's profile counter)
startst = st;
// instead of counting instructions, we count jumps
jumpcount = 0;
// add one to the callcount of this function because otherwise engine-called functions aren't counted
prog->xfunction->callcount++;
chooseexecprogram:
cachedpr_trace = prog->trace;
while (1)
{
st++;
if (prog->trace) PRVM_PrintStatement(st);
if (prvm_statementprofiling->value) prog->statement_profile[st - prog->statements]++;
switch (st->op)
{
case OP_ADD_F:
OPC->_float = OPA->_float + OPB->_float;
break;
case OP_ADD_V:
OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
break;
case OP_SUB_F:
OPC->_float = OPA->_float - OPB->_float;
break;
case OP_SUB_V:
OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
break;
case OP_MUL_F:
OPC->_float = OPA->_float * OPB->_float;
break;
case OP_MUL_V:
OPC->_float = OPA->vector[0]*OPB->vector[0] + OPA->vector[1]*OPB->vector[1] + OPA->vector[2]*OPB->vector[2];
break;
case OP_MUL_FV:
OPC->vector[0] = OPA->_float * OPB->vector[0];
OPC->vector[1] = OPA->_float * OPB->vector[1];
OPC->vector[2] = OPA->_float * OPB->vector[2];
break;
case OP_MUL_VF:
OPC->vector[0] = OPB->_float * OPA->vector[0];
OPC->vector[1] = OPB->_float * OPA->vector[1];
OPC->vector[2] = OPB->_float * OPA->vector[2];
break;
case OP_DIV_F:
if( OPB->_float != 0.0f )
{
OPC->_float = OPA->_float / OPB->_float;
}
else
{
if( host.developer >= D_WARN )
{
prog->xfunction->profile += (st - startst);
startst = st;
prog->xstatement = st - prog->statements;
VM_Warning( "Attempted division by zero in %s\n", PRVM_NAME );
}
OPC->_float = 0.0f;
}
break;
case OP_BITAND:
OPC->_float = (int)OPA->_float & (int)OPB->_float;
break;
case OP_BITOR:
OPC->_float = (int)OPA->_float | (int)OPB->_float;
break;
case OP_GE:
OPC->_float = OPA->_float >= OPB->_float;
break;
case OP_LE:
OPC->_float = OPA->_float <= OPB->_float;
break;
case OP_GT:
OPC->_float = OPA->_float > OPB->_float;
break;
case OP_LT:
OPC->_float = OPA->_float < OPB->_float;
break;
case OP_AND:
OPC->_float = OPA->_float && OPB->_float;
break;
case OP_OR:
OPC->_float = OPA->_float || OPB->_float;
break;
case OP_NOT_F:
OPC->_float = !OPA->_float;
break;
case OP_NOT_V:
OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2];
break;
case OP_NOT_S:
OPC->_float = !OPA->string || !*PRVM_GetString(OPA->string);
break;
case OP_NOT_FNC:
OPC->_float = !OPA->function;
break;
case OP_NOT_ENT:
OPC->_float = (OPA->edict == 0);
break;
case OP_EQ_F:
OPC->_float = OPA->_float == OPB->_float;
break;
case OP_EQ_V:
OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]);
break;
case OP_EQ_S:
OPC->_float = !strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string));
break;
case OP_EQ_E:
OPC->_float = OPA->_int == OPB->_int;
break;
case OP_EQ_FNC:
OPC->_float = OPA->function == OPB->function;
break;
case OP_NE_F:
OPC->_float = OPA->_float != OPB->_float;
break;
case OP_NE_V:
OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]);
break;
case OP_NE_S:
OPC->_float = strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string));
break;
case OP_NE_E:
OPC->_float = OPA->_int != OPB->_int;
break;
case OP_NE_FNC:
OPC->_float = OPA->function != OPB->function;
break;
case OP_STORE_F:
case OP_STORE_ENT:
case OP_STORE_FLD: // integers
case OP_STORE_S:
case OP_STORE_FNC: // pointers
OPB->_int = OPA->_int;
break;
case OP_STORE_V:
OPB->ivector[0] = OPA->ivector[0];
OPB->ivector[1] = OPA->ivector[1];
OPB->ivector[2] = OPA->ivector[2];
break;
case OP_STOREP_F:
case OP_STOREP_ENT:
case OP_STOREP_FLD: // integers
case OP_STOREP_S:
case OP_STOREP_FNC: // pointers
if(prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 4 > prog->edictareasize))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int);
return;
}
ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
ptr->_int = OPA->_int;
break;
case OP_STOREP_V:
if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 12 > prog->edictareasize))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int);
return;
}
ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
ptr->vector[0] = OPA->vector[0];
ptr->vector[1] = OPA->vector[1];
ptr->vector[2] = OPA->vector[2];
break;
case OP_ADDRESS:
if (prvm_boundscheck->value && ((uint)(OPB->_int) >= (uint)(prog->progs->entityfields)))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("%s attempted to address an invalid field (%i) in an edict", PRVM_NAME, OPB->_int);
return;
}
if (OPA->edict == 0 && !prog->allowworldwrites)
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("forbidden assignment to null/world entity in %s", PRVM_NAME);
return;
}
ed = PRVM_PROG_TO_EDICT(OPA->edict);
OPC->_int = (unsigned char *)((int *)ed->fields.vp + OPB->_int) - (unsigned char *)prog->edictsfields;
break;
case OP_LOAD_F:
case OP_LOAD_FLD:
case OP_LOAD_ENT:
case OP_LOAD_S:
case OP_LOAD_FNC:
if (prvm_boundscheck->value && ((uint)(OPB->_int) >= (uint)(prog->progs->entityfields)))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int);
return;
}
ed = PRVM_PROG_TO_EDICT(OPA->edict);
OPC->_int = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->_int;
break;
case OP_LOAD_V:
if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 2 >= prog->progs->entityfields))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int);
return;
}
ed = PRVM_PROG_TO_EDICT(OPA->edict);
OPC->vector[0] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[0];
OPC->vector[1] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[1];
OPC->vector[2] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[2];
break;
case OP_IFNOT:
if (!OPA->_int)
{
prog->xfunction->profile += (st - startst);
st += st->b - 1; // offset the s++
startst = st;
if (++jumpcount == 10000000)
{
prog->xstatement = st - prog->statements;
PRVM_Profile(1<<30, 1000000);
PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME);
}
}
break;
case OP_IF:
if (OPA->_int)
{
prog->xfunction->profile += (st - startst);
st += st->b - 1; // offset the s++
startst = st;
if (++jumpcount == 10000000)
{
prog->xstatement = st - prog->statements;
PRVM_Profile(1<<30, 1000000);
PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME);
}
}
break;
case OP_GOTO:
prog->xfunction->profile += (st - startst);
st += st->a - 1; // offset the s++
startst = st;
if (++jumpcount == 10000000)
{
prog->xstatement = st - prog->statements;
PRVM_Profile(1<<30, 1000000);
PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME);
}
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:
prog->xfunction->profile += (st - startst);
startst = st;
prog->xstatement = st - prog->statements;
prog->argc = st->op - OP_CALL0;
if (!OPA->function) PRVM_ERROR("NULL function in %s", PRVM_NAME);
newf = &prog->functions[OPA->function];
newf->callcount++;
if (newf->first_statement < 0)
{
// negative statements are built in functions
int builtinnumber = -newf->first_statement;
prog->xfunction->builtinsprofile++;
if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
prog->builtins[builtinnumber]();
else PRVM_ERROR("No such builtin #%i in %s", builtinnumber, PRVM_NAME);
}
else st = prog->statements + PRVM_EnterFunction(newf);
startst = st;
break;
case OP_DONE:
case OP_RETURN:
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
prog->globals.generic[OFS_RETURN+0] = prog->globals.generic[(word) st->a+0];
prog->globals.generic[OFS_RETURN+1] = prog->globals.generic[(word) st->a+1];
prog->globals.generic[OFS_RETURN+2] = prog->globals.generic[(word) st->a+2];
st = prog->statements + PRVM_LeaveFunction();
startst = st;
if (prog->depth <= exitdepth)
return; // all done
if (prog->trace != cachedpr_trace)
goto chooseexecprogram;
break;
case OP_STATE:
if(prog->flag & PRVM_OP_STATE)
{
ed = PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs));
PRVM_E_FLOAT(ed, PRVM_ED_FindField ("nextthink")->ofs) = *prog->time + 0.1;
PRVM_E_FLOAT(ed, PRVM_ED_FindField ("frame")->ofs) = OPA->_float;
*(func_t *)((float*)ed->fields.vp + PRVM_ED_FindField ("think")->ofs) = OPB->function;
}
else
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR("OP_STATE not supported by %s", PRVM_NAME);
}
break;
// LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized
#if 0
case OP_ADD_I:
OPC->_int = OPA->_int + OPB->_int;
break;
case OP_ADD_IF:
OPC->_int = OPA->_int + (int) OPB->_float;
break;
case OP_ADD_FI:
OPC->_float = OPA->_float + (float) OPB->_int;
break;
case OP_SUB_I:
OPC->_int = OPA->_int - OPB->_int;
break;
case OP_SUB_IF:
OPC->_int = OPA->_int - (int) OPB->_float;
break;
case OP_SUB_FI:
OPC->_float = OPA->_float - (float) OPB->_int;
break;
case OP_MUL_I:
OPC->_int = OPA->_int * OPB->_int;
break;
case OP_MUL_IF:
OPC->_int = OPA->_int * (int) OPB->_float;
break;
case OP_MUL_FI:
OPC->_float = OPA->_float * (float) OPB->_int;
break;
case OP_MUL_VI:
OPC->vector[0] = (float) OPB->_int * OPA->vector[0];
OPC->vector[1] = (float) OPB->_int * OPA->vector[1];
OPC->vector[2] = (float) OPB->_int * OPA->vector[2];
break;
case OP_DIV_VF:
{
float temp = 1.0f / OPB->_float;
OPC->vector[0] = temp * OPA->vector[0];
OPC->vector[1] = temp * OPA->vector[1];
OPC->vector[2] = temp * OPA->vector[2];
}
break;
case OP_DIV_I:
OPC->_int = OPA->_int / OPB->_int;
break;
case OP_DIV_IF:
OPC->_int = OPA->_int / (int) OPB->_float;
break;
case OP_DIV_FI:
OPC->_float = OPA->_float / (float) OPB->_int;
break;
case OP_CONV_ITOF:
OPC->_float = OPA->_int;
break;
case OP_CONV_FTOI:
OPC->_int = OPA->_float;
break;
case OP_BITAND_I:
OPC->_int = OPA->_int & OPB->_int;
break;
case OP_BITOR_I:
OPC->_int = OPA->_int | OPB->_int;
break;
case OP_BITAND_IF:
OPC->_int = OPA->_int & (int)OPB->_float;
break;
case OP_BITOR_IF:
OPC->_int = OPA->_int | (int)OPB->_float;
break;
case OP_BITAND_FI:
OPC->_float = (int)OPA->_float & OPB->_int;
break;
case OP_BITOR_FI:
OPC->_float = (int)OPA->_float | OPB->_int;
break;
case OP_GE_I:
OPC->_float = OPA->_int >= OPB->_int;
break;
case OP_LE_I:
OPC->_float = OPA->_int <= OPB->_int;
break;
case OP_GT_I:
OPC->_float = OPA->_int > OPB->_int;
break;
case OP_LT_I:
OPC->_float = OPA->_int < OPB->_int;
break;
case OP_AND_I:
OPC->_float = OPA->_int && OPB->_int;
break;
case OP_OR_I:
OPC->_float = OPA->_int || OPB->_int;
break;
case OP_GE_IF:
OPC->_float = (float)OPA->_int >= OPB->_float;
break;
case OP_LE_IF:
OPC->_float = (float)OPA->_int <= OPB->_float;
break;
case OP_GT_IF:
OPC->_float = (float)OPA->_int > OPB->_float;
break;
case OP_LT_IF:
OPC->_float = (float)OPA->_int < OPB->_float;
break;
case OP_AND_IF:
OPC->_float = (float)OPA->_int && OPB->_float;
break;
case OP_OR_IF:
OPC->_float = (float)OPA->_int || OPB->_float;
break;
case OP_GE_FI:
OPC->_float = OPA->_float >= (float)OPB->_int;
break;
case OP_LE_FI:
OPC->_float = OPA->_float <= (float)OPB->_int;
break;
case OP_GT_FI:
OPC->_float = OPA->_float > (float)OPB->_int;
break;
case OP_LT_FI:
OPC->_float = OPA->_float < (float)OPB->_int;
break;
case OP_AND_FI:
OPC->_float = OPA->_float && (float)OPB->_int;
break;
case OP_OR_FI:
OPC->_float = OPA->_float || (float)OPB->_int;
break;
case OP_NOT_I:
OPC->_float = !OPA->_int;
break;
case OP_EQ_I:
OPC->_float = OPA->_int == OPB->_int;
break;
case OP_EQ_IF:
OPC->_float = (float)OPA->_int == OPB->_float;
break;
case OP_EQ_FI:
OPC->_float = OPA->_float == (float)OPB->_int;
break;
case OP_NE_I:
OPC->_float = OPA->_int != OPB->_int;
break;
case OP_NE_IF:
OPC->_float = (float)OPA->_int != OPB->_float;
break;
case OP_NE_FI:
OPC->_float = OPA->_float != (float)OPB->_int;
break;
case OP_STORE_I:
OPB->_int = OPA->_int;
break;
case OP_STOREP_I:
if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 4 > prog->edictareasize))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to write to an out of bounds edict", PRVM_NAME);
return;
}
ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int);
ptr->_int = OPA->_int;
break;
case OP_LOAD_I:
if (prvm_boundscheck->value && (OPA->edict < 0 || OPA->edict >= prog->edictareasize))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to read an out of bounds edict number", PRVM_NAME);
return;
}
if (OPB->_int < 0 || OPB->_int >= prog->progs->entityfields)
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to read an invalid field in an edict", PRVM_NAME);
return;
}
ed = PRVM_PROG_TO_EDICT(OPA->edict);
OPC->_int = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->_int;
break;
case OP_GSTOREP_I:
case OP_GSTOREP_F:
case OP_GSTOREP_ENT:
case OP_GSTOREP_FLD: // integers
case OP_GSTOREP_S:
case OP_GSTOREP_FNC: // pointers
if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int >= (uint)prog->progs->numglobaldefs))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME);
return;
}
prog->globals.generic[OPB->_int] = OPA->_float;
break;
case OP_GSTOREP_V:
if (prvm_boundscheck->value && (OPB->_int < 0 || OPB->_int + 2 >= (uint)prog->progs->numglobaldefs))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME);
return;
}
prog->globals.generic[OPB->_int+0] = OPA->vector[0];
prog->globals.generic[OPB->_int+1] = OPA->vector[1];
prog->globals.generic[OPB->_int+2] = OPA->vector[2];
break;
case OP_GLOBALADDRESS:
i = OPA->_int + (int)OPB->_float;
if (prvm_boundscheck->value && (i < 0 || i >= (uint)prog->progs->numglobaldefs))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to address an out of bounds global", PRVM_NAME);
return;
}
OPC->_float = prog->globals.generic[i];
break;
case OP_LOADA_I:
case OP_LOADA_F:
case OP_LOADA_FLD:
case OP_LOADA_ENT:
case OP_LOADA_S:
case OP_LOADA_FNC:
if (prvm_boundscheck->value && (OPA->_int < 0 || OPA->_int >= (uint)prog->progs->numglobaldefs))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME);
return;
}
OPC->_float = prog->globals.generic[OPA->_int];
break;
case OP_LOADA_V:
if (prvm_boundscheck->value && (OPA->_int < 0 || OPA->_int + 2 >= (uint)prog->progs->numglobaldefs))
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME);
return;
}
OPC->vector[0] = prog->globals.generic[OPA->_int+0];
OPC->vector[1] = prog->globals.generic[OPA->_int+1];
OPC->vector[2] = prog->globals.generic[OPA->_int+2];
break;
case OP_BOUNDCHECK:
if (OPA->_int < 0 || OPA->_int >= st->b)
{
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("%s Progs boundcheck failed at line number %d, value is < 0 or >= %d", PRVM_NAME, st->b, st->c);
return;
}
break;
#endif
default:
prog->xfunction->profile += (st - startst);
prog->xstatement = st - prog->statements;
PRVM_ERROR ("Bad opcode %i in %s", st->op, PRVM_NAME);
}
}
}