📄 vm.c
字号:
FS_FreeFile( header );
return vm;
}
/*
================
VM_Create
If image ends in .qvm it will be interpreted, otherwise
it will attempt to load as a system dll
================
*/
#define STACK_SIZE 0x20000
vm_t *VM_Create( const char *module, int (*systemCalls)(int *),
vmInterpret_t interpret ) {
vm_t *vm;
vmHeader_t *header;
int length;
int dataLength;
int i, remaining;
char filename[MAX_QPATH];
if ( !module || !module[0] || !systemCalls ) {
Com_Error( ERR_FATAL, "VM_Create: bad parms" );
}
remaining = Hunk_MemoryRemaining();
// see if we already have the VM
for ( i = 0 ; i < MAX_VM ; i++ ) {
if (!Q_stricmp(vmTable[i].name, module)) {
vm = &vmTable[i];
return vm;
}
}
// find a free vm
for ( i = 0 ; i < MAX_VM ; i++ ) {
if ( !vmTable[i].name[0] ) {
break;
}
}
if ( i == MAX_VM ) {
Com_Error( ERR_FATAL, "VM_Create: no free vm_t" );
}
vm = &vmTable[i];
Q_strncpyz( vm->name, module, sizeof( vm->name ) );
vm->systemCall = systemCalls;
// never allow dll loading with a demo
if ( interpret == VMI_NATIVE ) {
if ( Cvar_VariableValue( "fs_restrict" ) ) {
interpret = VMI_COMPILED;
}
}
if ( interpret == VMI_NATIVE ) {
// try to load as a system dll
Com_Printf( "Loading dll file %s.\n", vm->name );
vm->dllHandle = Sys_LoadDll( module, vm->fqpath , &vm->entryPoint, VM_DllSyscall );
if ( vm->dllHandle ) {
return vm;
}
Com_Printf( "Failed to load dll, looking for qvm.\n" );
interpret = VMI_COMPILED;
}
// load the image
Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
Com_Printf( "Loading vm file %s.\n", filename );
length = FS_ReadFile( filename, (void **)&header );
if ( !header ) {
Com_Printf( "Failed.\n" );
VM_Free( vm );
return NULL;
}
// byte swap the header
for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
((int *)header)[i] = LittleLong( ((int *)header)[i] );
}
// validate
if ( header->vmMagic != VM_MAGIC
|| header->bssLength < 0
|| header->dataLength < 0
|| header->litLength < 0
|| header->codeLength <= 0 ) {
VM_Free( vm );
Com_Error( ERR_FATAL, "%s has bad header", filename );
}
// round up to next power of 2 so all data operations can
// be mask protected
dataLength = header->dataLength + header->litLength + header->bssLength;
for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
}
dataLength = 1 << i;
// allocate zero filled space for initialized and uninitialized data
vm->dataBase = Hunk_Alloc( dataLength, h_high );
vm->dataMask = dataLength - 1;
// copy the intialized data
Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
// byte swap the longs
for ( i = 0 ; i < header->dataLength ; i += 4 ) {
*(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
}
// 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 );
// copy or compile the instructions
vm->codeLength = header->codeLength;
if ( interpret >= VMI_COMPILED ) {
vm->compiled = qtrue;
VM_Compile( vm, header );
} else {
vm->compiled = qfalse;
VM_PrepareInterpreter( vm, header );
}
// free the original file
FS_FreeFile( header );
// load the map file
VM_LoadSymbols( vm );
// the stack is implicitly at the end of the image
vm->programStack = vm->dataMask + 1;
vm->stackBottom = vm->programStack - STACK_SIZE;
Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining());
return vm;
}
/*
==============
VM_Free
==============
*/
void VM_Free( vm_t *vm ) {
if ( vm->dllHandle ) {
Sys_UnloadDll( vm->dllHandle );
Com_Memset( vm, 0, sizeof( *vm ) );
}
#if 0 // now automatically freed by hunk
if ( vm->codeBase ) {
Z_Free( vm->codeBase );
}
if ( vm->dataBase ) {
Z_Free( vm->dataBase );
}
if ( vm->instructionPointers ) {
Z_Free( vm->instructionPointers );
}
#endif
Com_Memset( vm, 0, sizeof( *vm ) );
currentVM = NULL;
lastVM = NULL;
}
void VM_Clear(void) {
int i;
for (i=0;i<MAX_VM; i++) {
if ( vmTable[i].dllHandle ) {
Sys_UnloadDll( vmTable[i].dllHandle );
}
Com_Memset( &vmTable[i], 0, sizeof( vm_t ) );
}
currentVM = NULL;
lastVM = NULL;
}
void *VM_ArgPtr( int intValue ) {
if ( !intValue ) {
return NULL;
}
// bk001220 - currentVM is missing on reconnect
if ( currentVM==NULL )
return NULL;
if ( currentVM->entryPoint ) {
return (void *)(currentVM->dataBase + intValue);
}
else {
return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
}
}
void *VM_ExplicitArgPtr( vm_t *vm, int intValue ) {
if ( !intValue ) {
return NULL;
}
// bk010124 - currentVM is missing on reconnect here as well?
if ( currentVM==NULL )
return NULL;
//
if ( vm->entryPoint ) {
return (void *)(vm->dataBase + intValue);
}
else {
return (void *)(vm->dataBase + (intValue & vm->dataMask));
}
}
/*
==============
VM_Call
Upon a system call, the stack will look like:
sp+32 parm1
sp+28 parm0
sp+24 return value
sp+20 return address
sp+16 local1
sp+14 local0
sp+12 arg1
sp+8 arg0
sp+4 return stack
sp return address
An interpreted function will immediately execute
an OP_ENTER instruction, which will subtract space for
locals from sp
==============
*/
#define MAX_STACK 256
#define STACK_MASK (MAX_STACK-1)
int QDECL VM_Call( vm_t *vm, int callnum, ... ) {
vm_t *oldVM;
int r;
int i;
int args[16];
va_list ap;
if ( !vm ) {
Com_Error( ERR_FATAL, "VM_Call with NULL vm" );
}
oldVM = currentVM;
currentVM = vm;
lastVM = vm;
if ( vm_debugLevel ) {
Com_Printf( "VM_Call( %i )\n", callnum );
}
// if we have a dll loaded, call it directly
if ( vm->entryPoint ) {
//rcg010207 - see dissertation at top of VM_DllSyscall() in this file.
va_start(ap, callnum);
for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) {
args[i] = va_arg(ap, int);
}
va_end(ap);
r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13], args[14], args[15]);
} else if ( vm->compiled ) {
r = VM_CallCompiled( vm, &callnum );
} else {
r = VM_CallInterpreted( vm, &callnum );
}
if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL
currentVM = oldVM;
return r;
}
//=================================================================
static int QDECL VM_ProfileSort( const void *a, const void *b ) {
vmSymbol_t *sa, *sb;
sa = *(vmSymbol_t **)a;
sb = *(vmSymbol_t **)b;
if ( sa->profileCount < sb->profileCount ) {
return -1;
}
if ( sa->profileCount > sb->profileCount ) {
return 1;
}
return 0;
}
/*
==============
VM_VmProfile_f
==============
*/
void VM_VmProfile_f( void ) {
vm_t *vm;
vmSymbol_t **sorted, *sym;
int i;
double total;
if ( !lastVM ) {
return;
}
vm = lastVM;
if ( !vm->numSymbols ) {
return;
}
sorted = Z_Malloc( vm->numSymbols * sizeof( *sorted ) );
sorted[0] = vm->symbols;
total = sorted[0]->profileCount;
for ( i = 1 ; i < vm->numSymbols ; i++ ) {
sorted[i] = sorted[i-1]->next;
total += sorted[i]->profileCount;
}
qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
for ( i = 0 ; i < vm->numSymbols ; i++ ) {
int perc;
sym = sorted[i];
perc = 100 * (float) sym->profileCount / total;
Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName );
sym->profileCount = 0;
}
Com_Printf(" %9.0f total\n", total );
Z_Free( sorted );
}
/*
==============
VM_VmInfo_f
==============
*/
void VM_VmInfo_f( void ) {
vm_t *vm;
int i;
Com_Printf( "Registered virtual machines:\n" );
for ( i = 0 ; i < MAX_VM ; i++ ) {
vm = &vmTable[i];
if ( !vm->name[0] ) {
break;
}
Com_Printf( "%s : ", vm->name );
if ( vm->dllHandle ) {
Com_Printf( "native\n" );
continue;
}
if ( vm->compiled ) {
Com_Printf( "compiled on load\n" );
} else {
Com_Printf( "interpreted\n" );
}
Com_Printf( " code length : %7i\n", vm->codeLength );
Com_Printf( " table length: %7i\n", vm->instructionPointersLength );
Com_Printf( " data length : %7i\n", vm->dataMask + 1 );
}
}
/*
===============
VM_LogSyscalls
Insert calls to this while debugging the vm compiler
===============
*/
void VM_LogSyscalls( int *args ) {
static int callnum;
static FILE *f;
if ( !f ) {
f = fopen("syscalls.log", "w" );
}
callnum++;
fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, args - (int *)currentVM->dataBase,
args[0], args[1], args[2], args[3], args[4] );
}
#ifdef oDLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM
int VM_CallCompiled( vm_t *vm, int *args ) {
return(0);
}
void VM_Compile( vm_t *vm, vmHeader_t *header ) {}
#endif // DLL_ONLY
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -