Aligned gamevar/gamearray allocation

git-svn-id: https://svn.eduke32.com/eduke32@4698 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
terminx 2014-10-29 17:05:29 +00:00
parent 384aec522a
commit 6f5726e015
2 changed files with 96 additions and 100 deletions

View file

@ -30,6 +30,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define _gamevars_c_ #define _gamevars_c_
#define PLAYER_VAR_ALIGNMENT (sizeof(intptr_t))
#define ACTOR_VAR_ALIGNMENT 16
#ifdef LUNATIC #ifdef LUNATIC
int32_t g_noResetVars; int32_t g_noResetVars;
LUNATIC_CB void (*A_ResetVars)(int32_t iActor); LUNATIC_CB void (*A_ResetVars)(int32_t iActor);
@ -41,24 +44,18 @@ static void Gv_Free(void) /* called from Gv_ReadSave() and Gv_ResetVars() */
// call this function as many times as needed. // call this function as many times as needed.
int32_t i; int32_t i;
for (i=MAXGAMEVARS-1; i>=0; i--) for (i=0; i<g_gameVarCount; i++)
{ {
if ((aGameVars[i].dwFlags & GAMEVAR_USER_MASK) && aGameVars[i].val.plValues) if (aGameVars[i].dwFlags & GAMEVAR_USER_MASK)
{ ALIGNED_FREE_AND_NULL(aGameVars[i].val.plValues);
Bfree(aGameVars[i].val.plValues);
aGameVars[i].val.plValues=NULL;
}
aGameVars[i].dwFlags |= GAMEVAR_RESET; aGameVars[i].dwFlags |= GAMEVAR_RESET;
if (i >= MAXGAMEARRAYS) if (i >= MAXGAMEARRAYS)
continue; continue;
if ((aGameArrays[i].dwFlags & GAMEARRAY_NORMAL) && aGameArrays[i].plValues) if (aGameArrays[i].dwFlags & GAMEARRAY_NORMAL)
{ ALIGNED_FREE_AND_NULL(aGameArrays[i].plValues);
Bfree(aGameArrays[i].plValues);
aGameArrays[i].plValues=NULL;
}
aGameArrays[i].dwFlags |= GAMEARRAY_RESET; aGameArrays[i].dwFlags |= GAMEARRAY_RESET;
} }
@ -76,18 +73,16 @@ static void Gv_Clear(void)
Gv_Free(); Gv_Free();
// Now, only do work that Gv_Free() hasn't done. // Now, only do work that Gv_Free() hasn't done.
for (i=MAXGAMEVARS-1; i>=0; i--) for (i=0; i<g_gameVarCount; i++)
{ {
Bfree(aGameVars[i].szLabel); DO_FREE_AND_NULL(aGameVars[i].szLabel);
aGameVars[i].szLabel=NULL;
aGameVars[i].val.lValue=0; aGameVars[i].val.lValue=0;
if (i >= MAXGAMEARRAYS) if (i >= MAXGAMEARRAYS)
continue; continue;
Bfree(aGameArrays[i].szLabel); DO_FREE_AND_NULL(aGameArrays[i].szLabel);
aGameArrays[i].szLabel=NULL;
} }
} }
@ -116,18 +111,18 @@ int32_t Gv_ReadSave(int32_t fil, int32_t newbehav)
for (i=0; i<g_gameVarCount; i++) for (i=0; i<g_gameVarCount; i++)
{ {
if (kdfread(&(aGameVars[i]),sizeof(gamevar_t),1,fil) != 1) goto corrupt; if (kdfread(&(aGameVars[i]),sizeof(gamevar_t),1,fil) != 1) goto corrupt;
aGameVars[i].szLabel = (char *)Xcalloc(MAXVARLABEL,sizeof(uint8_t)); aGameVars[i].szLabel = (char *)Xmalloc(MAXVARLABEL * sizeof(uint8_t));
if (kdfread(aGameVars[i].szLabel,sizeof(uint8_t) * MAXVARLABEL, 1, fil) != 1) goto corrupt; if (kdfread(aGameVars[i].szLabel,sizeof(uint8_t) * MAXVARLABEL, 1, fil) != 1) goto corrupt;
hash_add(&h_gamevars, aGameVars[i].szLabel,i, 1); hash_add(&h_gamevars, aGameVars[i].szLabel,i, 1);
if (aGameVars[i].dwFlags & GAMEVAR_PERPLAYER) if (aGameVars[i].dwFlags & GAMEVAR_PERPLAYER)
{ {
aGameVars[i].val.plValues = (intptr_t*)Xcalloc(MAXPLAYERS,sizeof(intptr_t)); aGameVars[i].val.plValues = (intptr_t*)Xaligned_alloc(PLAYER_VAR_ALIGNMENT, MAXPLAYERS * sizeof(intptr_t));
if (kdfread(aGameVars[i].val.plValues,sizeof(intptr_t) * MAXPLAYERS, 1, fil) != 1) goto corrupt; if (kdfread(aGameVars[i].val.plValues,sizeof(intptr_t) * MAXPLAYERS, 1, fil) != 1) goto corrupt;
} }
else if (aGameVars[i].dwFlags & GAMEVAR_PERACTOR) else if (aGameVars[i].dwFlags & GAMEVAR_PERACTOR)
{ {
aGameVars[i].val.plValues = (intptr_t*)Xcalloc(MAXSPRITES,sizeof(intptr_t)); aGameVars[i].val.plValues = (intptr_t*)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, MAXSPRITES * sizeof(intptr_t));
if (kdfread(&aGameVars[i].val.plValues[0],sizeof(intptr_t), MAXSPRITES, fil) != MAXSPRITES) goto corrupt; if (kdfread(&aGameVars[i].val.plValues[0],sizeof(intptr_t), MAXSPRITES, fil) != MAXSPRITES) goto corrupt;
} }
} }
@ -149,11 +144,11 @@ int32_t Gv_ReadSave(int32_t fil, int32_t newbehav)
// read for .size and .dwFlags (the rest are pointers): // read for .size and .dwFlags (the rest are pointers):
if (kdfread(&aGameArrays[i],sizeof(gamearray_t),1,fil) != 1) goto corrupt; if (kdfread(&aGameArrays[i],sizeof(gamearray_t),1,fil) != 1) goto corrupt;
aGameArrays[i].szLabel = (char *)Xcalloc(MAXARRAYLABEL,sizeof(uint8_t)); aGameArrays[i].szLabel = (char *)Xmalloc(MAXARRAYLABEL * sizeof(uint8_t));
if (kdfread(aGameArrays[i].szLabel,sizeof(uint8_t) * MAXARRAYLABEL, 1, fil) != 1) goto corrupt; if (kdfread(aGameArrays[i].szLabel,sizeof(uint8_t) * MAXARRAYLABEL, 1, fil) != 1) goto corrupt;
hash_add(&h_arrays, aGameArrays[i].szLabel, i, 1); hash_add(&h_arrays, aGameArrays[i].szLabel, i, 1);
aGameArrays[i].plValues = (intptr_t *)Xcalloc(aGameArrays[i].size, GAR_ELTSZ); aGameArrays[i].plValues = (intptr_t *)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, aGameArrays[i].size * GAR_ELTSZ);
if (kdfread(aGameArrays[i].plValues, GAR_ELTSZ * aGameArrays[i].size, 1, fil) < 1) goto corrupt; if (kdfread(aGameArrays[i].plValues, GAR_ELTSZ * aGameArrays[i].size, 1, fil) < 1) goto corrupt;
} }
@ -172,7 +167,7 @@ int32_t Gv_ReadSave(int32_t fil, int32_t newbehav)
if (savedstate[i]) if (savedstate[i])
{ {
if (MapInfo[i].savedstate == NULL) if (MapInfo[i].savedstate == NULL)
MapInfo[i].savedstate = (mapstate_t *)Xcalloc(1,sizeof(mapstate_t)); MapInfo[i].savedstate = (mapstate_t *)Xaligned_alloc(16, sizeof(mapstate_t));
if (kdfread(MapInfo[i].savedstate,sizeof(mapstate_t),1,fil) != sizeof(mapstate_t)) goto corrupt; if (kdfread(MapInfo[i].savedstate,sizeof(mapstate_t),1,fil) != sizeof(mapstate_t)) goto corrupt;
for (j=0; j<g_gameVarCount; j++) for (j=0; j<g_gameVarCount; j++)
{ {
@ -180,13 +175,13 @@ int32_t Gv_ReadSave(int32_t fil, int32_t newbehav)
if (aGameVars[j].dwFlags & GAMEVAR_PERPLAYER) if (aGameVars[j].dwFlags & GAMEVAR_PERPLAYER)
{ {
// if (!MapInfo[i].savedstate->vars[j]) // if (!MapInfo[i].savedstate->vars[j])
MapInfo[i].savedstate->vars[j] = (intptr_t *)Xcalloc(MAXPLAYERS,sizeof(intptr_t)); MapInfo[i].savedstate->vars[j] = (intptr_t *)Xaligned_alloc(PLAYER_VAR_ALIGNMENT, MAXPLAYERS * sizeof(intptr_t));
if (kdfread(&MapInfo[i].savedstate->vars[j][0],sizeof(intptr_t) * MAXPLAYERS, 1, fil) != 1) goto corrupt; if (kdfread(&MapInfo[i].savedstate->vars[j][0],sizeof(intptr_t) * MAXPLAYERS, 1, fil) != 1) goto corrupt;
} }
else if (aGameVars[j].dwFlags & GAMEVAR_PERACTOR) else if (aGameVars[j].dwFlags & GAMEVAR_PERACTOR)
{ {
// if (!MapInfo[i].savedstate->vars[j]) // if (!MapInfo[i].savedstate->vars[j])
MapInfo[i].savedstate->vars[j] = (intptr_t *)Xcalloc(MAXSPRITES,sizeof(intptr_t)); MapInfo[i].savedstate->vars[j] = (intptr_t *)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, MAXSPRITES * sizeof(intptr_t));
if (kdfread(&MapInfo[i].savedstate->vars[j][0],sizeof(intptr_t), MAXSPRITES, fil) != MAXSPRITES) goto corrupt; if (kdfread(&MapInfo[i].savedstate->vars[j][0],sizeof(intptr_t), MAXSPRITES, fil) != MAXSPRITES) goto corrupt;
} }
} }
@ -426,11 +421,15 @@ int32_t Gv_NewArray(const char *pszLabel, void *arrayptr, intptr_t asize, uint32
if (aGameArrays[i].szLabel == NULL) if (aGameArrays[i].szLabel == NULL)
aGameArrays[i].szLabel=(char *)Xcalloc(MAXVARLABEL,sizeof(uint8_t)); aGameArrays[i].szLabel=(char *)Xcalloc(MAXVARLABEL,sizeof(uint8_t));
if (aGameArrays[i].szLabel != pszLabel) if (aGameArrays[i].szLabel != pszLabel)
Bstrcpy(aGameArrays[i].szLabel,pszLabel); Bstrcpy(aGameArrays[i].szLabel,pszLabel);
if (!(dwFlags & GAMEARRAY_TYPE_MASK)) if (!(dwFlags & GAMEARRAY_TYPE_MASK))
aGameArrays[i].plValues=(intptr_t *)Xcalloc(asize,GAR_ELTSZ); {
aGameArrays[i].plValues = (intptr_t *)Xaligned_alloc(ACTOR_VAR_ALIGNMENT, asize * GAR_ELTSZ);
Bmemset(aGameArrays[i].plValues, 0, asize * GAR_ELTSZ);
}
else else
aGameArrays[i].plValues=(intptr_t *)arrayptr; aGameArrays[i].plValues=(intptr_t *)arrayptr;
@ -495,18 +494,16 @@ int32_t Gv_NewVar(const char *pszLabel, intptr_t lValue, uint32_t dwFlags)
// Allocate and set its label // Allocate and set its label
if (aGameVars[i].szLabel == NULL) if (aGameVars[i].szLabel == NULL)
aGameVars[i].szLabel = (char *)Xcalloc(MAXVARLABEL,sizeof(uint8_t)); aGameVars[i].szLabel = (char *)Xcalloc(MAXVARLABEL,sizeof(uint8_t));
if (aGameVars[i].szLabel != pszLabel) if (aGameVars[i].szLabel != pszLabel)
Bstrcpy(aGameVars[i].szLabel,pszLabel); Bstrcpy(aGameVars[i].szLabel,pszLabel);
// and the flags // and the flags
aGameVars[i].dwFlags=dwFlags; aGameVars[i].dwFlags=dwFlags;
// only free if per-{actor,player}
if (aGameVars[i].dwFlags & GAMEVAR_USER_MASK) if (aGameVars[i].dwFlags & GAMEVAR_USER_MASK)
{ ALIGNED_FREE_AND_NULL(aGameVars[i].val.plValues);
// only free if per-{actor,player}
Bfree(aGameVars[i].val.plValues);
aGameVars[i].val.plValues=NULL;
}
} }
// if existing is system, they only get to change default value.... // if existing is system, they only get to change default value....
@ -523,14 +520,20 @@ int32_t Gv_NewVar(const char *pszLabel, intptr_t lValue, uint32_t dwFlags)
if (aGameVars[i].dwFlags & GAMEVAR_PERPLAYER) if (aGameVars[i].dwFlags & GAMEVAR_PERPLAYER)
{ {
if (!aGameVars[i].val.plValues) if (!aGameVars[i].val.plValues)
aGameVars[i].val.plValues = (intptr_t *)Xcalloc(MAXPLAYERS,sizeof(intptr_t)); {
aGameVars[i].val.plValues = (intptr_t *) Xaligned_alloc(PLAYER_VAR_ALIGNMENT, MAXPLAYERS * sizeof(intptr_t));
Bmemset(aGameVars[i].val.plValues, 0, MAXPLAYERS * sizeof(intptr_t));
}
for (j=MAXPLAYERS-1; j>=0; j--) for (j=MAXPLAYERS-1; j>=0; j--)
aGameVars[i].val.plValues[j]=lValue; aGameVars[i].val.plValues[j]=lValue;
} }
else if (aGameVars[i].dwFlags & GAMEVAR_PERACTOR) else if (aGameVars[i].dwFlags & GAMEVAR_PERACTOR)
{ {
if (!aGameVars[i].val.plValues) if (!aGameVars[i].val.plValues)
aGameVars[i].val.plValues = (intptr_t *)Xcalloc(MAXSPRITES,sizeof(intptr_t)); {
aGameVars[i].val.plValues = (intptr_t *) Xaligned_alloc(ACTOR_VAR_ALIGNMENT, MAXSPRITES * sizeof(intptr_t));
Bmemset(aGameVars[i].val.plValues, 0, MAXSPRITES * sizeof(intptr_t));
}
for (j=MAXSPRITES-1; j>=0; j--) for (j=MAXSPRITES-1; j>=0; j--)
aGameVars[i].val.plValues[j]=lValue; aGameVars[i].val.plValues[j]=lValue;
} }
@ -539,17 +542,6 @@ int32_t Gv_NewVar(const char *pszLabel, intptr_t lValue, uint32_t dwFlags)
return 1; return 1;
} }
void __fastcall A_ResetVars(int32_t iActor)
{
int32_t i=(MAXGAMEVARS-1);
do
{
if ((aGameVars[i].dwFlags & (GAMEVAR_PERACTOR|GAMEVAR_NODEFAULT)) == GAMEVAR_PERACTOR)
aGameVars[i].val.plValues[iActor]=aGameVars[i].lDefault;
}
while (i--);
}
static int32_t Gv_GetVarIndex(const char *szGameLabel) static int32_t Gv_GetVarIndex(const char *szGameLabel)
{ {
int32_t i = hash_find(&h_gamevars,szGameLabel); int32_t i = hash_find(&h_gamevars,szGameLabel);

View file

@ -104,7 +104,16 @@ void __fastcall Gv_SetVarX(int32_t id, int32_t lValue);
int32_t Gv_GetVarByLabel(const char *szGameLabel,int32_t lDefault,int32_t iActor,int32_t iPlayer); int32_t Gv_GetVarByLabel(const char *szGameLabel,int32_t lDefault,int32_t iActor,int32_t iPlayer);
int32_t Gv_NewArray(const char *pszLabel,void *arrayptr,intptr_t asize,uint32_t dwFlags); int32_t Gv_NewArray(const char *pszLabel,void *arrayptr,intptr_t asize,uint32_t dwFlags);
int32_t Gv_NewVar(const char *pszLabel,intptr_t lValue,uint32_t dwFlags); int32_t Gv_NewVar(const char *pszLabel,intptr_t lValue,uint32_t dwFlags);
void __fastcall A_ResetVars(int32_t iActor); static inline void A_ResetVars(int32_t iActor)
{
int32_t i;
for (i=0; i<g_gameVarCount; i++)
{
if ((aGameVars[i].dwFlags & (GAMEVAR_PERACTOR|GAMEVAR_NODEFAULT)) == GAMEVAR_PERACTOR)
aGameVars[i].val.plValues[iActor]=aGameVars[i].lDefault;
}
}
void Gv_DumpValues(void); void Gv_DumpValues(void);
void Gv_InitWeaponPointers(void); void Gv_InitWeaponPointers(void);
void Gv_RefreshPointers(void); void Gv_RefreshPointers(void);
@ -121,35 +130,28 @@ void Gv_Init(void);
void Gv_FinalizeWeaponDefaults(void); void Gv_FinalizeWeaponDefaults(void);
#if !defined LUNATIC #if !defined LUNATIC
#define GV_VAROP(func, operator) static inline void __fastcall func(int32_t id, int32_t lValue) \ #define VM_GAMEVAR_OPERATOR(func, operator) \
{ \ static inline void __fastcall func(int32_t id, int32_t lValue) \
switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK|GAMEVAR_PTR_MASK)) \ { \
{ \ switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK)) \
default: \ { \
aGameVars[id].val.lValue operator lValue; \ default: aGameVars[id].val.lValue operator lValue; break; \
break; \ case GAMEVAR_PERPLAYER: \
case GAMEVAR_PERPLAYER: \ if (EDUKE32_PREDICT_FALSE((unsigned)vm.g_p > MAXPLAYERS - 1)) \
if (EDUKE32_PREDICT_FALSE((unsigned)vm.g_p > MAXPLAYERS-1)) break; \ break; \
aGameVars[id].val.plValues[vm.g_p] operator lValue; \ aGameVars[id].val.plValues[vm.g_p] operator lValue; \
break; \ break; \
case GAMEVAR_PERACTOR: \ case GAMEVAR_PERACTOR: \
if (EDUKE32_PREDICT_FALSE((unsigned)vm.g_i > MAXSPRITES-1)) break; \ if (EDUKE32_PREDICT_FALSE((unsigned)vm.g_i > MAXSPRITES - 1)) \
aGameVars[id].val.plValues[vm.g_i] operator lValue; \ break; \
break; \ aGameVars[id].val.plValues[vm.g_i] operator lValue; \
case GAMEVAR_INTPTR: \ break; \
*((int32_t *)aGameVars[id].val.lValue) operator (int32_t)lValue; \ case GAMEVAR_INTPTR: *((int32_t *)aGameVars[id].val.lValue) operator(int32_t) lValue; break; \
break; \ case GAMEVAR_SHORTPTR: *((int16_t *)aGameVars[id].val.lValue) operator(int16_t) lValue; break; \
case GAMEVAR_SHORTPTR: \ case GAMEVAR_CHARPTR: *((uint8_t *)aGameVars[id].val.lValue) operator(uint8_t) lValue; break; \
*((int16_t *)aGameVars[id].val.lValue) operator (int16_t)lValue; \ } \
break; \ }
case GAMEVAR_CHARPTR: \
*((uint8_t *)aGameVars[id].val.lValue) operator (uint8_t)lValue; \
break; \
} \
}
// even though libdivide is faster than straight division (when using the LUT) the overhead makes this slower on x86
// ARM, however, has no hardware integer division
#if defined(__arm__) || defined(LIBDIVIDE_ALWAYS) #if defined(__arm__) || defined(LIBDIVIDE_ALWAYS)
static inline void __fastcall Gv_DivVar(int32_t id, int32_t lValue) static inline void __fastcall Gv_DivVar(int32_t id, int32_t lValue)
{ {
@ -158,47 +160,49 @@ static inline void __fastcall Gv_DivVar(int32_t id, int32_t lValue)
libdivide_s32_t *dptr = &sdiv; libdivide_s32_t *dptr = &sdiv;
intptr_t *iptr = &aGameVars[id].val.lValue; intptr_t *iptr = &aGameVars[id].val.lValue;
if (EDUKE32_PREDICT_FALSE((aGameVars[id].dwFlags & GAMEVAR_PERPLAYER && (unsigned) vm.g_p > MAXPLAYERS-1) || if (EDUKE32_PREDICT_FALSE((aGameVars[id].dwFlags & GAMEVAR_PERPLAYER && (unsigned)vm.g_p > MAXPLAYERS - 1) ||
(aGameVars[id].dwFlags & GAMEVAR_PERACTOR && (unsigned) vm.g_i > MAXSPRITES-1))) return; (aGameVars[id].dwFlags & GAMEVAR_PERACTOR && (unsigned)vm.g_i > MAXSPRITES - 1)))
return;
if ((unsigned) lValue < DIVTABLESIZE) if ((unsigned)lValue < DIVTABLESIZE)
dptr = (libdivide_s32_t *)&divtable32[lValue]; dptr = (libdivide_s32_t *)&divtable32[lValue];
else if (lValue != lastlValue) else if (lValue != lastlValue)
sdiv = libdivide_s32_gen(lValue), lastlValue = lValue; sdiv = libdivide_s32_gen(lValue), lastlValue = lValue;
switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK|GAMEVAR_PTR_MASK)) switch (aGameVars[id].dwFlags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK))
{ {
case GAMEVAR_PERPLAYER: case GAMEVAR_PERPLAYER: iptr = &aGameVars[id].val.plValues[vm.g_p];
iptr = &aGameVars[id].val.plValues[vm.g_p]; default: break;
default: case GAMEVAR_PERACTOR: iptr = &aGameVars[id].val.plValues[vm.g_i]; break;
break; case GAMEVAR_INTPTR:
case GAMEVAR_PERACTOR: *((int32_t *)aGameVars[id].val.lValue) =
iptr = &aGameVars[id].val.plValues[vm.g_i]; (int32_t)libdivide_s32_do(*((int32_t *)aGameVars[id].val.lValue), dptr);
break; return;
case GAMEVAR_INTPTR: case GAMEVAR_SHORTPTR:
*((int32_t *) aGameVars[id].val.lValue) = (int32_t) libdivide_s32_do(*((int32_t *) aGameVars[id].val.lValue), dptr); *((int16_t *)aGameVars[id].val.lValue) =
return; (int16_t)libdivide_s32_do(*((int16_t *)aGameVars[id].val.lValue), dptr);
case GAMEVAR_SHORTPTR: return;
*((int16_t *) aGameVars[id].val.lValue) = (int16_t) libdivide_s32_do(*((int16_t *) aGameVars[id].val.lValue), dptr); case GAMEVAR_CHARPTR:
return; *((uint8_t *)aGameVars[id].val.lValue) =
case GAMEVAR_CHARPTR: (uint8_t)libdivide_s32_do(*((uint8_t *)aGameVars[id].val.lValue), dptr);
*((uint8_t *) aGameVars[id].val.lValue) = (uint8_t) libdivide_s32_do(*((uint8_t *) aGameVars[id].val.lValue), dptr); return;
return;
} }
*iptr = libdivide_s32_do(*iptr, dptr); *iptr = libdivide_s32_do(*iptr, dptr);
} }
#else #else
GV_VAROP(Gv_DivVar, /=) VM_GAMEVAR_OPERATOR(Gv_DivVar, /= )
#endif #endif
GV_VAROP(Gv_AddVar, +=) VM_GAMEVAR_OPERATOR(Gv_AddVar, +=)
GV_VAROP(Gv_SubVar, -=) VM_GAMEVAR_OPERATOR(Gv_SubVar, -=)
GV_VAROP(Gv_MulVar, *=) VM_GAMEVAR_OPERATOR(Gv_MulVar, *=)
GV_VAROP(Gv_ModVar, %=) VM_GAMEVAR_OPERATOR(Gv_ModVar, %=)
GV_VAROP(Gv_AndVar, &=) VM_GAMEVAR_OPERATOR(Gv_AndVar, &=)
GV_VAROP(Gv_XorVar, ^=) VM_GAMEVAR_OPERATOR(Gv_XorVar, ^=)
GV_VAROP(Gv_OrVar, |=) VM_GAMEVAR_OPERATOR(Gv_OrVar, |=)
#undef VM_GAMEVAR_OPERATOR
#endif #endif