summaryrefslogtreecommitdiff
path: root/src/qcommon
diff options
context:
space:
mode:
Diffstat (limited to 'src/qcommon')
-rw-r--r--src/qcommon/common.c131
-rw-r--r--src/qcommon/cvar.c306
-rw-r--r--src/qcommon/files.c56
-rw-r--r--src/qcommon/net_ip.c6
-rw-r--r--src/qcommon/q_shared.h55
-rw-r--r--src/qcommon/qcommon.h9
-rw-r--r--src/qcommon/vm.c16
-rw-r--r--src/qcommon/vm_interpreted.c21
-rw-r--r--src/qcommon/vm_local.h11
-rw-r--r--src/qcommon/vm_x86_64.c229
-rw-r--r--src/qcommon/vm_x86_64_assembler.c41
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, };