📄 xvm.cpp
字号:
g_Scripts [ iThreadIndex ].FuncTable.iSize = iFuncTableSize;
// Allocate the table
if ( ! ( g_Scripts [ iThreadIndex ].FuncTable.pFuncs = ( Func * ) malloc ( iFuncTableSize * sizeof ( Func ) ) ) )
return XS_LOAD_ERROR_OUT_OF_MEMORY;
// Read each function
for ( int iCurrFuncIndex = 0; iCurrFuncIndex < iFuncTableSize; ++ iCurrFuncIndex )
{
// Read the entry point (4 bytes)
int iEntryPoint;
fread ( & iEntryPoint, 4, 1, pScriptFile );
// Read the parameter count (1 byte)
int iParamCount = 0;
fread ( & iParamCount, 1, 1, pScriptFile );
// Read the local data size (4 bytes)
int iLocalDataSize;
fread ( & iLocalDataSize, 4, 1, pScriptFile );
// Calculate the stack size
int iStackFrameSize = iParamCount + 1 + iLocalDataSize;
// Read the function name length (1 byte)
int iFuncNameLength = 0;
fread ( & iFuncNameLength, 1, 1, pScriptFile );
// Read the function name (N bytes) and append a null-terminator
fread ( & g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].pstrName, iFuncNameLength, 1, pScriptFile );
g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].pstrName [ iFuncNameLength ] = '\0';
// Write everything to the function table
g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].iEntryPoint = iEntryPoint;
g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].iParamCount = iParamCount;
g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].iLocalDataSize = iLocalDataSize;
g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iCurrFuncIndex ].iStackFrameSize = iStackFrameSize;
}
// ---- Read the host API call table
// Read the host API call count
fread ( & g_Scripts [ iThreadIndex ].HostAPICallTable.iSize, 4, 1, pScriptFile );
// Allocate the table
if ( ! ( g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls = ( char ** ) malloc ( g_Scripts [ iThreadIndex ].HostAPICallTable.iSize * sizeof ( char * ) ) ) )
return XS_LOAD_ERROR_OUT_OF_MEMORY;
// Read each host API call
for ( int iCurrCallIndex = 0; iCurrCallIndex < g_Scripts [ iThreadIndex ].HostAPICallTable.iSize; ++ iCurrCallIndex )
{
// Read the host API call string size (1 byte)
int iCallLength = 0;
fread ( & iCallLength, 1, 1, pScriptFile );
// Allocate space for the string plus the null terminator in a temporary pointer
char * pstrCurrCall;
if ( ! ( pstrCurrCall = ( char * ) malloc ( iCallLength + 1 ) ) )
return XS_LOAD_ERROR_OUT_OF_MEMORY;
// Read the host API call string data and append the null terminator
fread ( pstrCurrCall, iCallLength, 1, pScriptFile );
pstrCurrCall [ iCallLength ] = '\0';
// Assign the temporary pointer to the table
g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls [ iCurrCallIndex ] = pstrCurrCall;
}
// ---- Close the input file
fclose ( pScriptFile );
// The script is fully loaded and ready to go, so set the active flag
g_Scripts [ iThreadIndex ].iIsActive = TRUE;
// Reset the script
XS_ResetScript ( iThreadIndex );
// Return a success code
return XS_LOAD_OK;
}
/******************************************************************************************
*
* XS_UnloadScript ()
*
* Unloads a script from memory.
*/
void XS_UnloadScript ( int iThreadIndex )
{
// Exit if the script isn't active
if ( ! g_Scripts [ iThreadIndex ].iIsActive )
return;
// ---- Free The instruction stream
// First check to see if any instructions have string operands, and free them if they
// do
for ( int iCurrInstrIndex = 0; iCurrInstrIndex < g_Scripts [ iThreadIndex ].InstrStream.iSize; ++ iCurrInstrIndex )
{
// Make a local copy of the operand count and operand list
int iOpCount = g_Scripts [ iThreadIndex ].InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount;
Value * pOpList = g_Scripts [ iThreadIndex ].InstrStream.pInstrs [ iCurrInstrIndex ].pOpList;
// Loop through each operand and free its string pointer
for ( int iCurrOpIndex = 0; iCurrOpIndex < iOpCount; ++ iCurrOpIndex )
if ( pOpList [ iCurrOpIndex ].pstrStringLiteral )
pOpList [ iCurrOpIndex ].pstrStringLiteral;
}
// Now free the stream itself
if ( g_Scripts [ iThreadIndex ].InstrStream.pInstrs )
free ( g_Scripts [ iThreadIndex ].InstrStream.pInstrs );
// ---- Free the runtime stack
// Free any strings that are still on the stack
for ( int iCurrElmtnIndex = 0; iCurrElmtnIndex < g_Scripts [ iThreadIndex ].Stack.iSize; ++ iCurrElmtnIndex )
if ( g_Scripts [ iThreadIndex ].Stack.pElmnts [ iCurrElmtnIndex ].iType == OP_TYPE_STRING )
free ( g_Scripts [ iThreadIndex ].Stack.pElmnts [ iCurrElmtnIndex ].pstrStringLiteral );
// Now free the stack itself
if ( g_Scripts [ iThreadIndex ].Stack.pElmnts )
free ( g_Scripts [ iThreadIndex ].Stack.pElmnts );
// ---- Free the function table
if ( g_Scripts [ iThreadIndex ].FuncTable.pFuncs )
free ( g_Scripts [ iThreadIndex ].FuncTable.pFuncs );
// --- Free the host API call table
// First free each string in the table individually
for ( int iCurrCallIndex = 0; iCurrCallIndex < g_Scripts [ iThreadIndex ].HostAPICallTable.iSize; ++ iCurrCallIndex )
if ( g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls [ iCurrCallIndex ] )
free ( g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls [ iCurrCallIndex ] );
// Now free the table itself
if ( g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls )
free ( g_Scripts [ iThreadIndex ].HostAPICallTable.ppstrCalls );
}
/******************************************************************************************
*
* XS_ResetScript ()
*
* Resets the script. This function accepts a thread index rather than relying on the
* currently active thread, because scripts can (and will) need to be reset arbitrarily.
*/
void XS_ResetScript ( int iThreadIndex )
{
// Get _Main ()'s function index in case we need it
int iMainFuncIndex = g_Scripts [ iThreadIndex ].iMainFuncIndex;
// If the function table is present, set the entry point
if ( g_Scripts [ iThreadIndex ].FuncTable.pFuncs )
{
// If _Main () is present, read _Main ()'s index of the function table to get its
// entry point
if ( g_Scripts [ iThreadIndex ].iIsMainFuncPresent )
{
g_Scripts [ iThreadIndex ].InstrStream.iCurrInstr = g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iMainFuncIndex ].iEntryPoint;
}
}
// Clear the stack
g_Scripts [ iThreadIndex ].Stack.iTopIndex = 0;
g_Scripts [ iThreadIndex ].Stack.iFrameIndex = 0;
// Set the entire stack to null
for ( int iCurrElmntIndex = 0; iCurrElmntIndex < g_Scripts [ iThreadIndex ].Stack.iSize; ++ iCurrElmntIndex )
g_Scripts [ iThreadIndex ].Stack.pElmnts [ iCurrElmntIndex ].iType = OP_TYPE_NULL;
// Unpause the script
g_Scripts [ iThreadIndex ].iIsPaused = FALSE;
// Allocate space for the globals
PushFrame ( iThreadIndex, g_Scripts [ iThreadIndex ].iGlobalDataSize );
// If _Main () is present, push its stack frame (plus one extra stack element to
// compensate for the function index that usually sits on top of stack frames and
// causes indices to start from -2)
PushFrame ( iThreadIndex, g_Scripts [ iThreadIndex ].FuncTable.pFuncs [ iMainFuncIndex ].iLocalDataSize + 1 );
}
/******************************************************************************************
*
* XS_RunScripts ()
*
* Runs the currenty loaded script array for a given timeslice duration.
*/
void XS_RunScripts ( int iTimesliceDur )
{
// Begin a loop that runs until a keypress. The instruction pointer has already been
// initialized with a prior call to ResetScripts (), so execution can begin
// Create a flag that instructions can use to break the execution loop
int iExitExecLoop = FALSE;
// Create a variable to hold the time at which the main timeslice started
int iMainTimesliceStartTime = GetCurrTime ();
// Create a variable to hold the current time
int iCurrTime;
while ( TRUE )
{
// Check to see if all threads have terminated, and if so, break the execution
// cycle
int iIsStillActive = FALSE;
for ( int iCurrThreadIndex = 0; iCurrThreadIndex < MAX_THREAD_COUNT; ++ iCurrThreadIndex )
{
if ( g_Scripts [ iCurrThreadIndex ].iIsActive && g_Scripts [ iCurrThreadIndex ].iIsRunning )
iIsStillActive = TRUE;
}
if ( ! iIsStillActive )
break;
// Update the current time
iCurrTime = GetCurrTime ();
// Check for a context switch if the threading mode is set for multithreading
if ( g_iCurrThreadMode == THREAD_MODE_MULTI )
{
// If the current thread's timeslice has elapsed, or if it's terminated switch
// to the next valid thread
if ( iCurrTime > g_iCurrThreadActiveTime + g_Scripts [ g_iCurrThread ].iTimesliceDur ||
! g_Scripts [ g_iCurrThread ].iIsRunning )
{
// Loop until the next thread is found
while ( TRUE )
{
// Move to the next thread in the array
++ g_iCurrThread;
// If we're past the end of the array, loop back around
if ( g_iCurrThread >= MAX_THREAD_COUNT )
g_iCurrThread = 0;
// If the thread we've chosen is active and running, break the loop
if ( g_Scripts [ g_iCurrThread ].iIsActive && g_Scripts [ g_iCurrThread ].iIsRunning )
break;
}
// Reset the timeslice
g_iCurrThreadActiveTime = iCurrTime;
}
}
// Is the script currently paused?
if ( g_Scripts [ g_iCurrThread ].iIsPaused )
{
// Has the pause duration elapsed yet?
if ( iCurrTime >= g_Scripts [ g_iCurrThread ].iPauseEndTime )
{
// Yes, so unpause the script
g_Scripts [ g_iCurrThread ].iIsPaused = FALSE;
}
else
{
// No, so skip this iteration of the execution cycle
continue;
}
}
// Make a copy of the instruction pointer to compare later
int iCurrInstr = g_Scripts [ g_iCurrThread ].InstrStream.iCurrInstr;
// Get the current opcode
int iOpcode = g_Scripts [ g_iCurrThread ].InstrStream.pInstrs [ iCurrInstr ].iOpcode;
// Execute the current instruction based on its opcode, as long as we aren't
// currently paused
switch ( iOpcode )
{
// ---- Binary Operations
// All of the binary operation instructions (move, arithmetic, and bitwise)
// are combined into a single case that keeps us from having to rewrite the
// otherwise redundant operand resolution and result storage phases over and
// over. We then use an additional switch block to determine which operation
// should be performed.
// Move
case INSTR_MOV:
// Arithmetic Operations
case INSTR_ADD:
case INSTR_SUB:
case INSTR_MUL:
case INSTR_DIV:
case INSTR_MOD:
case INSTR_EXP:
// Bitwise Operations
case INSTR_AND:
case INSTR_OR:
case INSTR_XOR:
case INSTR_SHL:
case INSTR_SHR:
{
// Get a local copy of the destination operand (operand index 0)
Value Dest = ResolveOpValue ( 0 );
// Get a local copy of the source operand (operand index 1)
Value Source = ResolveOpValue ( 1 );
// Depending on the instruction, perform a binary operation
switch ( iOpcode )
{
// Move
case INSTR_MOV:
// Skip cases where the two operands are the same
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -