Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

598 lines
16 KiB
C++

//***************************************************************************
//
// ABLEXEC.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef ABLGEN_H
#include "ablgen.h"
#endif
#ifndef ABLERR_H
#include "ablerr.h"
#endif
#ifndef ABLSCAN_H
#include "ablscan.h"
#endif
#ifndef ABLEXEC_H
#include "ablexec.h"
#endif
#ifndef ABLDBUG_H
#include "abldbug.h"
#endif
#ifndef ABLHEAP_H
#include "ablheap.h"
#endif
//***************************************************************************
namespace ABL
{
//--------
// GLOBALS
ABLExecFunctionType routineExecFunction = NULL;
char* codeBuffer = NULL;
char* codeBufferPtr = NULL;
char* codeSegmentPtr = NULL;
char* codeSegmentLimit = NULL;
char* statementStartPtr = NULL;
TokenCodeType codeToken;
long execLineNumber;
long execStatementCount = 0;
StackItem* stack = NULL;
StackItemPtr tos = NULL;
StackItemPtr stackFrameBasePtr = NULL;
StackItemPtr StaticDataPtr = NULL;
long* StaticVariablesSizes = NULL;
long* EternalVariablesSizes = NULL;
long eternalOffset = 0;
long MaxStaticVariables = 0;
long MaxEternalVariables = 0;
long NumStaticVariables = 0;
long CurModuleHandle = 0;
long MaxCodeBufferSize = 0;
bool CallModuleInit = false;
bool InOrdersBlock = false;
long MaxLoopIterations = 100001;
bool AssertEnabled = false;
bool PrintEnabled = true;
bool StringFunctionsEnabled = true;
bool DebugCodeEnabled = false;
bool IncludeDebugInfo = true;
bool ProfileABL = false;
bool Crunch = true;
//----------
// EXTERNALS
extern SymTableNodePtr CurRoutineIdPtr;
extern ModuleEntryPtr ModuleRegistry;
extern ABLModulePtr* ModuleInstanceRegistry;
extern ABLModulePtr CurModule;
extern ABLModulePtr CurLibrary;
extern TokenCodeType curToken;
extern long lineNumber;
extern long FileNumber;
extern long level;
extern TypePtr IntegerTypePtr;
extern TypePtr CharTypePtr;
extern TypePtr RealTypePtr;
extern TypePtr BooleanTypePtr;
extern UserHeapPtr AblCodeHeap;
extern UserHeapPtr AblStackHeap;
extern StackItem returnValue;
extern bool ExitWithReturn;
extern bool ExitFromTacOrder;
extern DebuggerPtr debugger;
//***************************************************************************
// CRUNCH/DECRUNCH routines
//***************************************************************************
void crunchToken (void)
{
if (!Crunch)
return;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
*codeBufferPtr = (char) curToken;
codeBufferPtr++;
}
}
//***************************************************************************
void uncrunchToken (void)
{
codeBufferPtr--;
}
//***************************************************************************
void crunchSymTableNodePtr (SymTableNodePtr nodePtr)
{
if (!Crunch)
return;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
SymTableNodePtr* nodePtrPtr = (SymTableNodePtr*)codeBufferPtr;
*nodePtrPtr = nodePtr;
codeBufferPtr += sizeof(SymTableNodePtr);
}
}
//***************************************************************************
void uncrunchSymTableNodePtr (void)
{
//-------------------------------------
// Pull sym table node ptr off the buffer
codeBufferPtr -= sizeof(SymTableNodePtr);
}
//***************************************************************************
void crunchStatementMarker (void)
{
if (!Crunch)
return;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
char saveCode = *(--codeBufferPtr);
*codeBufferPtr = (char)TKN_STATEMENT_MARKER;
codeBufferPtr++;
if (IncludeDebugInfo)
{
*((unsigned char*)codeBufferPtr) = (unsigned char)FileNumber;
codeBufferPtr += sizeof(unsigned char);
*((long*)codeBufferPtr) = lineNumber;
codeBufferPtr += sizeof(long);
}
*codeBufferPtr = saveCode;
codeBufferPtr++;
}
}
//***************************************************************************
void uncrunchStatementMarker (void)
{
//-------------------------
// Pull code off the buffer
codeBufferPtr--;
//-------------------------------
// Pull debug info off the buffer
if (IncludeDebugInfo)
codeBufferPtr -= (sizeof(unsigned char) + sizeof(long));
//-------------------------------------
// Pull statement marker off the buffer
codeBufferPtr--;
}
//***************************************************************************
char* crunchAddressMarker (Address address)
{
if (!Crunch)
return(NULL);
char* saveCodeBufferPtr = NULL;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
char saveCode = *(--codeBufferPtr);
*codeBufferPtr = (char)TKN_ADDRESS_MARKER;
codeBufferPtr++;
saveCodeBufferPtr = codeBufferPtr;
*((Address*)codeBufferPtr) = address;
codeBufferPtr += sizeof(Address);
*codeBufferPtr = saveCode;
codeBufferPtr++;
}
return(saveCodeBufferPtr);
}
//***************************************************************************
char* fixupAddressMarker (Address address)
{
if (!Crunch)
return(NULL);
char* oldAddress = *((Address*)address);
*((long*)address) = codeBufferPtr - address;
return(oldAddress);
}
//***************************************************************************
void crunchInteger (long value)
{
if (!Crunch)
return;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
*((long*)codeBufferPtr) = value;
codeBufferPtr += sizeof(long);
}
}
//***************************************************************************
void crunchOffset (Address address)
{
if (!Crunch)
return;
if (codeBufferPtr >= (codeBuffer + MaxCodeBufferSize - 100))
{
STOP(("ABL SyntaxError #%d", ABL_ERR_SYNTAX_CODE_SEGMENT_OVERFLOW));
}
else
{
*((long*)codeBufferPtr) = address - codeBufferPtr;
codeBufferPtr += sizeof(long);
}
}
//***************************************************************************
char* createCodeSegment (long& codeSegmentSize)
{
codeSegmentSize = codeBufferPtr - codeBuffer + 1;
char* codeSegment = (char*)AblCodeHeap->Malloc(codeSegmentSize);
if (!codeSegment)
ABL_Fatal(0, " ABL: Unable to AblCodeHeap->malloc code segment ");
for (long i = 0; i < codeSegmentSize; i++)
codeSegment[i] = codeBuffer[i];
codeBufferPtr = codeBuffer;
return(codeSegment);
}
//***************************************************************************
void pushStackFrameHeader (long oldLevel, long newLevel)
{
//-----------------------------------
// Make space for the return value...
pushInteger(0);
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
//----------------------------------------------------------------------
// STATIC LINK
// Currently, let's not allow functions defined within functions. Assume
// the old scope level equals the new scope level, for now.
if (newLevel == -1)
{
//--------------------------------------------------------------------
// Calling a library function, so push a NULL static link since
// it's scope is in a different module than the calling function.
// Note that global variables in libraries should be STATIC, otherwise
// they're never in scope! Weird "feature" which we may want
// to fix later...
pushAddress(NULL);
}
else if (newLevel == oldLevel + 1)
{
//----------------------------------------------------------------
// Calling a routine nested within the caller, so push the pointer
// to the caller's stack frame. In ABL, as currently defined
// (2/22/96), this should only be when a module's code section is
// calling a function...
pushAddress((Address)headerPtr);
}
else if (newLevel == oldLevel)
{
//---------------------------------------------------------------
// Calling a function at the same scope level. We like that! Push
// pointer to stack frame of their common parent...
pushAddress(headerPtr->staticLink.address);
}
else
{
//-------------------------------------------------------
// Oops. We don't want nested functions, for now, in ABL.
STOP(("ABL SyntaxError #%d", ABL_ERR_RUNTIME_NESTED_FUNCTION_CALL));
}
pushAddress((Address)stackFrameBasePtr);
//---------------------------
// Push the return address...
pushAddress(0);
}
//***************************************************************************
void allocLocal (TypePtr typePtr)
{
if (typePtr == IntegerTypePtr)
pushInteger(0);
else if (typePtr == RealTypePtr)
pushReal((float)0.0);
else if (typePtr == BooleanTypePtr)
pushByte(0);
else if (typePtr == CharTypePtr)
pushByte(0);
else
switch (typePtr->form)
{
case FRM_ENUM:
pushInteger(0);
break;
// NOTE: We currently are not supporting sub ranges, until
// we really want 'em...
// case FRM_SUBRANGE:
// allocLocal(typePtr->info.subrange.rangeTypePtr);
// break;
case FRM_ARRAY:
char* ptr = (char*)AblStackHeap->Malloc(typePtr->size);
if (!ptr)
ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc local array ");
pushAddress((Address)ptr);
break;
}
}
//***************************************************************************
void freeLocal (SymTableNodePtr idPtr)
{
//---------------------------------------
// Frees data allocated on local stack...
TypePtr typePtr = (TypePtr)(idPtr->typePtr);
StackItemPtr itemPtr = NULL;
if (((typePtr->form == FRM_ARRAY) /* || (typePtr->form == FRM_RECORD)*/) &&
(idPtr->defn.key != DFN_REFPARAM))
{
switch (idPtr->defn.info.data.varType)
{
case VAR_TYPE_NORMAL:
itemPtr = stackFrameBasePtr + idPtr->defn.info.data.offset;
break;
// case VAR_TYPE_ETERNAL:
// itemPtr = stack + idPtr->defn.info.data.offset;
// break;
// case VAR_TYPE_STATIC:
// itemPtr = StaticDataPtr + idPtr->defn.info.data.offset;
// break;
}
if (!itemPtr)
runtimeError(0);
else
AblStackHeap->Free(itemPtr->address);
}
}
//***************************************************************************
// FUNCTION ENTRY/EXIT routines
//***************************************************************************
void routineEntry (SymTableNodePtr routineIdPtr)
{
if (debugger)
debugger->traceRoutineEntry(routineIdPtr);
memset(&returnValue, 0, sizeof(StackItem));
//------------------------------
// Switch to new code segment...
codeSegmentPtr = routineIdPtr->defn.info.routine.codeSegment;
//----------------------------------------------
// Allocate local variables onto system stack...
for (SymTableNodePtr varIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.locals);
varIdPtr != NULL;
varIdPtr = varIdPtr->next)
if (varIdPtr->defn.info.data.varType == VAR_TYPE_NORMAL)
allocLocal((TypePtr)(varIdPtr->typePtr));
}
//***************************************************************************
void routineExit (SymTableNodePtr routineIdPtr)
{
if (debugger)
debugger->traceRoutineExit(routineIdPtr);
//-----------------------------------------
// De-alloc parameters & local variables...
for (SymTableNodePtr idPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);
idPtr != NULL;
idPtr = idPtr->next)
freeLocal(idPtr);
for (idPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.locals);
idPtr != NULL;
idPtr = idPtr->next)
if (idPtr->defn.info.data.varType == VAR_TYPE_NORMAL)
freeLocal(idPtr);
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
codeSegmentPtr = headerPtr->returnAddress.address;
if (routineIdPtr->typePtr == NULL)
tos = stackFrameBasePtr - 1;
else
tos = stackFrameBasePtr;
stackFrameBasePtr = (StackItemPtr)headerPtr->dynamicLink.address;
}
//***************************************************************************
void execute (SymTableNodePtr routineIdPtr)
{
SymTableNodePtr thisRoutineIdPtr = CurRoutineIdPtr;
CurRoutineIdPtr = routineIdPtr;
routineEntry(routineIdPtr);
//----------------------------------------------------
// Now, search this module for the function we want...
if (CallModuleInit)
{
CallModuleInit = false;
SymTableNodePtr initFunctionIdPtr = searchSymTable("init", ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.info.routine.localSymTable);
if (initFunctionIdPtr)
{
execRoutineCall(initFunctionIdPtr);
//-------------------------------------------------------------------------
// Since we're calling the function directly, we need to compensate for the
// codeSegmentPtr being incremented by 1 in the normal execRoutineCall...
codeSegmentPtr--;
}
}
if (routineIdPtr->defn.key == DFN_FSM)
{
execRoutineCall (routineIdPtr->defn.info.routine.state);
codeSegmentPtr--;
routineExit(routineIdPtr);
}
else
{
getCodeToken();
execStatement();
//---------------------------------------------
// In case we exited with a return statement...
ExitWithReturn = false;
ExitFromTacOrder = false;
routineExit(routineIdPtr);
}
CurRoutineIdPtr = thisRoutineIdPtr;
}
//***************************************************************************
void executeChild (SymTableNodePtr routineIdPtr, SymTableNodePtr childRoutineIdPtr)
{
// THIS DOES NOT SUPPORT CALLING FUNCTIONS WITH PARAMETERS YET!
SymTableNodePtr thisRoutineIdPtr = CurRoutineIdPtr;
CurRoutineIdPtr = routineIdPtr;
routineEntry(routineIdPtr);
//----------------------------------------------------
// Now, search this module for the function we want...
SymTableNodePtr initFunctionIdPtr = NULL;
if (CallModuleInit)
{
CallModuleInit = false;
initFunctionIdPtr = searchSymTable("init", routineIdPtr->defn.info.routine.localSymTable);
if (initFunctionIdPtr)
{
execRoutineCall(initFunctionIdPtr);
//-------------------------------------------------------------------------
// Since we're calling the function directly, we need to compensate for the
// codeSegmentPtr being incremented by 1 in the normal execRoutineCall...
codeSegmentPtr--;
}
}
if (initFunctionIdPtr != childRoutineIdPtr)
{
//-----------------------------------------------------------------------
// If we're calling the module's init function, and we just did above,
// don't call it again! That's why we make the check on the above line...
execRoutineCall(childRoutineIdPtr);
//-------------------------------------------------------------------------
// Since we're calling the function directly, we need to compensate for the
// codeSegmentPtr being incremented by 1 in the normal execRoutineCall...
codeSegmentPtr--;
}
//---------------------------------------------
// In case we exited with a return statement...
ExitWithReturn = false;
ExitFromTacOrder = false;
routineExit(routineIdPtr);
CurRoutineIdPtr = thisRoutineIdPtr;
}
//***************************************************************************
// MISC routines
//***************************************************************************
}