c4d3497054
PF_VarString can now shortcircuit, directly returning the first argument (which also bypasses related length limitations). this required const correctness fixes. r_showshaders cvar reports the wall texture being pointed at. improve dp compat in a number of areas, should now mostly be able to run xonotic 0.8.2, but will need some more extra cvars/defaults/stuff. fix q3bsp loader race condition. fix dpm loader multi-surface issue. remove limit on stringbuffer count. don't poll xinput (because its disturbingly slow), instead wait for some window message before re-checking. fixed splitscreen saved games. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5140 fc73d0e0-1445-4013-8a0c-d673dee63da5
1353 lines
40 KiB
C
1353 lines
40 KiB
C
//qc execution code.
|
|
//we have two conditions.
|
|
//one allows us to debug and trace through our code, the other doesn't.
|
|
|
|
//hopefully, the compiler will do a great job at optimising this code for us, where required.
|
|
//if it dosn't, then bum.
|
|
|
|
//the general overhead should be reduced significantly, and I would be supprised if it did run slower.
|
|
|
|
//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall.
|
|
|
|
//Appears to work fine.
|
|
|
|
#if INTSIZE == 16
|
|
#define reeval reeval16
|
|
#define pr_statements pr_statements16
|
|
#define fakeop fakeop16
|
|
#define dstatement_t dstatement16_t
|
|
#define sofs signed short
|
|
#elif INTSIZE == 32
|
|
#define reeval reeval32
|
|
#define pr_statements pr_statements32
|
|
#define fakeop fakeop32
|
|
#define dstatement_t dstatement32_t
|
|
#define sofs signed int
|
|
#elif INTSIZE == 24
|
|
#error INTSIZE should be set to 32.
|
|
#else
|
|
#error Bad cont size
|
|
#endif
|
|
|
|
#define ENGINEPOINTER(p) ((char*)(p) - progfuncs->funcs.stringtable)
|
|
#define QCPOINTER(p) (eval_t *)(p->_int+progfuncs->funcs.stringtable)
|
|
#define QCPOINTERM(p) (eval_t *)((p)+progfuncs->funcs.stringtable)
|
|
#define QCPOINTERWRITEFAIL(p,sz) ((float)(p)-1 >= prinst.addressableused-1-(sz)) //disallows null writes
|
|
#define QCPOINTERREADFAIL(p,sz) ((unsigned int)(p) >= prinst.addressableused-(sz)) //permits null reads
|
|
|
|
|
|
|
|
#define QCFAULT return (pr_xstatement=(st-pr_statements)-1),PR_HandleFault
|
|
#define EVAL_FLOATISTRUE(ev) ((ev)->_int & 0x7fffffff) //mask away sign bit. This avoids using denormalized floats.
|
|
|
|
#ifdef __GNUC__
|
|
#define errorif(x) if(__builtin_expect(x,0))
|
|
#else
|
|
#define errorif(x) if(x)
|
|
#endif
|
|
|
|
//rely upon just st
|
|
{
|
|
#ifdef DEBUGABLE
|
|
s = st-pr_statements;
|
|
s+=1;
|
|
|
|
errorif (prinst.watch_ptr && prinst.watch_ptr->_int != prinst.watch_old._int)
|
|
{
|
|
//this will fire on the next instruction after the variable got changed.
|
|
pr_xstatement = s;
|
|
if (current_progstate->linenums)
|
|
printf("Watch point hit in %s:%u, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name);
|
|
else
|
|
printf("Watch point hit in %s, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), prinst.watch_name);
|
|
switch(prinst.watch_type)
|
|
{
|
|
case ev_float:
|
|
printf(" from %g to %g", prinst.watch_old._float, prinst.watch_ptr->_float);
|
|
break;
|
|
case ev_vector:
|
|
printf(" from '%g %g %g' to '%g %g %g'", prinst.watch_old._vector[0], prinst.watch_old._vector[1], prinst.watch_old._vector[2], prinst.watch_ptr->_vector[0], prinst.watch_ptr->_vector[1], prinst.watch_ptr->_vector[2]);
|
|
break;
|
|
default:
|
|
printf(" from %i to %i", prinst.watch_old._int, prinst.watch_ptr->_int);
|
|
break;
|
|
case ev_entity:
|
|
printf(" from %i(%s) to %i(%s)", prinst.watch_old._int, PR_GetEdictClassname(progfuncs, prinst.watch_old._int), prinst.watch_ptr->_int, PR_GetEdictClassname(progfuncs, prinst.watch_ptr->_int));
|
|
break;
|
|
case ev_function:
|
|
case ev_string:
|
|
printf(", now set to %s", PR_ValueString(progfuncs, prinst.watch_type, prinst.watch_ptr, false));
|
|
break;
|
|
}
|
|
printf(".\n");
|
|
prinst.watch_old = *prinst.watch_ptr;
|
|
// prinst.watch_ptr = NULL;
|
|
progfuncs->funcs.debug_trace=DEBUG_TRACE_INTO; //this is what it's for
|
|
|
|
s=ShowStep(progfuncs, s, "Watchpoint hit", false);
|
|
}
|
|
else if (progfuncs->funcs.debug_trace)
|
|
s=ShowStep(progfuncs, s, NULL, false);
|
|
st = pr_statements + s;
|
|
pr_xfunction->profile+=1;
|
|
|
|
op = (progfuncs->funcs.debug_trace?(st->op & ~0x8000):st->op);
|
|
reeval:
|
|
#else
|
|
st++;
|
|
op = st->op;
|
|
#endif
|
|
|
|
switch (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:
|
|
tmpf = OPA->_float;
|
|
OPC->_vector[0] = tmpf * OPB->_vector[0];
|
|
OPC->_vector[1] = tmpf * OPB->_vector[1];
|
|
OPC->_vector[2] = tmpf * OPB->_vector[2];
|
|
break;
|
|
case OP_MUL_VF:
|
|
tmpf = OPB->_float;
|
|
OPC->_vector[0] = tmpf * OPA->_vector[0];
|
|
OPC->_vector[1] = tmpf * OPA->_vector[1];
|
|
OPC->_vector[2] = tmpf * OPA->_vector[2];
|
|
break;
|
|
|
|
case OP_DIV_F:
|
|
/* errorif (OPB->_float == 0)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
printf ("Division by 0 in %s\n", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
PR_StackTrace (&progfuncs->funcs, 1);
|
|
OPC->_float = 0.0;
|
|
}
|
|
else
|
|
*/ OPC->_float = OPA->_float / OPB->_float;
|
|
break;
|
|
case OP_DIV_VF:
|
|
tmpf = OPB->_float;
|
|
/* errorif (!tmpf)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
printf ("Division by 0 in %s\n", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
PR_StackTrace (&progfuncs->funcs, 1);
|
|
}
|
|
*/
|
|
OPC->_vector[0] = OPA->_vector[0] / tmpf;
|
|
OPC->_vector[1] = OPA->_vector[1] / tmpf;
|
|
OPC->_vector[2] = OPA->_vector[2] / tmpf;
|
|
break;
|
|
|
|
case OP_BITAND_F:
|
|
OPC->_float = (float)((int)OPA->_float & (int)OPB->_float);
|
|
break;
|
|
|
|
case OP_BITOR_F:
|
|
OPC->_float = (float)((int)OPA->_float | (int)OPB->_float);
|
|
break;
|
|
|
|
|
|
case OP_GE_F:
|
|
OPC->_float = (float)(OPA->_float >= OPB->_float);
|
|
break;
|
|
case OP_GE_I:
|
|
OPC->_int = (int)(OPA->_int >= OPB->_int);
|
|
break;
|
|
case OP_GE_IF:
|
|
OPC->_int = (float)(OPA->_int >= OPB->_float);
|
|
break;
|
|
case OP_GE_FI:
|
|
OPC->_int = (float)(OPA->_float >= OPB->_int);
|
|
break;
|
|
|
|
case OP_LE_F:
|
|
OPC->_float = (float)(OPA->_float <= OPB->_float);
|
|
break;
|
|
case OP_LE_I:
|
|
OPC->_int = (int)(OPA->_int <= OPB->_int);
|
|
break;
|
|
case OP_LE_IF:
|
|
OPC->_int = (float)(OPA->_int <= OPB->_float);
|
|
break;
|
|
case OP_LE_FI:
|
|
OPC->_int = (float)(OPA->_float <= OPB->_int);
|
|
break;
|
|
|
|
case OP_GT_F:
|
|
OPC->_float = (float)(OPA->_float > OPB->_float);
|
|
break;
|
|
case OP_GT_I:
|
|
OPC->_int = (int)(OPA->_int > OPB->_int);
|
|
break;
|
|
case OP_GT_IF:
|
|
OPC->_int = (float)(OPA->_int > OPB->_float);
|
|
break;
|
|
case OP_GT_FI:
|
|
OPC->_int = (float)(OPA->_float > OPB->_int);
|
|
break;
|
|
|
|
case OP_LT_F:
|
|
OPC->_float = (float)(OPA->_float < OPB->_float);
|
|
break;
|
|
case OP_LT_I:
|
|
OPC->_int = (int)(OPA->_int < OPB->_int);
|
|
break;
|
|
case OP_LT_IF:
|
|
OPC->_int = (float)(OPA->_int < OPB->_float);
|
|
break;
|
|
case OP_LT_FI:
|
|
OPC->_int = (float)(OPA->_float < OPB->_int);
|
|
break;
|
|
|
|
case OP_AND_F:
|
|
//original logic
|
|
//OPC->_float = (float)(OPA->_float && OPB->_float);
|
|
//deal with denormalized floats by ensuring that they're not 0 (ignoring sign bit).
|
|
//this avoids issues where the fpu treats denormalised floats as 0, or fpus that don't support denormals.
|
|
OPC->_float = (float)(EVAL_FLOATISTRUE(OPA) && EVAL_FLOATISTRUE(OPB));
|
|
break;
|
|
case OP_OR_F:
|
|
OPC->_float = (float)(EVAL_FLOATISTRUE(OPA) || EVAL_FLOATISTRUE(OPB));
|
|
break;
|
|
|
|
case OP_NOT_F:
|
|
OPC->_float = (float)(!EVAL_FLOATISTRUE(OPA));
|
|
break;
|
|
case OP_NOT_V:
|
|
OPC->_float = (float)(!OPA->_vector[0] && !OPA->_vector[1] && !OPA->_vector[2]);
|
|
break;
|
|
case OP_NOT_S:
|
|
OPC->_float = (float)(!(OPA->string) || !*PR_StringToNative(&progfuncs->funcs, OPA->string));
|
|
break;
|
|
case OP_NOT_FNC:
|
|
OPC->_float = (float)(!(OPA->function & ~0xff000000));
|
|
break;
|
|
case OP_NOT_ENT:
|
|
OPC->_float = (float)(!(OPA->edict));//(PROG_TO_EDICT(progfuncs, OPA->edict) == (edictrun_t *)sv_edicts);
|
|
break;
|
|
|
|
case OP_EQ_F:
|
|
OPC->_float = (float)(OPA->_float == OPB->_float);
|
|
break;
|
|
case OP_EQ_IF:
|
|
OPC->_int = (float)(OPA->_int == OPB->_float);
|
|
break;
|
|
case OP_EQ_FI:
|
|
OPC->_float = (float)(OPA->_float == OPB->_int);
|
|
break;
|
|
|
|
|
|
case OP_EQ_V:
|
|
OPC->_float = (float)((OPA->_vector[0] == OPB->_vector[0]) &&
|
|
(OPA->_vector[1] == OPB->_vector[1]) &&
|
|
(OPA->_vector[2] == OPB->_vector[2]));
|
|
break;
|
|
case OP_EQ_S:
|
|
if (OPA->string==OPB->string)
|
|
OPC->_float = true;
|
|
else if (!OPA->string)
|
|
{
|
|
if (!OPB->string || !*PR_StringToNative(&progfuncs->funcs, OPB->string))
|
|
OPC->_float = true;
|
|
else
|
|
OPC->_float = false;
|
|
}
|
|
else if (!OPB->string)
|
|
{
|
|
if (!OPA->string || !*PR_StringToNative(&progfuncs->funcs, OPA->string))
|
|
OPC->_float = true;
|
|
else
|
|
OPC->_float = false;
|
|
}
|
|
else
|
|
OPC->_float = (float)(!strcmp(PR_StringToNative(&progfuncs->funcs, OPA->string),PR_StringToNative(&progfuncs->funcs, OPB->string)));
|
|
break;
|
|
case OP_EQ_E:
|
|
OPC->_float = (float)(OPA->_int == OPB->_int);
|
|
break;
|
|
case OP_EQ_FNC:
|
|
OPC->_float = (float)(OPA->function == OPB->function);
|
|
break;
|
|
|
|
|
|
case OP_NE_F:
|
|
OPC->_float = (float)(OPA->_float != OPB->_float);
|
|
break;
|
|
case OP_NE_V:
|
|
OPC->_float = (float)((OPA->_vector[0] != OPB->_vector[0]) ||
|
|
(OPA->_vector[1] != OPB->_vector[1]) ||
|
|
(OPA->_vector[2] != OPB->_vector[2]));
|
|
break;
|
|
case OP_NE_S:
|
|
if (OPA->string==OPB->string)
|
|
OPC->_float = false;
|
|
else if (!OPA->string)
|
|
{
|
|
if (!OPB->string || !*(PR_StringToNative(&progfuncs->funcs, OPB->string)))
|
|
OPC->_float = false;
|
|
else
|
|
OPC->_float = true;
|
|
}
|
|
else if (!OPB->string)
|
|
{
|
|
if (!OPA->string || !*PR_StringToNative(&progfuncs->funcs, OPA->string))
|
|
OPC->_float = false;
|
|
else
|
|
OPC->_float = true;
|
|
}
|
|
else
|
|
OPC->_float = (float)(strcmp(PR_StringToNative(&progfuncs->funcs, OPA->string),PR_StringToNative(&progfuncs->funcs, OPB->string)));
|
|
break;
|
|
case OP_NE_E:
|
|
OPC->_float = (float)(OPA->_int != OPB->_int);
|
|
break;
|
|
case OP_NE_FNC:
|
|
OPC->_float = (float)(OPA->function != OPB->function);
|
|
break;
|
|
|
|
//==================
|
|
case OP_STORE_IF:
|
|
OPB->_float = (float)OPA->_int;
|
|
break;
|
|
case OP_STORE_FI:
|
|
OPB->_int = (int)OPA->_float;
|
|
break;
|
|
|
|
case OP_STORE_F:
|
|
case OP_STORE_ENT:
|
|
case OP_STORE_FLD: // integers
|
|
case OP_STORE_S:
|
|
case OP_STORE_I:
|
|
case OP_STORE_FNC: // pointers
|
|
case OP_STORE_P:
|
|
OPB->_int = OPA->_int;
|
|
break;
|
|
case OP_STORE_V:
|
|
OPB->_vector[0] = OPA->_vector[0];
|
|
OPB->_vector[1] = OPA->_vector[1];
|
|
OPB->_vector[2] = OPA->_vector[2];
|
|
break;
|
|
|
|
//store a value to a pointer
|
|
case OP_STOREP_IF:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
if (i == -1)
|
|
break;
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_float = (float)OPA->_int;
|
|
break;
|
|
case OP_STOREP_FI:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(int)))
|
|
{
|
|
if (i == -1)
|
|
break;
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_int = (int)OPA->_float;
|
|
break;
|
|
case OP_STOREP_I:
|
|
case OP_STOREP_F:
|
|
case OP_STOREP_ENT:
|
|
case OP_STOREP_FLD: // integers
|
|
case OP_STOREP_S:
|
|
case OP_STOREP_FNC: // pointers
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(int)))
|
|
{
|
|
if (i == -1)
|
|
break;
|
|
if (i == 0)
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s (null pointer)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
else
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_int = OPA->_int;
|
|
break;
|
|
case OP_STOREP_V:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(vec3_t)))
|
|
{
|
|
if (i == -1)
|
|
break;
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_vector[0] = OPA->_vector[0];
|
|
ptr->_vector[1] = OPA->_vector[1];
|
|
ptr->_vector[2] = OPA->_vector[2];
|
|
break;
|
|
|
|
case OP_STOREP_C: //store character in a string
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(char)))
|
|
{
|
|
if (i == -1)
|
|
break;
|
|
QCFAULT(&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
*(unsigned char *)ptr = (char)OPA->_float;
|
|
break;
|
|
|
|
//get a pointer to a field var
|
|
case OP_ADDRESS:
|
|
errorif ((unsigned)OPA->edict >= (unsigned)num_edicts)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_ADDRESS references invalid entity in %s\n", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
break;
|
|
}
|
|
ed = PROG_TO_EDICT(progfuncs, OPA->edict);
|
|
#ifdef PARANOID
|
|
NUM_FOR_EDICT(ed); // make sure it's in range
|
|
#endif
|
|
errorif (!ed || ed->readonly)
|
|
{
|
|
|
|
//boot it over to the debugger
|
|
{
|
|
#if INTSIZE == 16
|
|
ddef16_t *d = ED_GlobalAtOfs16(progfuncs, st->a);
|
|
#else
|
|
ddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a);
|
|
#endif
|
|
fdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust);
|
|
if (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, "assignment to read-only entity %i in %s (%s.%s)\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):NULL, f?f->name:NULL))
|
|
return pr_xstatement;
|
|
OPC->_int = ~0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods.
|
|
#ifdef NOLEGACY
|
|
errorif (ed->ereftype == ER_FREE)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "assignment to free entity in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
i = OPB->_int + progfuncs->funcs.fieldadjust;
|
|
#ifdef PARANOID
|
|
errorif ((unsigned int)i*4 >= ed->fieldsize) //FIXME:lazy size check
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_ADDRESS references invalid field %i in %s\n", OPB->_int, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
OPC->_int = ENGINEPOINTER((((int *)edvars(ed)) + i));
|
|
break;
|
|
|
|
//load a field to a value
|
|
case OP_LOAD_P:
|
|
case OP_LOAD_I:
|
|
case OP_LOAD_F:
|
|
case OP_LOAD_FLD:
|
|
case OP_LOAD_ENT:
|
|
case OP_LOAD_S:
|
|
case OP_LOAD_FNC:
|
|
errorif ((unsigned)OPA->edict >= (unsigned)num_edicts)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD references invalid entity %i in %s\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
ed = PROG_TO_EDICT(progfuncs, OPA->edict);
|
|
#ifdef PARANOID
|
|
NUM_FOR_EDICT(ed); // make sure it's in range
|
|
#endif
|
|
#ifdef NOLEGACY
|
|
if (ed->ereftype == ER_FREE)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD references free entity %i in %s\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_int = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
i = OPB->_int + progfuncs->funcs.fieldadjust;
|
|
errorif ((unsigned int)i*4 >= ed->fieldsize) //FIXME:lazy size check
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD references invalid field %i in %s\n", OPB->_int, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
ptr = (eval_t *)(((int *)edvars(ed)) + i);
|
|
OPC->_int = ptr->_int;
|
|
}
|
|
break;
|
|
|
|
case OP_LOAD_V:
|
|
errorif ((unsigned)OPA->edict >= (unsigned)num_edicts)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD_V references invalid entity %i in %s\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_vector[0] = 0;
|
|
OPC->_vector[1] = 0;
|
|
OPC->_vector[2] = 0;
|
|
break;
|
|
}
|
|
ed = PROG_TO_EDICT(progfuncs, OPA->edict);
|
|
#ifdef PARANOID
|
|
NUM_FOR_EDICT(ed); // make sure it's in range
|
|
#endif
|
|
#ifdef NOLEGACY
|
|
if (ed->ereftype == ER_FREE)
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD references free entity %i in %s\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_vector[0] = 0;
|
|
OPC->_vector[1] = 0;
|
|
OPC->_vector[2] = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
i = OPB->_int + progfuncs->funcs.fieldadjust;
|
|
errorif ((unsigned int)i*4 >= ed->fieldsize) //FIXME:lazy size check
|
|
{
|
|
if (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, "OP_LOAD references invalid field %i in %s\n", OPB->_int, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)))
|
|
return pr_xstatement;
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
ptr = (eval_t *)(((int *)edvars(ed)) + i);
|
|
OPC->_vector[0] = ptr->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2];
|
|
}
|
|
break;
|
|
|
|
//==================
|
|
|
|
case OP_IFNOT_S:
|
|
RUNAWAYCHECK();
|
|
if (!OPA->string || !PR_StringToNative(&progfuncs->funcs, OPA->string))
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
case OP_IFNOT_F:
|
|
RUNAWAYCHECK();
|
|
if (!EVAL_FLOATISTRUE(OPA))
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
//WARNING: vanilla uses this for floats too, which results in a discrepancy with -0
|
|
case OP_IFNOT_I:
|
|
RUNAWAYCHECK();
|
|
if (!OPA->_int)
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
case OP_IF_S:
|
|
RUNAWAYCHECK();
|
|
if (OPA->string && PR_StringToNative(&progfuncs->funcs, OPA->string))
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
case OP_IF_F:
|
|
RUNAWAYCHECK();
|
|
if (EVAL_FLOATISTRUE(OPA))
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
//WARNING: vanilla uses this for floats too, which results in a discrepancy with -0
|
|
case OP_IF_I:
|
|
RUNAWAYCHECK();
|
|
if (OPA->_int)
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
|
|
case OP_GOTO:
|
|
RUNAWAYCHECK();
|
|
st += (sofs)st->a - 1; // offset the s++
|
|
break;
|
|
|
|
case OP_CALL8H:
|
|
case OP_CALL7H:
|
|
case OP_CALL6H:
|
|
case OP_CALL5H:
|
|
case OP_CALL4H:
|
|
case OP_CALL3H:
|
|
case OP_CALL2H:
|
|
G_VECTOR(OFS_PARM1)[0] = OPC->_vector[0];
|
|
G_VECTOR(OFS_PARM1)[1] = OPC->_vector[1];
|
|
G_VECTOR(OFS_PARM1)[2] = OPC->_vector[2];
|
|
case OP_CALL1H:
|
|
G_VECTOR(OFS_PARM0)[0] = OPB->_vector[0];
|
|
G_VECTOR(OFS_PARM0)[1] = OPB->_vector[1];
|
|
G_VECTOR(OFS_PARM0)[2] = OPB->_vector[2];
|
|
|
|
case OP_CALL8:
|
|
case OP_CALL7:
|
|
case OP_CALL6:
|
|
case OP_CALL5:
|
|
case OP_CALL4:
|
|
case OP_CALL3:
|
|
case OP_CALL2:
|
|
case OP_CALL1:
|
|
case OP_CALL0:
|
|
{
|
|
int callerprogs;
|
|
int newpr;
|
|
unsigned int fnum;
|
|
RUNAWAYCHECK();
|
|
pr_xstatement = st-pr_statements;
|
|
|
|
if (op > OP_CALL8)
|
|
progfuncs->funcs.callargc = op - (OP_CALL1H-1);
|
|
else
|
|
progfuncs->funcs.callargc = op - OP_CALL0;
|
|
fnum = OPA->function;
|
|
|
|
glob = NULL; //try to derestrict it.
|
|
|
|
callerprogs=prinst.pr_typecurrent; //so we can revert to the right caller.
|
|
newpr = (fnum & 0xff000000)>>24; //this is the progs index of the callee
|
|
fnum &= ~0xff000000; //the callee's function index.
|
|
|
|
//if it's an external call, switch now (before any function pointers are used)
|
|
errorif (!PR_SwitchProgsParms(progfuncs, newpr) || !fnum || fnum > pr_progs->numfunctions)
|
|
{
|
|
char *msg = fnum?"OP_CALL references invalid function in %s\n":"NULL function from qc (inside %s).\n";
|
|
PR_SwitchProgsParms(progfuncs, callerprogs);
|
|
|
|
glob = pr_globals;
|
|
if (!progfuncs->funcs.debug_trace)
|
|
QCFAULT(&progfuncs->funcs, msg, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
|
|
//skip the instruction if they just try stepping over it anyway.
|
|
PR_StackTrace(&progfuncs->funcs, 0);
|
|
printf(msg, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
|
|
pr_globals[OFS_RETURN] = 0;
|
|
pr_globals[OFS_RETURN+1] = 0;
|
|
pr_globals[OFS_RETURN+2] = 0;
|
|
break;
|
|
}
|
|
|
|
newf = &pr_cp_functions[fnum & ~0xff000000];
|
|
|
|
if (newf->first_statement <= 0)
|
|
{ // negative statements are built in functions
|
|
/*calling a builtin in another progs may affect that other progs' globals instead, is the theory anyway, so args and stuff need to move over*/
|
|
if (prinst.pr_typecurrent != 0)
|
|
{
|
|
//builtins quite hackily refer to only a single global.
|
|
//for builtins to affect the globals of other progs, we need to first switch to the progs that it will affect, so they'll be correct when we switch back
|
|
PR_SwitchProgsParms(progfuncs, 0);
|
|
}
|
|
i = -newf->first_statement;
|
|
// p = pr_typecurrent;
|
|
if (i < externs->numglobalbuiltins)
|
|
{
|
|
#ifndef QCGC
|
|
prinst.numtempstringsstack = prinst.numtempstrings;
|
|
#endif
|
|
(*externs->globalbuiltins[i]) (&progfuncs->funcs, (struct globalvars_s *)current_progstate->globals);
|
|
|
|
//in case ed_alloc was called
|
|
num_edicts = sv_num_edicts;
|
|
|
|
if (prinst.continuestatement!=-1)
|
|
{
|
|
st=&pr_statements[prinst.continuestatement];
|
|
prinst.continuestatement=-1;
|
|
glob = pr_globals;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if (newf->first_statement == -0x7fffffff)
|
|
// ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals);
|
|
// else
|
|
PR_RunError (&progfuncs->funcs, "Bad builtin call number - %i", -newf->first_statement);
|
|
}
|
|
// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t));
|
|
PR_SwitchProgsParms(progfuncs, (progsnum_t)callerprogs);
|
|
|
|
//decide weather non debugger wants to start debugging.
|
|
s = st-pr_statements;
|
|
return s;
|
|
}
|
|
// PR_SwitchProgsParms((OPA->function & 0xff000000)>>24);
|
|
s = PR_EnterFunction (progfuncs, newf, callerprogs);
|
|
st = &pr_statements[s];
|
|
}
|
|
|
|
//resume at the new statement, which might be in a different progs
|
|
return s;
|
|
// break;
|
|
|
|
case OP_DONE:
|
|
case OP_RETURN:
|
|
|
|
RUNAWAYCHECK();
|
|
|
|
glob[OFS_RETURN] = glob[st->a];
|
|
glob[OFS_RETURN+1] = glob[st->a+1];
|
|
glob[OFS_RETURN+2] = glob[st->a+2];
|
|
/*
|
|
{
|
|
static char buffer[1024*1024*8];
|
|
int size = sizeof buffer;
|
|
progfuncs->save_ents(progfuncs, buffer, &size, 0);
|
|
}
|
|
*/
|
|
s = PR_LeaveFunction (progfuncs);
|
|
st = &pr_statements[s];
|
|
if (pr_depth == prinst.exitdepth)
|
|
{
|
|
return -1; // all done
|
|
}
|
|
return s;
|
|
// break;
|
|
|
|
case OP_STATE:
|
|
externs->stateop(&progfuncs->funcs, OPA->_float, OPB->function);
|
|
break;
|
|
|
|
case OP_ADD_I:
|
|
OPC->_int = OPA->_int + OPB->_int;
|
|
break;
|
|
case OP_ADD_FI:
|
|
OPC->_float = OPA->_float + (float)OPB->_int;
|
|
break;
|
|
case OP_ADD_IF:
|
|
OPC->_float = (float)OPA->_int + OPB->_float;
|
|
break;
|
|
|
|
case OP_SUB_I:
|
|
OPC->_int = OPA->_int - OPB->_int;
|
|
break;
|
|
case OP_SUB_FI:
|
|
OPC->_float = OPA->_float - (float)OPB->_int;
|
|
break;
|
|
case OP_SUB_IF:
|
|
OPC->_float = (float)OPA->_int - OPB->_float;
|
|
break;
|
|
|
|
case OP_CONV_ITOF:
|
|
OPC->_float = (float)OPA->_int;
|
|
break;
|
|
case OP_CONV_FTOI:
|
|
OPC->_int = (int)OPA->_float;
|
|
break;
|
|
|
|
case OP_CP_ITOF:
|
|
ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int);
|
|
OPC->_float = (float)ptr->_int;
|
|
break;
|
|
|
|
case OP_CP_FTOI:
|
|
ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int);
|
|
OPC->_int = (int)ptr->_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_MUL_I:
|
|
OPC->_int = OPA->_int * OPB->_int;
|
|
break;
|
|
case OP_DIV_I:
|
|
if (OPB->_int == 0) //no division by zero allowed...
|
|
OPC->_int = 0;
|
|
else
|
|
OPC->_int = OPA->_int / OPB->_int;
|
|
break;
|
|
case OP_EQ_I:
|
|
OPC->_int = (OPA->_int == OPB->_int);
|
|
break;
|
|
case OP_NE_I:
|
|
OPC->_int = (OPA->_int != OPB->_int);
|
|
break;
|
|
|
|
|
|
//array/structure reading/writing.
|
|
case OP_GLOBALADDRESS:
|
|
OPC->_int = ENGINEPOINTER(&OPA->_int + OPB->_int); /*pointer arithmatic*/
|
|
break;
|
|
case OP_ADD_PIW: //pointer to 32 bit (remember to *3 for vectors)
|
|
OPC->_int = OPA->_int + OPB->_int*sizeof(float);
|
|
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:
|
|
ptr = (eval_t *)(&OPA->_int + OPB->_int); /*pointer arithmatic*/
|
|
OPC->_int = ptr->_int;
|
|
break;
|
|
|
|
case OP_LOADA_V:
|
|
ptr = (eval_t *)(&OPA->_int + OPB->_int);
|
|
OPC->_vector[0] = ptr->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2];
|
|
break;
|
|
|
|
|
|
|
|
case OP_ADD_SF: //(char*)c = (char*)a + (float)b
|
|
OPC->_int = OPA->_int + (int)OPB->_float;
|
|
break;
|
|
case OP_SUB_S: //(float)c = (char*)a - (char*)b
|
|
OPC->_int = OPA->_int - OPB->_int;
|
|
break;
|
|
case OP_LOADP_C: //load character from a string/pointer
|
|
i = (unsigned int)OPA->_int + (unsigned int)OPB->_float;
|
|
errorif (QCPOINTERREADFAIL(i, sizeof(char)))
|
|
{
|
|
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(int))))
|
|
{
|
|
if (i == -1)
|
|
{
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%i bytes into %s)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, ptr);
|
|
}
|
|
}
|
|
else
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_float = *(unsigned char *)ptr;
|
|
break;
|
|
case OP_LOADP_I:
|
|
case OP_LOADP_F:
|
|
case OP_LOADP_FLD:
|
|
case OP_LOADP_ENT:
|
|
case OP_LOADP_S:
|
|
case OP_LOADP_FNC:
|
|
i = OPA->_int + OPB->_int*4;
|
|
errorif (QCPOINTERREADFAIL(i, sizeof(int)))
|
|
{
|
|
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(int))))
|
|
{
|
|
if (i == -1)
|
|
{
|
|
OPC->_int = 0;
|
|
break;
|
|
}
|
|
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (from %#x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i);
|
|
}
|
|
}
|
|
else
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_int = ptr->_int;
|
|
break;
|
|
|
|
case OP_LOADP_V:
|
|
i = OPA->_int + OPB->_int*4; //NOTE: inconsistant, but a bit more practical for the qcc when structs etc are involved
|
|
errorif (QCPOINTERREADFAIL(i, sizeof(vec3_t)))
|
|
{
|
|
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(vec3_t))))
|
|
{
|
|
if (i == -1)
|
|
{
|
|
OPC->_vector[0] = 0;
|
|
OPC->_vector[1] = 0;
|
|
OPC->_vector[2] = 0;
|
|
break;
|
|
}
|
|
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (from %#x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i);
|
|
}
|
|
}
|
|
else
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_vector[0] = ptr->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2];
|
|
break;
|
|
|
|
case OP_BITXOR_I:
|
|
OPC->_int = OPA->_int ^ OPB->_int;
|
|
break;
|
|
case OP_RSHIFT_I:
|
|
OPC->_int = OPA->_int >> OPB->_int;
|
|
break;
|
|
case OP_LSHIFT_I:
|
|
OPC->_int = OPA->_int << OPB->_int;
|
|
break;
|
|
|
|
//hexen2 arrays contain a prefix global set to (arraysize-1) inserted before the actual array data
|
|
//for vectors, this prefix is the number of vectors rather than the number of globals. this can cause issues with using OP_FETCH_GBL_V within structs.
|
|
case OP_FETCH_GBL_F:
|
|
case OP_FETCH_GBL_S:
|
|
case OP_FETCH_GBL_E:
|
|
case OP_FETCH_GBL_FNC:
|
|
i = OPB->_float;
|
|
errorif((unsigned)i > (unsigned)((eval_t *)&glob[st->a-1])->_int)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError(&progfuncs->funcs, "array index out of bounds: %s[%d] (max %d)", PR_GlobalStringNoContents(progfuncs, st->a), i, ((eval_t *)&glob[st->a-1])->_int);
|
|
}
|
|
OPC->_int = ((eval_t *)&glob[st->a + i])->_int;
|
|
break;
|
|
case OP_FETCH_GBL_V:
|
|
i = OPB->_float;
|
|
errorif((unsigned)i > (unsigned)((eval_t *)&glob[st->a-1])->_int)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError(&progfuncs->funcs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i);
|
|
}
|
|
ptr = (eval_t *)&glob[st->a + i*3];
|
|
OPC->_vector[0] = ptr->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2];
|
|
break;
|
|
|
|
case OP_CSTATE:
|
|
externs->cstateop(&progfuncs->funcs, OPA->_float, OPB->_float, pr_xfunction - pr_cp_functions);
|
|
break;
|
|
|
|
case OP_CWSTATE:
|
|
externs->cwstateop(&progfuncs->funcs, OPA->_float, OPB->_float, pr_xfunction - pr_cp_functions);
|
|
break;
|
|
|
|
case OP_THINKTIME:
|
|
externs->thinktimeop(&progfuncs->funcs, (struct edict_s *)PROG_TO_EDICT(progfuncs, OPA->edict), OPB->_float);
|
|
break;
|
|
|
|
case OP_MULSTORE_F:
|
|
/*OPC->_float = */OPB->_float *= OPA->_float;
|
|
break;
|
|
case OP_MULSTORE_VF:
|
|
tmpf = OPA->_float; //don't break on vec*=vec_x;
|
|
/*OPC->_vector[0] = */OPB->_vector[0] *= tmpf;
|
|
/*OPC->_vector[1] = */OPB->_vector[1] *= tmpf;
|
|
/*OPC->_vector[2] = */OPB->_vector[2] *= tmpf;
|
|
break;
|
|
case OP_MULSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_float = ptr->_float *= OPA->_float;
|
|
break;
|
|
case OP_MULSTOREP_VF:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
tmpf = OPA->_float; //don't break on vec*=vec_x;
|
|
OPC->_vector[0] = ptr->_vector[0] *= tmpf;
|
|
OPC->_vector[1] = ptr->_vector[1] *= tmpf;
|
|
OPC->_vector[2] = ptr->_vector[2] *= tmpf;
|
|
break;
|
|
case OP_DIVSTORE_F:
|
|
/*OPC->_float = */OPB->_float /= OPA->_float;
|
|
break;
|
|
case OP_DIVSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_float = ptr->_float /= OPA->_float;
|
|
break;
|
|
case OP_ADDSTORE_F:
|
|
/*OPC->_float = */OPB->_float += OPA->_float;
|
|
break;
|
|
case OP_ADDSTORE_V:
|
|
/*OPC->_vector[0] =*/ OPB->_vector[0] += OPA->_vector[0];
|
|
/*OPC->_vector[1] =*/ OPB->_vector[1] += OPA->_vector[1];
|
|
/*OPC->_vector[2] =*/ OPB->_vector[2] += OPA->_vector[2];
|
|
break;
|
|
case OP_ADDSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_float = ptr->_float += OPA->_float;
|
|
break;
|
|
case OP_ADDSTOREP_V:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_vector[0] = ptr->_vector[0] += OPA->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1] += OPA->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2] += OPA->_vector[2];
|
|
break;
|
|
case OP_SUBSTORE_F:
|
|
/*OPC->_float = */OPB->_float -= OPA->_float;
|
|
break;
|
|
case OP_SUBSTORE_V:
|
|
/*OPC->_vector[0] = */OPB->_vector[0] -= OPA->_vector[0];
|
|
/*OPC->_vector[1] = */OPB->_vector[1] -= OPA->_vector[1];
|
|
/*OPC->_vector[2] = */OPB->_vector[2] -= OPA->_vector[2];
|
|
break;
|
|
case OP_SUBSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_float = ptr->_float -= OPA->_float;
|
|
break;
|
|
case OP_SUBSTOREP_V:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
OPC->_vector[0] = ptr->_vector[0] -= OPA->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1] -= OPA->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2] -= OPA->_vector[2];
|
|
break;
|
|
case OP_BITSETSTORE_F:
|
|
OPB->_float = (int)OPB->_float | (int)OPA->_float;
|
|
break;
|
|
case OP_BITSETSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_float = (int)ptr->_float | (int)OPA->_float;
|
|
break;
|
|
case OP_BITCLRSTORE_F:
|
|
OPB->_float = (int)OPB->_float & ~(int)OPA->_float;
|
|
break;
|
|
case OP_BITCLRSTOREP_F:
|
|
i = OPB->_int;
|
|
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
|
|
}
|
|
ptr = QCPOINTERM(i);
|
|
ptr->_float = (int)ptr->_float & ~(int)OPA->_float;
|
|
break;
|
|
|
|
//for scaler randoms, prevent the random value from ever reaching 1
|
|
//this avoids issues when array[random()*array.length]
|
|
case OP_RAND0:
|
|
OPC->_float = (rand ()&0x7fff) / ((float)0x8000);
|
|
break;
|
|
case OP_RAND1:
|
|
OPC->_float = (rand ()&0x7fff) / ((float)0x8000)*OPA->_float;
|
|
break;
|
|
case OP_RAND2: //backwards range shouldn't matter (except that it is b that is never reached, rather than the higher of the two)
|
|
OPC->_float = OPA->_float + (rand ()&0x7fff) / ((float)0x8000)*(OPB->_float-OPA->_float);
|
|
break;
|
|
//random vectors DO result in 0 to 1 inclusive, to try to ensure a more balanced range
|
|
case OP_RANDV0:
|
|
OPC->_vector[0] = (rand ()&0x7fff) / ((float)0x7fff);
|
|
OPC->_vector[1] = (rand ()&0x7fff) / ((float)0x7fff);
|
|
OPC->_vector[2] = (rand ()&0x7fff) / ((float)0x7fff);
|
|
break;
|
|
case OP_RANDV1:
|
|
OPC->_vector[0] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[0];
|
|
OPC->_vector[1] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[1];
|
|
OPC->_vector[2] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[2];
|
|
break;
|
|
case OP_RANDV2: //backwards range shouldn't matter
|
|
OPC->_vector[0] = OPA->_vector[0] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[0]-OPA->_vector[0]);
|
|
OPC->_vector[1] = OPA->_vector[1] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[1]-OPA->_vector[1]);
|
|
OPC->_vector[2] = OPA->_vector[2] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[2]-OPA->_vector[2]);
|
|
break;
|
|
|
|
case OP_SWITCH_F:
|
|
case OP_SWITCH_V:
|
|
case OP_SWITCH_S:
|
|
case OP_SWITCH_E:
|
|
case OP_SWITCH_FNC:
|
|
//the case opcodes depend upon the preceding switch.
|
|
//otherwise the switch itself is much like a goto
|
|
//don't embed the case/caserange checks directly into the switch so that custom caseranges can be potentially be implemented with hybrid emulation.
|
|
switchcomparison = op - OP_SWITCH_F;
|
|
switchref = OPA;
|
|
RUNAWAYCHECK();
|
|
st += (sofs)st->b - 1; // offset the s++
|
|
break;
|
|
case OP_CASE:
|
|
//if the comparison is true, jump (back up) to the relevent code block
|
|
if (casecmp[switchcomparison](progfuncs, switchref, OPA))
|
|
{
|
|
RUNAWAYCHECK();
|
|
st += (sofs)st->b-1; // -1 to offset the s++
|
|
}
|
|
break;
|
|
case OP_CASERANGE:
|
|
//if the comparison is true, jump (back up) to the relevent code block
|
|
if (casecmprange[switchcomparison](progfuncs, switchref, OPA, OPB))
|
|
{
|
|
RUNAWAYCHECK();
|
|
st += (sofs)st->c-1; // -1 to offset the s++
|
|
}
|
|
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->_int = ((int)OPA->_float & OPB->_int);
|
|
break;
|
|
case OP_BITOR_FI:
|
|
OPC->_int = ((int)OPA->_float | OPB->_int);
|
|
break;
|
|
|
|
case OP_MUL_IF:
|
|
OPC->_float = (OPA->_int * OPB->_float);
|
|
break;
|
|
case OP_MUL_FI:
|
|
OPC->_float = (OPA->_float * OPB->_int);
|
|
break;
|
|
|
|
case OP_MUL_VI:
|
|
tmpi = OPB->_int;
|
|
OPC->_vector[0] = OPA->_vector[0] * tmpi;
|
|
OPC->_vector[1] = OPA->_vector[1] * tmpi;
|
|
OPC->_vector[2] = OPA->_vector[2] * tmpi;
|
|
break;
|
|
case OP_MUL_IV:
|
|
tmpi = OPA->_int;
|
|
OPC->_vector[0] = tmpi * OPB->_vector[0];
|
|
OPC->_vector[1] = tmpi * OPB->_vector[1];
|
|
OPC->_vector[2] = tmpi * OPB->_vector[2];
|
|
break;
|
|
|
|
case OP_DIV_IF:
|
|
OPC->_float = (OPA->_int / OPB->_float);
|
|
break;
|
|
case OP_DIV_FI:
|
|
OPC->_float = (OPA->_float / OPB->_int);
|
|
break;
|
|
|
|
case OP_AND_I:
|
|
OPC->_int = (OPA->_int && OPB->_int);
|
|
break;
|
|
case OP_OR_I:
|
|
OPC->_int = (OPA->_int || OPB->_int);
|
|
break;
|
|
|
|
case OP_AND_IF:
|
|
OPC->_int = (OPA->_int && OPB->_float);
|
|
break;
|
|
case OP_OR_IF:
|
|
OPC->_int = (OPA->_int || OPB->_float);
|
|
break;
|
|
|
|
case OP_AND_FI:
|
|
OPC->_int = (OPA->_float && OPB->_int);
|
|
break;
|
|
case OP_OR_FI:
|
|
OPC->_int = (OPA->_float || OPB->_int);
|
|
break;
|
|
|
|
case OP_NOT_I:
|
|
OPC->_int = !OPA->_int;
|
|
break;
|
|
|
|
case OP_NE_IF:
|
|
OPC->_int = (OPA->_int != OPB->_float);
|
|
break;
|
|
case OP_NE_FI:
|
|
OPC->_int = (OPA->_float != OPB->_int);
|
|
break;
|
|
|
|
case OP_GADDRESS: //return glob[aint+bfloat]
|
|
//this instruction is not implemented due to the weirdness of it.
|
|
//its theoretically a more powerful load... but untyped?
|
|
//or is it meant to be an LEA instruction (that could simply be switched with OP_GLOAD_I)
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "OP_GADDRESS not implemented (found in %s)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
break;
|
|
case OP_GLOAD_I:
|
|
case OP_GLOAD_F:
|
|
case OP_GLOAD_FLD:
|
|
case OP_GLOAD_ENT:
|
|
case OP_GLOAD_S:
|
|
case OP_GLOAD_FNC:
|
|
errorif (OPA->_int < 0 || OPA->_int*4 >= current_progstate->globals_size)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad indexed global read in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPA->_int, current_progstate->globals_size);
|
|
}
|
|
ptr = ((eval_t *)&glob[OPA->_int]);
|
|
OPC->_int = ptr->_int;
|
|
break;
|
|
case OP_GLOAD_V:
|
|
errorif (OPA->_int < 0 || (OPA->_int+2)*4 >= current_progstate->globals_size)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad indexed global read in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPA->_int, current_progstate->globals_size);
|
|
}
|
|
ptr = ((eval_t *)&glob[OPA->_int]);
|
|
OPC->_vector[0] = ptr->_vector[0];
|
|
OPC->_vector[1] = ptr->_vector[1];
|
|
OPC->_vector[2] = ptr->_vector[2];
|
|
break;
|
|
case OP_GSTOREP_I:
|
|
case OP_GSTOREP_F:
|
|
case OP_GSTOREP_ENT:
|
|
case OP_GSTOREP_FLD:
|
|
case OP_GSTOREP_S:
|
|
case OP_GSTOREP_FNC:
|
|
errorif (OPB->_int < 0 || OPB->_int*4 >= current_progstate->globals_size)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused);
|
|
}
|
|
ptr = ((eval_t *)&glob[OPB->_int]);
|
|
ptr->_int = OPA->_int;
|
|
break;
|
|
case OP_GSTOREP_V:
|
|
errorif (OPB->_int < 0 || (OPB->_int+2)*4 >= current_progstate->globals_size)
|
|
{
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused);
|
|
}
|
|
ptr = ((eval_t *)&glob[OPB->_int]);
|
|
ptr->_vector[0] = OPA->_vector[0];
|
|
ptr->_vector[1] = OPA->_vector[1];
|
|
ptr->_vector[2] = OPA->_vector[2];
|
|
break;
|
|
|
|
case OP_BOUNDCHECK:
|
|
errorif ((unsigned int)OPA->_int < (unsigned int)st->c || (unsigned int)OPA->_int >= (unsigned int)st->b)
|
|
{
|
|
printf("Progs boundcheck failed. Value is %i. Must be %u<=value<%u\n", OPA->_int, st->c, st->b);
|
|
QCFAULT(&progfuncs->funcs, "Progs boundcheck failed. Value is %i. Must be %u<=value<%u\n", OPA->_int, st->c, st->b);
|
|
/* s=ShowStepf(progfuncs, st - pr_statements, "Progs boundcheck failed. Value is %i. Must be between %u and %u\n", OPA->_int, st->c, st->b);
|
|
if (st == pr_statements + s)
|
|
PR_RunError(&progfuncs->funcs, "unable to resume boundcheck");
|
|
st = pr_statements + s;
|
|
return s;
|
|
*/ }
|
|
break;
|
|
case OP_PUSH:
|
|
OPC->_int = ENGINEPOINTER(&prinst.localstack[prinst.localstack_used+prinst.spushed]);
|
|
prinst.spushed += OPA->_int;
|
|
if (prinst.spushed + prinst.localstack_used >= LOCALSTACK_SIZE)
|
|
{
|
|
prinst.spushed = 0;
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError(&progfuncs->funcs, "Progs pushed too much");
|
|
}
|
|
break;
|
|
/* case OP_POP:
|
|
pr_spushed -= OPA->_int;
|
|
if (pr_spushed < 0)
|
|
{
|
|
pr_spushed = 0;
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError(progfuncs, "Progs poped more than it pushed");
|
|
}
|
|
break;
|
|
*/
|
|
default:
|
|
if (op & 0x8000) //break point!
|
|
{
|
|
op &= ~0x8000;
|
|
s = st-pr_statements;
|
|
if (pr_xstatement != s)
|
|
{
|
|
pr_xstatement = s;
|
|
printf("Break point hit in %s.\n", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));
|
|
s = ShowStep(progfuncs, s, NULL, false);
|
|
st = &pr_statements[s]; //let the user move execution
|
|
pr_xstatement = s = st-pr_statements;
|
|
op = st->op & ~0x8000;
|
|
}
|
|
goto reeval; //reexecute
|
|
}
|
|
pr_xstatement = st-pr_statements;
|
|
PR_RunError (&progfuncs->funcs, "Bad opcode %i", st->op);
|
|
}
|
|
}
|
|
|
|
|
|
#undef reeval
|
|
#undef st
|
|
#undef pr_statements
|
|
#undef fakeop
|
|
#undef dstatement_t
|
|
#undef sofs
|
|
#undef OPCODE
|
|
|
|
#undef ENGINEPOINTER
|
|
#undef QCPOINTER
|
|
#undef QCPOINTERM
|
|
|