diff options
Diffstat (limited to 'src/qcommon')
-rw-r--r-- | src/qcommon/common.c | 131 | ||||
-rw-r--r-- | src/qcommon/cvar.c | 306 | ||||
-rw-r--r-- | src/qcommon/files.c | 56 | ||||
-rw-r--r-- | src/qcommon/net_ip.c | 6 | ||||
-rw-r--r-- | src/qcommon/q_shared.h | 55 | ||||
-rw-r--r-- | src/qcommon/qcommon.h | 9 | ||||
-rw-r--r-- | src/qcommon/vm.c | 16 | ||||
-rw-r--r-- | src/qcommon/vm_interpreted.c | 21 | ||||
-rw-r--r-- | src/qcommon/vm_local.h | 11 | ||||
-rw-r--r-- | src/qcommon/vm_x86_64.c | 229 | ||||
-rw-r--r-- | src/qcommon/vm_x86_64_assembler.c | 41 |
11 files changed, 654 insertions, 227 deletions
diff --git a/src/qcommon/common.c b/src/qcommon/common.c index ddd3c023..d1e702c6 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -92,8 +92,9 @@ int com_frameTime; int com_frameMsec; int com_frameNumber; -qboolean com_errorEntered; -qboolean com_fullyInitialized; +qboolean com_errorEntered = qfalse; +qboolean com_fullyInitialized = qfalse; +qboolean com_gameRestarting = qfalse; char com_errorMessage[MAXPRINTMSG]; @@ -244,9 +245,23 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { va_list argptr; static int lastErrorTime; static int errorCount; + static qboolean calledSysError = qfalse; int currentTime; - Cvar_Set( "com_errorCode", va( "%i", code ) ); + if(com_errorEntered) + { + if(!calledSysError) + { + calledSysError = qtrue; + Sys_Error("recursive error after: %s", com_errorMessage); + } + + return; + } + + com_errorEntered = qtrue; + + Cvar_Set("com_errorCode", va("%i", code)); // when we are running automated scripts, make sure we // know if anything failed @@ -265,11 +280,6 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { } lastErrorTime = currentTime; - if ( com_errorEntered ) { - Sys_Error( "recursive error after: %s", com_errorMessage ); - } - com_errorEntered = qtrue; - va_start (argptr,fmt); Q_vsnprintf (com_errorMessage, sizeof(com_errorMessage),fmt,argptr); va_end (argptr); @@ -304,12 +314,13 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { VM_Forced_Unload_Start(); CL_FlushMemory( ); VM_Forced_Unload_Done(); - com_errorEntered = qfalse; CL_CDDialog(); } else { Com_Printf("Server didn't have CD\n" ); } FS_PureServerSetLoadedPaks("", ""); + + com_errorEntered = qfalse; longjmp (abortframe, -1); } else { CL_Shutdown (); @@ -318,6 +329,7 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { Com_Shutdown (); + calledSysError = qtrue; Sys_Error ("%s", com_errorMessage); } @@ -2363,15 +2375,7 @@ void Com_Setenv_f(void) { char *arg2 = Cmd_ArgsFrom(2); -#ifdef _WIN32 - // windows already removes env variable if value is an empty string - _putenv(va("%s=%s", arg1, arg2)); -#else - if(!*arg2) - unsetenv(arg1); - else - setenv(arg1, arg2, 1); -#endif + Sys_SetEnv(arg1, arg2); } else if(argc == 2) { @@ -2384,6 +2388,85 @@ void Com_Setenv_f(void) } } +/* +================== +Com_ExecuteCfg + +For controlling environment variables +================== +*/ + +void Com_ExecuteCfg(void) +{ + Cbuf_ExecuteText(EXEC_NOW, "exec default.cfg\n"); + Cbuf_Execute(); // Always execute after exec to prevent text buffer overflowing + + if(!Com_SafeMode()) + { + // skip the q3config.cfg and autoexec.cfg if "safe" is on the command line + Cbuf_ExecuteText(EXEC_NOW, "exec " Q3CONFIG_CFG "\n"); + Cbuf_Execute(); + Cbuf_ExecuteText(EXEC_NOW, "exec autoexec.cfg\n"); + Cbuf_Execute(); + } +} + +/* +================== +Com_GameRestart + +Change to a new mod properly with cleaning up cvars before switching. +================== +*/ + +void Com_GameRestart(int checksumFeed, qboolean clientRestart) +{ + // make sure no recursion can be triggered + if(!com_gameRestarting && com_fullyInitialized) + { + com_gameRestarting = qtrue; + + if(clientRestart) + { + CL_Disconnect(qfalse); + CL_ShutdownAll(); + } + + // Kill server if we have one + if(com_sv_running->integer) + SV_Shutdown("Game directory changed"); + + FS_Restart(checksumFeed); + + // Clean out any user and VM created cvars + Cvar_Restart(qtrue); + Com_ExecuteCfg(); + + // Restart sound subsystem so old handles are flushed + CL_Snd_Restart(); + + if(clientRestart) + CL_StartHunkUsers(qfalse); + + com_gameRestarting = qfalse; + } +} + +/* +================== +Com_GameRestart_f + +Expose possibility to change current running mod to the user +================== +*/ + +void Com_GameRestart_f(void) +{ + Cvar_Set("fs_game", Cmd_Argv(1)); + + Com_GameRestart(0, qtrue); +} + static void Com_DetectAltivec(void) { // Only detect if user hasn't forcibly disabled it. @@ -2480,17 +2563,9 @@ void Com_Init( char *commandLine ) { Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f ); Cmd_AddCommand ("writeconfig", Com_WriteConfig_f ); Cmd_SetCommandCompletionFunc( "writeconfig", Cmd_CompleteCfgName ); + Cmd_AddCommand("game_restart", Com_GameRestart_f); - // Make it execute the configuration files - Cbuf_AddText ("exec default.cfg\n"); - - // skip the autogen.cfg if "safe" is on the command line - if (!Com_SafeMode()) - Cbuf_AddText("exec " Q3CONFIG_CFG "\n"); - - Cbuf_AddText ("exec autoexec.cfg\n"); - - Cbuf_Execute (); + Com_ExecuteCfg(); // override anything from the config files with command line args Com_StartupVariable( NULL ); diff --git a/src/qcommon/cvar.c b/src/qcommon/cvar.c index f82eb78f..459f602b 100644 --- a/src/qcommon/cvar.c +++ b/src/qcommon/cvar.c @@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "q_shared.h" #include "qcommon.h" -cvar_t *cvar_vars; +cvar_t *cvar_vars = NULL; cvar_t *cvar_cheats; int cvar_modifiedFlags; @@ -34,7 +34,7 @@ cvar_t cvar_indexes[MAX_CVARS]; int cvar_numIndexes; #define FILE_HASH_SIZE 256 -static cvar_t* hashTable[FILE_HASH_SIZE]; +static cvar_t *hashTable[FILE_HASH_SIZE]; cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force); @@ -182,11 +182,14 @@ int Cvar_Flags(const char *var_name) Cvar_CommandCompletion ============ */ -void Cvar_CommandCompletion( void(*callback)(const char *s) ) { +void Cvar_CommandCompletion(void (*callback)(const char *s)) +{ cvar_t *cvar; - for ( cvar = cvar_vars ; cvar ; cvar = cvar->next ) { - callback( cvar->name ); + for(cvar = cvar_vars; cvar; cvar = cvar->next) + { + if(cvar->name) + callback(cvar->name); } } @@ -305,6 +308,7 @@ The flags will be or'ed in if the variable exists. cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; + int index; if ( !var_name || ! var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); @@ -323,13 +327,15 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { #endif var = Cvar_FindVar (var_name); - if ( var ) { - var_value = Cvar_Validate( var, var_value, qfalse ); + + if(var) + { + var_value = Cvar_Validate(var, var_value, qfalse); // if the C code is now specifying a variable that the user already // set a value for, take the new value as the reset value - if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) - && var_value[0] ) { + if(var->flags & CVAR_USER_CREATED) + { var->flags &= ~CVAR_USER_CREATED; Z_Free( var->resetString ); var->resetString = CopyString( var_value ); @@ -344,13 +350,22 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { var->latchedString = CopyString(var_value); } - - // ZOID--needs to be set so that cvars the game sets as - // SERVERINFO get sent to clients - cvar_modifiedFlags |= flags; } - + + // Make sure the game code cannot mark engine-added variables as gamecode vars + if(var->flags & CVAR_VM_CREATED) + { + if(!(flags & CVAR_VM_CREATED)) + var->flags &= ~CVAR_VM_CREATED; + } + else + { + if(flags & CVAR_VM_CREATED) + flags &= ~CVAR_VM_CREATED; + } + var->flags |= flags; + // only allow one non-empty reset string without a warning if ( !var->resetString[0] ) { // we don't have a reset string yet @@ -368,6 +383,10 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { var->latchedString = NULL; // otherwise cvar_set2 would free it Cvar_Set2( var_name, s, qtrue ); Z_Free( s ); + + // ZOID--needs to be set so that cvars the game sets as + // SERVERINFO get sent to clients + cvar_modifiedFlags |= flags; } return var; @@ -376,11 +395,27 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { // // allocate a new cvar // - if ( cvar_numIndexes >= MAX_CVARS ) { - Com_Error( ERR_FATAL, "MAX_CVARS" ); + + // find a free cvar + for(index = 0; index < MAX_CVARS; index++) + { + if(!cvar_indexes[index].name) + break; + } + + if(index >= MAX_CVARS) + { + if(!com_errorEntered) + Com_Error(ERR_FATAL, "Error: Too many cvars, cannot create a new one!"); + + return NULL; } - var = &cvar_indexes[cvar_numIndexes]; - cvar_numIndexes++; + + var = &cvar_indexes[index]; + + if(index >= cvar_numIndexes) + cvar_numIndexes = index + 1; + var->name = CopyString (var_name); var->string = CopyString (var_value); var->modified = qtrue; @@ -392,6 +427,10 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { // link the variable in var->next = cvar_vars; + if(cvar_vars) + cvar_vars->prev = var; + + var->prev = NULL; cvar_vars = var; var->flags = flags; @@ -399,7 +438,13 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_modifiedFlags |= var->flags; hash = generateHashValue(var_name); + var->hashIndex = hash; + var->hashNext = hashTable[hash]; + if(hashTable[hash]) + hashTable[hash]->hashPrev = var; + + var->hashPrev = NULL; hashTable[hash] = var; return var; @@ -471,15 +516,23 @@ cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force ) { value = var->resetString; } - value = Cvar_Validate( var, value, qtrue ); + value = Cvar_Validate(var, value, qtrue); - if((var->flags & CVAR_LATCH) && var->latchedString) { - if(!strcmp(value,var->latchedString)) + if((var->flags & CVAR_LATCH) && var->latchedString) + { + if(!strcmp(value, var->string)) + { + Z_Free(var->latchedString); + var->latchedString = NULL; + return var; + } + + if(!strcmp(value, var->latchedString)) return var; } - else if (!strcmp(value,var->string)) { + else if(!strcmp(value, var->string)) return var; - } + // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) cvar_modifiedFlags |= var->flags; @@ -610,12 +663,15 @@ Cvar_SetCheatState Any testing variables will be reset to the safe values ============ */ -void Cvar_SetCheatState( void ) { +void Cvar_SetCheatState(void) +{ cvar_t *var; // set all default vars to the safe value - for ( var = cvar_vars ; var ; var = var->next ) { - if ( var->flags & CVAR_CHEAT ) { + for(var = cvar_vars; var ; var = var->next) + { + if(var->flags & CVAR_CHEAT) + { // the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here // because of a different var->latchedString if (var->latchedString) @@ -623,9 +679,8 @@ void Cvar_SetCheatState( void ) { Z_Free(var->latchedString); var->latchedString = NULL; } - if (strcmp(var->resetString,var->string)) { - Cvar_Set( var->name, var->resetString ); - } + if (strcmp(var->resetString,var->string)) + Cvar_Set(var->name, var->resetString); } } } @@ -803,11 +858,16 @@ Appends lines containing "set variable value" for all variables with the archive flag set to qtrue. ============ */ -void Cvar_WriteVariables( fileHandle_t f ) { +void Cvar_WriteVariables(fileHandle_t f) +{ cvar_t *var; char buffer[1024]; - for (var = cvar_vars ; var ; var = var->next) { + for (var = cvar_vars; var; var = var->next) + { + if(!var->name) + continue; + if( var->flags & CVAR_ARCHIVE ) { // write the latched value, even if it hasn't taken effect yet if ( var->latchedString ) { @@ -849,7 +909,8 @@ void Cvar_List_f( void ) { i = 0; for (var = cvar_vars ; var ; var = var->next, i++) { - if (match && !Com_Filter(match, var->name, qfalse)) continue; + if(!var->name || (match && !Com_Filter(match, var->name, qfalse))) + continue; if (var->flags & CVAR_SERVERINFO) { Com_Printf("S"); @@ -906,74 +967,141 @@ void Cvar_List_f( void ) { /* ============ -Cvar_Restart_f +Cvar_Unset -Resets all cvars to their hardcoded values +Unsets a cvar ============ */ -void Cvar_Restart_f( void ) { - cvar_t *var; - cvar_t **prev; - prev = &cvar_vars; - while ( 1 ) { - var = *prev; - if ( !var ) { - break; - } +cvar_t *Cvar_Unset(cvar_t *cv) +{ + cvar_t *next = cv->next; + + if(cv->name) + Z_Free(cv->name); + if(cv->string) + Z_Free(cv->string); + if(cv->latchedString) + Z_Free(cv->latchedString); + if(cv->resetString) + Z_Free(cv->resetString); + + if(cv->prev) + cv->prev->next = cv->next; + else + cvar_vars = cv->next; + if(cv->next) + cv->next->prev = cv->prev; - // don't mess with rom values, or some inter-module - // communication will get broken (com_cl_running, etc) - if ( var->flags & ( CVAR_ROM | CVAR_INIT | CVAR_NORESTART ) ) { - prev = &var->next; - continue; - } + if(cv->hashPrev) + cv->hashPrev->hashNext = cv->hashNext; + else + hashTable[cv->hashIndex] = cv->hashNext; + if(cv->hashNext) + cv->hashNext->hashPrev = cv->hashPrev; - // throw out any variables the user created - if ( var->flags & CVAR_USER_CREATED ) { - *prev = var->next; - if ( var->name ) { - Z_Free( var->name ); - } - if ( var->string ) { - Z_Free( var->string ); - } - if ( var->latchedString ) { - Z_Free( var->latchedString ); - } - if ( var->resetString ) { - Z_Free( var->resetString ); - } - // clear the var completely, since we - // can't remove the index from the list - Com_Memset( var, 0, sizeof( var ) ); - continue; - } + Com_Memset(cv, '\0', sizeof(*cv)); + + return next; +} - Cvar_Set( var->name, var->resetString ); +/* +============ +Cvar_Unset_f + +Unsets a userdefined cvar +============ +*/ - prev = &var->next; +void Cvar_Unset_f(void) +{ + cvar_t *cv; + + if(Cmd_Argc() != 2) + { + Com_Printf("Usage: %s <varname>\n", Cmd_Argv(0)); + return; } + + cv = Cvar_FindVar(Cmd_Argv(1)); + + if(!cv) + return; + + if(cv->flags & CVAR_USER_CREATED) + Cvar_Unset(cv); + else + Com_Printf("Error: %s: Variable %s is not user created.\n", Cmd_Argv(0), cv->name); } /* +============ +Cvar_Restart + +Resets all cvars to their hardcoded values and removes userdefined variables +and variables added via the VMs if requested. +============ +*/ + +void Cvar_Restart(qboolean unsetVM) +{ + cvar_t *curvar; + + curvar = cvar_vars; + + while(curvar) + { + if((curvar->flags & CVAR_USER_CREATED) || + (unsetVM && (curvar->flags & CVAR_VM_CREATED))) + { + // throw out any variables the user/vm created + curvar = Cvar_Unset(curvar); + continue; + } + + if(!(curvar->flags & (CVAR_ROM | CVAR_INIT | CVAR_NORESTART))) + { + // Just reset the rest to their default values. + Cvar_Set2(curvar->name, curvar->resetString, qfalse); + } + + curvar = curvar->next; + } +} + + +/* +============ +Cvar_Restart_f + +Resets all cvars to their hardcoded values +============ +*/ +void Cvar_Restart_f(void) +{ + Cvar_Restart(qfalse); +} + +/* ===================== Cvar_InfoString ===================== */ -char *Cvar_InfoString( int bit ) { +char *Cvar_InfoString(int bit) +{ static char info[MAX_INFO_STRING]; cvar_t *var; info[0] = 0; - for (var = cvar_vars ; var ; var = var->next) { - if (var->flags & bit) { + for(var = cvar_vars; var; var = var->next) + { + if(var->name && (var->flags & bit)) Info_SetValueForKey (info, var->name, var->string); - } } + return info; } @@ -984,16 +1112,17 @@ Cvar_InfoString_Big handles large info strings ( CS_SYSTEMINFO ) ===================== */ -char *Cvar_InfoString_Big( int bit ) { +char *Cvar_InfoString_Big(int bit) +{ static char info[BIG_INFO_STRING]; cvar_t *var; info[0] = 0; - for (var = cvar_vars ; var ; var = var->next) { - if (var->flags & bit) { + for (var = cvar_vars; var; var = var->next) + { + if(var->name && (var->flags & bit)) Info_SetValueForKey_Big (info, var->name, var->string); - } } return info; } @@ -1032,13 +1161,15 @@ Cvar_Register basically a slightly modified Cvar_Get for the interpreted modules ===================== */ -void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { +void Cvar_Register(vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags) +{ cvar_t *cv; - cv = Cvar_Get( varName, defaultValue, flags ); - if ( !vmCvar ) { + cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED); + + if (!vmCvar) return; - } + vmCvar->handle = cv - cvar_indexes; vmCvar->modificationCount = -1; Cvar_Update( vmCvar ); @@ -1103,7 +1234,11 @@ Cvar_Init Reads in all archived cvars ============ */ -void Cvar_Init (void) { +void Cvar_Init (void) +{ + Com_Memset(cvar_indexes, '\0', sizeof(cvar_indexes)); + Com_Memset(hashTable, '\0', sizeof(hashTable)); + cvar_cheats = Cvar_Get("sv_cheats", "1", CVAR_ROM | CVAR_SYSTEMINFO ); Cmd_AddCommand ("print", Cvar_Print_f); @@ -1119,6 +1254,9 @@ void Cvar_Init (void) { Cmd_SetCommandCompletionFunc( "seta", Cvar_CompleteCvarName ); Cmd_AddCommand ("reset", Cvar_Reset_f); Cmd_SetCommandCompletionFunc( "reset", Cvar_CompleteCvarName ); + Cmd_AddCommand ("unset", Cvar_Unset_f); + Cmd_SetCommandCompletionFunc("unset", Cvar_CompleteCvarName); + Cmd_AddCommand ("cvarlist", Cvar_List_f); Cmd_AddCommand ("cvar_restart", Cvar_Restart_f); } diff --git a/src/qcommon/files.c b/src/qcommon/files.c index 87301441..bb1f3f15 100644 --- a/src/qcommon/files.c +++ b/src/qcommon/files.c @@ -249,7 +249,7 @@ static searchpath_t *fs_searchpaths; static int fs_readCount; // total bytes read static int fs_loadCount; // total files read static int fs_loadStack; // total files in memory -static int fs_packFiles; // total number of files in packs +static int fs_packFiles = 0; // total number of files in packs static int fs_checksumFeed; @@ -281,7 +281,7 @@ static fileHandleData_t fsh[MAX_FILE_HANDLES]; static qboolean fs_reordered; // never load anything from pk3 files that are not present at the server when pure -static int fs_numServerPaks; +static int fs_numServerPaks = 0; static int fs_serverPaks[MAX_SEARCH_PATHS]; // checksums static char *fs_serverPakNames[MAX_SEARCH_PATHS]; // pk3 names @@ -438,10 +438,18 @@ Fix things up differently for win/unix/mac */ static void FS_ReplaceSeparators( char *path ) { char *s; + qboolean lastCharWasSep = qfalse; for ( s = path ; *s ; s++ ) { if ( *s == '/' || *s == '\\' ) { - *s = PATH_SEP; + if ( !lastCharWasSep ) { + *s = PATH_SEP; + lastCharWasSep = qtrue; + } else { + memmove (s, s + 1, strlen (s)); + } + } else { + lastCharWasSep = qfalse; } } } @@ -465,7 +473,7 @@ char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ) { } Com_sprintf( temp, sizeof(temp), "/%s/%s", game, qpath ); - FS_ReplaceSeparators( temp ); + FS_ReplaceSeparators( temp ); Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s", base, temp ); return ospath[toggle]; @@ -481,6 +489,7 @@ Creates any directories needed to store the given filename */ qboolean FS_CreatePath (char *OSPath) { char *ofs; + char path[MAX_OSPATH]; // make absolutely sure that it can't back up the path // FIXME: is c: allowed??? @@ -489,14 +498,25 @@ qboolean FS_CreatePath (char *OSPath) { return qtrue; } - for (ofs = OSPath+1 ; *ofs ; ofs++) { - if (*ofs == PATH_SEP) { + Q_strncpyz( path, OSPath, sizeof( path ) ); + FS_ReplaceSeparators( path ); + + // Skip creation of the root directory as it will always be there + ofs = strchr( path, PATH_SEP ); + ofs++; + + for (; ofs != NULL && *ofs ; ofs++) { + if (*ofs == PATH_SEP) { // create the directory *ofs = 0; - Sys_Mkdir (OSPath); + if (!Sys_Mkdir (path)) { + Com_Error( ERR_FATAL, "FS_CreatePath: failed to create path \"%s\"\n", + path ); + } *ofs = PATH_SEP; } } + return qfalse; } @@ -2839,6 +2859,8 @@ static void FS_Startup( const char *gameName ) Com_Printf( "----- FS_Startup -----\n" ); + fs_packFiles = 0; + fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT ); fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); @@ -2864,11 +2886,12 @@ static void FS_Startup( const char *gameName ) // NOTE: same filtering below for mods and basegame if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { + FS_CreatePath ( fs_homepath->string ); FS_AddGameDirectory ( fs_homepath->string, gameName ); } // check for additional base game so mods can be based upon other mods - if ( fs_basegame->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_basegame->string, gameName ) ) { + if ( fs_basegame->string[0] && Q_stricmp( fs_basegame->string, gameName ) ) { if (fs_basepath->string[0]) { FS_AddGameDirectory(fs_basepath->string, fs_basegame->string); } @@ -2878,7 +2901,7 @@ static void FS_Startup( const char *gameName ) } // check for additional game folder for mods - if ( fs_gamedirvar->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_gamedirvar->string, gameName ) ) { + if ( fs_gamedirvar->string[0] && Q_stricmp( fs_gamedirvar->string, gameName ) ) { if (fs_basepath->string[0]) { FS_AddGameDirectory(fs_basepath->string, fs_gamedirvar->string); } @@ -3348,11 +3371,20 @@ FS_ConditionalRestart restart if necessary ================= */ -qboolean FS_ConditionalRestart( int checksumFeed ) { - if( fs_gamedirvar->modified || checksumFeed != fs_checksumFeed ) { - FS_Restart( checksumFeed ); +qboolean FS_ConditionalRestart(int checksumFeed) +{ + if(fs_gamedirvar->modified) + { + Com_GameRestart(checksumFeed, qfalse); return qtrue; } + + else if(checksumFeed != fs_checksumFeed) + { + FS_Restart(checksumFeed); + return qtrue; + } + return qfalse; } diff --git a/src/qcommon/net_ip.c b/src/qcommon/net_ip.c index 9ad4a821..db9f2924 100644 --- a/src/qcommon/net_ip.c +++ b/src/qcommon/net_ip.c @@ -995,7 +995,7 @@ void NET_SetMulticast6(void) if(*net_mcast6iface->string) { #ifdef _WIN32 - curgroup.ipv6mr_interface = atoi(net_mcast6iface->string); + curgroup.ipv6mr_interface = net_mcast6iface->integer; #else curgroup.ipv6mr_interface = if_nametoindex(net_mcast6iface->string); #endif @@ -1470,7 +1470,11 @@ static qboolean NET_GetCvars( void ) { modified += net_mcast6addr->modified; net_mcast6addr->modified = qfalse; +#ifdef _WIN32 net_mcast6iface = Cvar_Get( "net_mcast6iface", "0", CVAR_LATCH | CVAR_ARCHIVE ); +#else + net_mcast6iface = Cvar_Get( "net_mcast6iface", "", CVAR_LATCH | CVAR_ARCHIVE ); +#endif modified += net_mcast6iface->modified; net_mcast6iface->modified = qfalse; diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h index 29cc4b9b..186bb461 100644 --- a/src/qcommon/q_shared.h +++ b/src/qcommon/q_shared.h @@ -863,30 +863,33 @@ default values. ========================================================== */ -#define CVAR_ARCHIVE 1 // set to cause it to be saved to vars.rc - // used for system variables, not for player - // specific configurations -#define CVAR_USERINFO 2 // sent to server on connect or change -#define CVAR_SERVERINFO 4 // sent in response to front end requests -#define CVAR_SYSTEMINFO 8 // these cvars will be duplicated on all clients -#define CVAR_INIT 16 // don't allow change from console at all, - // but can be set from the command line -#define CVAR_LATCH 32 // will only change when C code next does - // a Cvar_Get(), so it can't be changed - // without proper initialization. modified - // will be set, even though the value hasn't - // changed yet -#define CVAR_ROM 64 // display only, cannot be set by user at all -#define CVAR_USER_CREATED 128 // created by a set command -#define CVAR_TEMP 256 // can be set even when cheats are disabled, but is not archived -#define CVAR_CHEAT 512 // can not be changed if cheats are disabled -#define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued - -#define CVAR_SERVER_CREATED 2048 // cvar was created by a server the client connected to. +#define CVAR_ARCHIVE 0x0001 // set to cause it to be saved to vars.rc + // used for system variables, not for player + // specific configurations +#define CVAR_USERINFO 0x0002 // sent to server on connect or change +#define CVAR_SERVERINFO 0x0004 // sent in response to front end requests +#define CVAR_SYSTEMINFO 0x0008 // these cvars will be duplicated on all clients +#define CVAR_INIT 0x0010 // don't allow change from console at all, + // but can be set from the command line +#define CVAR_LATCH 0x0020 // will only change when C code next does + // a Cvar_Get(), so it can't be changed + // without proper initialization. modified + // will be set, even though the value hasn't + // changed yet +#define CVAR_ROM 0x0040 // display only, cannot be set by user at all +#define CVAR_USER_CREATED 0x0080 // created by a set command +#define CVAR_TEMP 0x0100 // can be set even when cheats are disabled, but is not archived +#define CVAR_CHEAT 0x0200 // can not be changed if cheats are disabled +#define CVAR_NORESTART 0x0400 // do not clear when a cvar_restart is issued + +#define CVAR_SERVER_CREATED 0x0800 // cvar was created by a server the client connected to. +#define CVAR_VM_CREATED 0x1000 // cvar was created exclusively in one of the VMs. #define CVAR_NONEXISTENT 0xFFFFFFFF // Cvar doesn't exist. // nothing outside the Cvar_*() functions should modify these fields! -typedef struct cvar_s { +typedef struct cvar_s cvar_t; + +struct cvar_s { char *name; char *string; char *resetString; // cvar_restart will reset to this value @@ -900,9 +903,13 @@ typedef struct cvar_s { qboolean integral; float min; float max; - struct cvar_s *next; - struct cvar_s *hashNext; -} cvar_t; + + cvar_t *next; + cvar_t *prev; + cvar_t *hashNext; + cvar_t *hashPrev; + int hashIndex; +}; #define MAX_CVAR_VALUE_STRING 256 diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index 1f9e744d..690e89c6 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -534,6 +534,7 @@ char *Cvar_InfoString_Big( int bit ); void Cvar_InfoStringBuffer( int bit, char *buff, int buffsize ); void Cvar_CheckRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral ); +void Cvar_Restart(qboolean unsetVM); void Cvar_Restart_f( void ); void Cvar_CompleteCvarName( char *args, int argNum ); @@ -784,6 +785,7 @@ void QDECL Com_Printf( const char *fmt, ... ) __attribute__ ((format (printf, void QDECL Com_DPrintf( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2))); void QDECL Com_Error( int code, const char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); void Com_Quit_f( void ); +void Com_GameRestart(int checksumFeed, qboolean clientRestart); int Com_Milliseconds( void ); // will be journaled properly unsigned Com_BlockChecksum( const void *buffer, int length ); @@ -961,6 +963,9 @@ void CL_FlushMemory( void ); void CL_StartHunkUsers( qboolean rendererOnly ); // start all the client stuff using the hunk +void CL_Snd_Restart(void); +// Restart sound subsystem + void Key_KeynameCompletion( void(*callback)(const char *s) ); // for keyname autocompletion @@ -1057,7 +1062,7 @@ qboolean Sys_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ); qboolean Sys_IsLANAddress (netadr_t adr); void Sys_ShowIP(void); -void Sys_Mkdir( const char *path ); +qboolean Sys_Mkdir( const char *path ); char *Sys_Cwd( void ); void Sys_SetDefaultInstallPath(const char *path); char *Sys_DefaultInstallPath(void); @@ -1078,6 +1083,8 @@ void Sys_Sleep(int msec); qboolean Sys_LowPhysicalMemory( void ); +void Sys_SetEnv(const char *name, const char *value); + /* This is based on the Adaptive Huffman algorithm described in Sayood's Data * Compression book. The ranks are not actually stored, but implicitly defined * by the location of a node within a doubly-linked list */ diff --git a/src/qcommon/vm.c b/src/qcommon/vm.c index 876d36d5..5956c89a 100644 --- a/src/qcommon/vm.c +++ b/src/qcommon/vm.c @@ -246,7 +246,7 @@ void VM_LoadSymbols( vm_t *vm ) { return; } - numInstructions = vm->instructionPointersLength >> 2; + numInstructions = vm->instructionCount; // parse the symbols text_p = mapfile.c; @@ -358,8 +358,6 @@ intptr_t QDECL VM_DllSyscall( intptr_t arg, ... ) { } -#define STACK_SIZE 0x20000 - /* ================= VM_LoadQVM @@ -431,7 +429,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { // round up to next power of 2 so all data operations can // be mask protected dataLength = header.h->dataLength + header.h->litLength + - header.h->bssLength + STACK_SIZE; + header.h->bssLength; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; @@ -577,8 +575,8 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), } // allocate space for the jump targets, which will be filled in by the compile/prep functions - vm->instructionPointersLength = header->instructionCount * 4; - vm->instructionPointers = Hunk_Alloc( vm->instructionPointersLength, h_high ); + vm->instructionCount = header->instructionCount; + vm->instructionPointers = Hunk_Alloc( vm->instructionCount*4, h_high ); // copy or compile the instructions vm->codeLength = header->codeLength; @@ -610,7 +608,7 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), // the stack is implicitly at the end of the image vm->programStack = vm->dataMask + 1; - vm->stackBottom = vm->programStack - STACK_SIZE; + vm->stackBottom = vm->programStack - PROGRAM_STACK_SIZE; Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining()); @@ -734,8 +732,6 @@ an OP_ENTER instruction, which will subtract space for locals from sp ============== */ -#define MAX_STACK 256 -#define STACK_MASK (MAX_STACK-1) intptr_t QDECL VM_Call( vm_t *vm, int callnum, ... ) { vm_t *oldVM; @@ -896,7 +892,7 @@ void VM_VmInfo_f( void ) { Com_Printf( "interpreted\n" ); } Com_Printf( " code length : %7i\n", vm->codeLength ); - Com_Printf( " table length: %7i\n", vm->instructionPointersLength ); + Com_Printf( " table length: %7i\n", vm->instructionCount*4 ); Com_Printf( " data length : %7i\n", vm->dataMask + 1 ); } } diff --git a/src/qcommon/vm_interpreted.c b/src/qcommon/vm_interpreted.c index cb6ae2f2..5f755623 100644 --- a/src/qcommon/vm_interpreted.c +++ b/src/qcommon/vm_interpreted.c @@ -312,13 +312,11 @@ an OP_ENTER instruction, which will subtract space for locals from sp ============== */ -#define MAX_STACK 256 -#define STACK_MASK (MAX_STACK-1) #define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack ) int VM_CallInterpreted( vm_t *vm, int *args ) { - int stack[MAX_STACK]; + int stack[OPSTACK_SIZE]; int *opStack; int programCounter; int programStack; @@ -393,7 +391,7 @@ nextInstruction2: if ( opStack < stack ) { Com_Error( ERR_DROP, "VM opStack underflow" ); } - if ( opStack >= stack+MAX_STACK ) { + if ( opStack >= stack+OPSTACK_SIZE ) { Com_Error( ERR_DROP, "VM opStack overflow" ); } @@ -441,10 +439,10 @@ nextInstruction2: Com_Error( ERR_DROP, "OP_LOAD4 misaligned" ); } #endif - r0 = *opStack = *(int *)&image[ r0&dataMask ]; + r0 = *opStack = *(int *)&image[ r0&dataMask&~3 ]; goto nextInstruction2; case OP_LOAD2: - r0 = *opStack = *(unsigned short *)&image[ r0&dataMask ]; + r0 = *opStack = *(unsigned short *)&image[ r0&dataMask&~1 ]; goto nextInstruction2; case OP_LOAD1: r0 = *opStack = image[ r0&dataMask ]; @@ -465,7 +463,7 @@ nextInstruction2: case OP_ARG: // single byte offset from programStack - *(int *)&image[ codeImage[programCounter] + programStack ] = r0; + *(int *)&image[ (codeImage[programCounter] + programStack)&dataMask&~3 ] = r0; opStack--; programCounter += 1; goto nextInstruction; @@ -549,7 +547,7 @@ nextInstruction2: Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) ); } #endif - } else if ( (unsigned)programCounter >= vm->codeLength ) { + } else if ( (unsigned)programCounter >= vm->instructionCount ) { Com_Error( ERR_DROP, "VM program counter out of range in OP_CALL" ); } else { programCounter = vm->instructionPointers[ programCounter ]; @@ -618,8 +616,11 @@ nextInstruction2: */ case OP_JUMP: - programCounter = r0; - programCounter = vm->instructionPointers[ programCounter ]; + if ( (unsigned)r0 >= vm->instructionCount ) + Com_Error( ERR_DROP, "VM program counter out of range in OP_JUMP" ); + + programCounter = vm->instructionPointers[ r0 ]; + opStack--; goto nextInstruction; diff --git a/src/qcommon/vm_local.h b/src/qcommon/vm_local.h index e1545d37..75ffee55 100644 --- a/src/qcommon/vm_local.h +++ b/src/qcommon/vm_local.h @@ -23,6 +23,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "q_shared.h" #include "qcommon.h" +#define OPSTACK_SIZE 256 +#define OPSTACK_MASK (OPSTACK_SIZE-1) + +// don't change +// Hardcoded in q3asm an reserved at end of bss +#define PROGRAM_STACK_SIZE 0x10000 +#define PROGRAM_STACK_MASK (PROGRAM_STACK_SIZE-1) + typedef enum { OP_UNDEF, @@ -147,7 +155,7 @@ struct vm_s { int codeLength; int *instructionPointers; - int instructionPointersLength; + int instructionCount; byte *dataBase; int dataMask; @@ -181,4 +189,3 @@ vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ); int VM_SymbolToValue( vm_t *vm, const char *symbol ); const char *VM_ValueToSymbol( vm_t *vm, int value ); void VM_LogSyscalls( int *args ); - diff --git a/src/qcommon/vm_x86_64.c b/src/qcommon/vm_x86_64.c index 02e7bf9c..3f950b2a 100644 --- a/src/qcommon/vm_x86_64.c +++ b/src/qcommon/vm_x86_64.c @@ -229,12 +229,47 @@ void emit(const char* fmt, ...) assemble_line(line, strlen(line)); } +#define CHECK_INSTR_REG(reg) \ + emit("cmpl $%u, %%"#reg, header->instructionCount); \ + emit("jb jmp_ok_i_%08x", instruction); \ + emit("movq $%lu, %%rax", (unsigned long)jmpviolation); \ + emit("callq *%%rax"); \ + emit("jmp_ok_i_%08x:", instruction); + +#define PREPARE_JMP(reg) \ + CHECK_INSTR_REG(reg) \ + emit("movq $%lu, %%rbx", (unsigned long)vm->instructionPointers); \ + emit("movl (%%rbx, %%rax, 4), %%eax"); \ + emit("addq %%r10, %%rax"); + +#define CHECK_INSTR(nr) \ + do { if(nr < 0 || nr >= header->instructionCount) { \ + Com_Error( ERR_DROP, \ + "%s: jump target 0x%x out of range at offset %d", __func__, nr, pc ); \ + } } while(0) + #define JMPIARG \ + CHECK_INSTR(iarg); \ emit("movq $%lu, %%rax", vm->codeBase+vm->instructionPointers[iarg]); \ emit("jmpq *%%rax"); +#define CONST_OPTIMIZE +#ifdef CONST_OPTIMIZE +#define MAYBE_EMIT_CONST() \ + if (got_const) \ + { \ + got_const = 0; \ + vm->instructionPointers[instruction-1] = assembler_get_code_size(); \ + emit("addq $4, %%rsi"); \ + emit("movl $%d, 0(%%rsi)", const_value); \ + } +#else +#define MAYBE_EMIT_CONST() +#endif + // integer compare and jump #define IJ(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $8, %%rsi"); \ emit("movl 4(%%rsi), %%eax"); \ emit("cmpl 8(%%rsi), %%eax"); \ @@ -244,6 +279,7 @@ void emit(const char* fmt, ...) #ifdef USE_X87 #define FJ(bits, op) \ + MAYBE_EMIT_CONST(); \ emit("subq $8, %%rsi");\ emit("flds 4(%%rsi)");\ emit("fcomps 8(%%rsi)");\ @@ -256,6 +292,7 @@ void emit(const char* fmt, ...) #else #define FJ(x, y) #define XJ(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $8, %%rsi");\ emit("movss 4(%%rsi), %%xmm0");\ emit("ucomiss 8(%%rsi), %%xmm0");\ @@ -266,12 +303,14 @@ void emit(const char* fmt, ...) #endif #define SIMPLE(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $4, %%rsi"); \ emit("movl 4(%%rsi), %%eax"); \ emit(op " %%eax, 0(%%rsi)"); #ifdef USE_X87 #define FSIMPLE(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $4, %%rsi"); \ emit("flds 0(%%rsi)"); \ emit(op " 4(%%rsi)"); \ @@ -280,6 +319,7 @@ void emit(const char* fmt, ...) #else #define FSIMPLE(op) #define XSIMPLE(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $4, %%rsi"); \ emit("movss 0(%%rsi), %%xmm0"); \ emit(op " 4(%%rsi), %%xmm0"); \ @@ -287,26 +327,28 @@ void emit(const char* fmt, ...) #endif #define SHIFT(op) \ + MAYBE_EMIT_CONST(); \ emit("subq $4, %%rsi"); \ emit("movl 4(%%rsi), %%ecx"); \ emit("movl 0(%%rsi), %%eax"); \ emit(op " %%cl, %%eax"); \ emit("movl %%eax, 0(%%rsi)"); -#if 1 -#define RANGECHECK(reg) \ - emit("andl $0x%x, %%" #reg, vm->dataMask); -#elif 0 -#define RANGECHECK(reg) \ - emit("pushl %%" #reg); \ - emit("andl $0x%x, %%" #reg, ~vm->dataMask); \ - emit("jz rangecheck_ok_i_%08x", instruction); \ - emit("int3"); \ - emit("rangecheck_ok_i_%08x:", instruction); \ - emit("popl %%" #reg); \ - emit("andl $0x%x, %%" #reg, vm->dataMask); +#ifdef DEBUG_VM +#define RANGECHECK(reg, bytes) \ + emit("movl %%" #reg ", %%ecx"); \ + emit("andl $0x%x, %%ecx", vm->dataMask &~(bytes-1)); \ + emit("cmpl %%" #reg ", %%ecx"); \ + emit("jz rc_ok_i_%08x", instruction); \ + emit("movq $%lu, %%rax", (unsigned long)memviolation); \ + emit("callq *%%rax"); \ + emit("rc_ok_i_%08x:", instruction); +#elif 1 +// check is too expensive, so just confine memory access +#define RANGECHECK(reg, bytes) \ + emit("andl $0x%x, %%" #reg, vm->dataMask &~(bytes-1)); #else -#define RANGECHECK(reg) +#define RANGECHECK(reg, bytes) #endif #ifdef DEBUG_VM @@ -337,6 +379,26 @@ static void block_copy_vm(unsigned dest, unsigned src, unsigned count) memcpy(currentVM->dataBase+dest, currentVM->dataBase+src, count); } +static void eop(void) +{ + Com_Error(ERR_DROP, "end of program reached without return!\n"); + exit(1); +} + +static void jmpviolation(void) +{ + Com_Error(ERR_DROP, "program tried to execute code outside VM\n"); + exit(1); +} + +#ifdef DEBUG_VM +static void memviolation(void) +{ + Com_Error(ERR_DROP, "program tried to access memory outside VM\n"); + exit(1); +} +#endif + /* ================= VM_Compile @@ -358,6 +420,9 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int pass; size_t compiledOfs = 0; + // const optimization + unsigned got_const = 0, const_value = 0; + gettimeofday(&tvstart, NULL); for (pass = 0; pass < 2; ++pass) { @@ -394,7 +459,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { vm->instructionPointers[instruction] = assembler_get_code_size(); /* store current instruction number in r15 for debugging */ -#if DEBUG_VM +#if DEBUG_VM0 emit("nop"); emit("movq $%d, %%r15", instruction); emit("nop"); @@ -428,31 +493,52 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { NOTIMPL(op); break; case OP_IGNORE: + MAYBE_EMIT_CONST(); emit("nop"); break; case OP_BREAK: + MAYBE_EMIT_CONST(); emit("int3"); break; case OP_ENTER: + MAYBE_EMIT_CONST(); emit("subl $%d, %%edi", iarg); - RANGECHECK(edi); break; case OP_LEAVE: + MAYBE_EMIT_CONST(); emit("addl $%d, %%edi", iarg); // get rid of stack frame emit("ret"); break; case OP_CALL: - emit("movl 0(%%rsi), %%eax"); // get instr from stack - emit("subq $4, %%rsi"); + RANGECHECK(edi, 4); emit("movl $%d, 0(%%r8, %%rdi, 1)", instruction+1); // save next instruction - emit("orl %%eax, %%eax"); - emit("jl callSyscall%d", instruction); - emit("movq $%lu, %%rbx", (unsigned long)vm->instructionPointers); - emit("movl (%%rbx, %%rax, 4), %%eax"); // load new relative jump address - emit("addq %%r10, %%rax"); - emit("callq *%%rax"); - emit("jmp i_%08x", instruction+1); - emit("callSyscall%d:", instruction); + if(got_const) + { + if ((int)const_value < 0) + goto emit_do_syscall; + + CHECK_INSTR(const_value); + emit("movq $%lu, %%rax", vm->codeBase+vm->instructionPointers[const_value]); + emit("callq *%%rax"); + got_const = 0; + break; + } + else + { + MAYBE_EMIT_CONST(); + emit("movl 0(%%rsi), %%eax"); // get instr from stack + emit("subq $4, %%rsi"); + + emit("orl %%eax, %%eax"); + emit("jl callSyscall%d", instruction); + + PREPARE_JMP(eax); + emit("callq *%%rax"); + + emit("jmp i_%08x", instruction+1); + emit("callSyscall%d:", instruction); + } +emit_do_syscall: // emit("fnsave 4(%%rsi)"); emit("push %%rsi"); emit("push %%rdi"); @@ -464,10 +550,15 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("andq $127, %%rbx"); // | emit("subq %%rbx, %%rsp"); // <-+ emit("push %%rbx"); - emit("negl %%eax"); // convert to actual number - emit("decl %%eax"); - // first argument already in rdi - emit("movq %%rax, %%rsi"); // second argument in rsi + if(got_const) { + got_const = 0; + emit("movq $%u, %%rsi", -1-const_value); // second argument in rsi + } else { + emit("negl %%eax"); // convert to actual number + emit("decl %%eax"); + // first argument already in rdi + emit("movq %%rax, %%rsi"); // second argument in rsi + } emit("movq $%lu, %%rax", (unsigned long)callAsmCall); emit("callq *%%rax"); emit("pop %%rbx"); @@ -483,28 +574,42 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { neednilabel = 1; break; case OP_PUSH: + MAYBE_EMIT_CONST(); emit("addq $4, %%rsi"); break; case OP_POP: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); break; case OP_CONST: + MAYBE_EMIT_CONST(); +#ifdef CONST_OPTIMIZE + got_const = 1; + const_value = iarg; +#else emit("addq $4, %%rsi"); emit("movl $%d, 0(%%rsi)", iarg); +#endif break; case OP_LOCAL: + MAYBE_EMIT_CONST(); emit("movl %%edi, %%ebx"); emit("addl $%d,%%ebx", iarg); emit("addq $4, %%rsi"); emit("movl %%ebx, 0(%%rsi)"); break; case OP_JUMP: - emit("movl 0(%%rsi), %%eax"); // get instr from stack - emit("subq $4, %%rsi"); - emit("movq $%lu, %%rbx", (unsigned long)vm->instructionPointers); - emit("movl (%%rbx, %%rax, 4), %%eax"); // load new relative jump address - emit("addq %%r10, %%rax"); - emit("jmp *%%rax"); + if(got_const) { + iarg = const_value; + got_const = 0; + JMPIARG; + } else { + emit("movl 0(%%rsi), %%eax"); // get instr from stack + emit("subq $4, %%rsi"); + + PREPARE_JMP(eax); + emit("jmp *%%rax"); + } break; case OP_EQ: IJ("jne"); @@ -543,6 +648,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { case OP_NEF: FJ(0x40, "jnz"); #ifndef USE_X87 + MAYBE_EMIT_CONST(); emit("subq $8, %%rsi"); emit("movss 4(%%rsi), %%xmm0"); emit("ucomiss 8(%%rsi), %%xmm0"); @@ -570,56 +676,64 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { XJ("jb"); break; case OP_LOAD1: + MAYBE_EMIT_CONST(); emit("movl 0(%%rsi), %%eax"); // get value from stack - RANGECHECK(eax); + RANGECHECK(eax, 1); emit("movb 0(%%r8, %%rax, 1), %%al"); // deref into eax emit("andq $255, %%rax"); emit("movl %%eax, 0(%%rsi)"); // store on stack break; case OP_LOAD2: + MAYBE_EMIT_CONST(); emit("movl 0(%%rsi), %%eax"); // get value from stack - RANGECHECK(eax); + RANGECHECK(eax, 2); emit("movw 0(%%r8, %%rax, 1), %%ax"); // deref into eax emit("movl %%eax, 0(%%rsi)"); // store on stack break; case OP_LOAD4: + MAYBE_EMIT_CONST(); emit("movl 0(%%rsi), %%eax"); // get value from stack - RANGECHECK(eax); // not a pointer!? + RANGECHECK(eax, 4); // not a pointer!? emit("movl 0(%%r8, %%rax, 1), %%eax"); // deref into eax emit("movl %%eax, 0(%%rsi)"); // store on stack break; case OP_STORE1: + MAYBE_EMIT_CONST(); emit("movl 0(%%rsi), %%eax"); // get value from stack emit("andq $255, %%rax"); emit("movl -4(%%rsi), %%ebx"); // get pointer from stack - RANGECHECK(ebx); + RANGECHECK(ebx, 1); emit("movb %%al, 0(%%r8, %%rbx, 1)"); // store in memory emit("subq $8, %%rsi"); break; case OP_STORE2: + MAYBE_EMIT_CONST(); emit("movl 0(%%rsi), %%eax"); // get value from stack emit("movl -4(%%rsi), %%ebx"); // get pointer from stack - RANGECHECK(ebx); + RANGECHECK(ebx, 2); emit("movw %%ax, 0(%%r8, %%rbx, 1)"); // store in memory emit("subq $8, %%rsi"); break; case OP_STORE4: + MAYBE_EMIT_CONST(); emit("movl -4(%%rsi), %%ebx"); // get pointer from stack - RANGECHECK(ebx); + RANGECHECK(ebx, 4); emit("movl 0(%%rsi), %%ecx"); // get value from stack emit("movl %%ecx, 0(%%r8, %%rbx, 1)"); // store in memory emit("subq $8, %%rsi"); break; case OP_ARG: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 4(%%rsi), %%eax"); // get value from stack emit("movl $0x%hhx, %%ebx", barg); emit("addl %%edi, %%ebx"); - RANGECHECK(ebx); + RANGECHECK(ebx, 4); emit("movl %%eax, 0(%%r8,%%rbx, 1)"); // store in args space break; case OP_BLOCK_COPY: + MAYBE_EMIT_CONST(); emit("subq $8, %%rsi"); emit("push %%rsi"); emit("push %%rdi"); @@ -639,6 +753,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { break; case OP_SEX8: + MAYBE_EMIT_CONST(); emit("movw 0(%%rsi), %%ax"); emit("andq $255, %%rax"); emit("cbw"); @@ -646,11 +761,13 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl %%eax, 0(%%rsi)"); break; case OP_SEX16: + MAYBE_EMIT_CONST(); emit("movw 0(%%rsi), %%ax"); emit("cwde"); emit("movl %%eax, 0(%%rsi)"); break; case OP_NEGI: + MAYBE_EMIT_CONST(); emit("negl 0(%%rsi)"); break; case OP_ADD: @@ -660,6 +777,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { SIMPLE("subl"); break; case OP_DIVI: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("cdq"); @@ -667,6 +785,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl %%eax, 0(%%rsi)"); break; case OP_DIVU: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("xorq %%rdx, %%rdx"); @@ -674,6 +793,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl %%eax, 0(%%rsi)"); break; case OP_MODI: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("xorl %%edx, %%edx"); @@ -682,6 +802,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl %%edx, 0(%%rsi)"); break; case OP_MODU: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("xorl %%edx, %%edx"); @@ -689,12 +810,14 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl %%edx, 0(%%rsi)"); break; case OP_MULI: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("imull 4(%%rsi)"); emit("movl %%eax, 0(%%rsi)"); break; case OP_MULU: + MAYBE_EMIT_CONST(); emit("subq $4, %%rsi"); emit("movl 0(%%rsi), %%eax"); emit("mull 4(%%rsi)"); @@ -710,6 +833,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { SIMPLE("xorl"); break; case OP_BCOM: + MAYBE_EMIT_CONST(); emit("notl 0(%%rsi)"); break; case OP_LSH: @@ -722,6 +846,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { SHIFT("shrl"); break; case OP_NEGF: + MAYBE_EMIT_CONST(); #ifdef USE_X87 emit("flds 0(%%rsi)"); emit("fchs"); @@ -748,6 +873,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { XSIMPLE("mulss"); break; case OP_CVIF: + MAYBE_EMIT_CONST(); #ifdef USE_X87 emit("filds 0(%%rsi)"); emit("fstps 0(%%rsi)"); @@ -758,6 +884,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { #endif break; case OP_CVFI: + MAYBE_EMIT_CONST(); #ifdef USE_X87 emit("flds 0(%%rsi)"); emit("fnstcw 4(%%rsi)"); @@ -775,8 +902,17 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { NOTIMPL(op); break; } + + } + if(got_const) { + Com_Error(ERR_DROP, "leftover const\n"); + } + + emit("movq $%lu, %%rax", (unsigned long)eop); + emit("callq *%%rax"); + } // pass loop assembler_init(0); @@ -789,6 +925,15 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { #ifdef DEBUG_VM fflush(qdasmout); fclose(qdasmout); + +#if 0 + strcpy(fn_d,vm->name); + strcat(fn_d, ".bin"); + qdasmout = fopen(fn_d, "w"); + fwrite(vm->codeBase, compiledOfs, 1, qdasmout); + fflush(qdasmout); + fclose(qdasmout); +#endif #endif if(vm->compiled) diff --git a/src/qcommon/vm_x86_64_assembler.c b/src/qcommon/vm_x86_64_assembler.c index 5747626a..c36c7a66 100644 --- a/src/qcommon/vm_x86_64_assembler.c +++ b/src/qcommon/vm_x86_64_assembler.c @@ -68,10 +68,15 @@ static void _crap(const char* func, const char* fmt, ...) static void emit1(unsigned char v) { + int writecnt; + if(assembler_pass) { out[compiledOfs++] = v; - if(fout) fwrite(&v, 1, 1, fout); + + if(fout) + writecnt = fwrite(&v, 1, 1, fout); + debug("%02hhx ", v); } else @@ -811,24 +816,34 @@ static void emit_call(const char* mnemonic, arg_t arg1, arg_t arg2, void* data) { u8 rex, modrm, sib; - if(arg1.type != T_REGISTER || arg2.type != T_NONE) + if((arg1.type != T_REGISTER && arg1.type != T_IMMEDIATE) || arg2.type != T_NONE) CRAP_INVALID_ARGS; - if(!arg1.absolute) - crap("call must be absolute"); + if(arg1.type == T_REGISTER) + { + if(!arg1.absolute) + crap("call must be absolute"); - if((arg1.v.reg & R_64) != R_64) - crap("register must be 64bit"); + if((arg1.v.reg & R_64) != R_64) + crap("register must be 64bit"); - arg1.v.reg ^= R_64; // no rex required for call + arg1.v.reg ^= R_64; // no rex required for call - compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1); + compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1); - modrm |= 0x2 << 3; + modrm |= 0x2 << 3; - if(rex) emit1(rex); - emit1(0xff); - emit1(modrm); + if(rex) emit1(rex); + emit1(0xff); + emit1(modrm); + } + else + { + if(!isu32(arg1.v.imm)) + crap("must be 32bit argument"); + emit1(0xe8); + emit4(arg1.v.imm); + } } @@ -875,7 +890,7 @@ static opparam_t params_or = { subcode: 1, rmcode: 0x09, }; static opparam_t params_and = { subcode: 4, rmcode: 0x21, }; static opparam_t params_sub = { subcode: 5, rmcode: 0x29, }; static opparam_t params_xor = { subcode: 6, rmcode: 0x31, }; -static opparam_t params_cmp = { subcode: 6, rmcode: 0x39, mrcode: 0x3b, }; +static opparam_t params_cmp = { subcode: 7, rmcode: 0x39, mrcode: 0x3b, }; static opparam_t params_dec = { subcode: 1, rcode: 0xff, rcode8: 0xfe, }; static opparam_t params_sar = { subcode: 7, rcode: 0xd3, rcode8: 0xd2, }; static opparam_t params_shl = { subcode: 4, rcode: 0xd3, rcode8: 0xd2, }; |