//***************************************************************************
//
//								ABLENV.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 ABLENV_H
	#include "ablenv.h"
#endif

#ifndef ABLDBUG_H
	#include "abldbug.h"
#endif

#ifndef ABLHEAP_H
	#include "ablheap.h"
#endif

#ifndef ABLFILE_H
	#include "ablfile.h"
#endif

#include "ai.hpp"
#include "mwobject.hpp"

#include <gameos\gameos.hpp>
//***************************************************************************
namespace ABL
{
	MechWarrior4::AI              *CurWarrior;

	StackItem       returnValue;
	bool          eofFlag = false;
	bool          ExitWithReturn = false;
	bool          ExitFromTacOrder = false;

//-------------------
// EXTERNAL variables
	extern UserHeapPtr    AblStackHeap;

	extern long       lineNumber;
	extern long       errorCount;
	extern long       execStatementCount;

	extern TokenCodeType  curToken;
	extern char       wordString[];
	extern SymTableNodePtr  symTableDisplay[];
	extern long       level;
	extern bool       blockFlag;
	extern BlockType    blockType;
	extern bool       printFlag;
	extern SymTableNodePtr  CurModuleIdPtr;
	extern SymTableNodePtr  CurRoutineIdPtr;
	extern long       CurModuleHandle;
	extern bool       CallModuleInit;
	extern long       FileNumber;

	extern Type       DummyType;
	extern char*      codeBuffer;
	extern char*      codeBufferPtr;
	extern StackItem*   stack;
//extern StackItem*		eternalStack;
	extern StackItemPtr   tos;
	extern StackItemPtr   stackFrameBasePtr;
	extern long       eternalOffset;

	extern TokenCodeType  statementStartList[];
	extern TokenCodeType  statementEndList[];
	extern TokenCodeType  declarationStartList[];

	extern char       tokenString[MAXLEN_TOKENSTRING];

	extern CharCodeType   charTable[256];
	extern FILE*      sourceFile;

	extern char       sourceBuffer[MAXLEN_SOURCELINE];
	extern long       bufferOffset;
	extern char*      bufferp;
	extern char*      tokenp;

	extern long       digitCount;
	extern bool       countError;

	extern bool       eofFlag;
	extern long       pageNumber;

	extern UserHeapPtr    systemHeap;
	extern UserHeapPtr    AblSymTableHeap;
	extern UserHeapPtr    AblStackHeap;
	extern UserHeapPtr    AblCodeHeap;

	extern SymTableNodePtr  SymTableDisplay[MAX_NESTING_LEVEL];
	extern TypePtr      IntegerTypePtr;
	extern TypePtr      RealTypePtr;
	extern TypePtr      BooleanTypePtr;

	extern StackItemPtr   StaticDataPtr;
	extern StackItem    returnValue;

	extern DebuggerPtr    debugger;
	extern long*      EternalVariablesSizes;

//-----------------------
// CLASS static variables
	long        NumModules = 0;

//-----------------
// GLOBAL variables
	ModuleEntryPtr    ModuleRegistry = NULL;
	ABLModulePtr*   ModuleInstanceRegistry = NULL;
	long        MaxModules = 0;
	long        NumModulesRegistered = 0;
	long        NumModuleInstances = 0;
	long        MaxWatchesPerModule = 20;
	long        MaxBreakPointsPerModule = 20;
	ABLModulePtr    CurModule = NULL;
	ABLModulePtr    CurLibrary = NULL;
	ABLModulePtr*   LibraryInstanceRegistry = NULL;
	long        MaxLibraries = 0;
	extern long     numLibrariesLoaded;

	extern long     NumExecutions;
	long        CallStackLevel = 0;

#define	MAX_PROFILE_LINELEN		128
#define MAX_PROFILE_LINES		256

	long NumProfileLogLines = 0;
	long TotalProfileLogLines = 0;
	char ProfileLogBuffer[MAX_PROFILE_LINES][MAX_PROFILE_LINELEN];
	bool ProfileLog = true;
	double ProfileLogFunctionTimeLimit = 0.005f;

//	UserFile     *UserFile::files[MAX_USER_FILES];

//***************************************************************************
// PROFILING LOG routines
//***************************************************************************

	void DumpProfileLog (void)
	{
		//----------------
		// Dump to file...
		if (ProfileLog)
		{
#if defined(LAB_ONLY)
			for (long i = 0; i < NumProfileLogLines; i++)
			{
				SPEWALWAYS (("afarrier",ProfileLogBuffer[i]));
			}
#endif
			NumProfileLogLines = 0;
		}
	}

//---------------------------------------------------------------------------

	void ABL_CloseProfileLog (void)
	{
		if (ProfileLog)
		{
			DumpProfileLog();
#if defined(LAB_ONLY)
			char s[512];
			sprintf(s, "\nABL:: Num Total Lines = %d\n", TotalProfileLogLines);
			SPEWALWAYS (("afarrier",s));
#endif
			ProfileLog = false;
			NumProfileLogLines = 0;
			TotalProfileLogLines = 0;
		}
	}

//---------------------------------------------------------------------------

	void ABL_OpenProfileLog (void)
	{
		if (ProfileLog)
			ABL_CloseProfileLog();

		NumProfileLogLines = 0;
		ProfileLog = true;
#if 0
		if (!ProfileLog)
			ABL_Fatal(0, " unable to malloc ABL ProfileLog ");
		if (ProfileLog->create("abl.log") != NO_ERR)
			ABL_Fatal(0, " unable to create ABL ProfileLog ");
#endif
	}

//---------------------------------------------------------------------------

	void ABL_AddToProfileLog (char* profileString)
	{
		if (NumProfileLogLines == MAX_PROFILE_LINES)
			DumpProfileLog();

		strncpy(ProfileLogBuffer[NumProfileLogLines], profileString, MAX_PROFILE_LINELEN - 1);
		ProfileLogBuffer[NumProfileLogLines][MAX_PROFILE_LINELEN] = NULL;
		NumProfileLogLines++;
		TotalProfileLogLines++;
	}
#if 0
//***************************************************************************
// USER FILE routines
//***************************************************************************

//---------------------------------------------------------------------------

	void UserFile::dump (void)
	{

		//----------------
		// Dump to file...
		for (long i = 0; i < numLines; i++)
			filePtr->writeString(lines[i]);
		numLines = 0;
	}

//---------------------------------------------------------------------------

	void UserFile::close (void)
	{

		if (filePtr && inUse)
		{
			dump();
			char s[512];
			sprintf(s, "\nNum Total Lines = %d\n", totalLines);
			filePtr->writeString(s);
			filePtr->close();
			inUse = false;
			numLines = 0;
			totalLines = 0;
		}
	}

//---------------------------------------------------------------------------

	long UserFile::open (MemoryStream *str)
	{
		stream = str;
		numLines = 0;
		totalLines = 0;

		inUse = true;
		return(0);
	}

//---------------------------------------------------------------------------

	void UserFile::write (char* s)
	{

		static char buffer[MAX_USER_FILE_LINELEN];

		if (numLines == MAX_USER_FILE_LINES)
			dump();

		if (strlen(s) > (MAX_USER_FILE_LINELEN - 1))
			s[MAX_USER_FILE_LINELEN - 1] = NULL;

		sprintf(buffer, "%s\n", s);
		strncpy(lines[numLines], buffer, MAX_USER_FILE_LINELEN - 1);
		numLines++;
		totalLines++;
	}

//---------------------------------------------------------------------------

	UserFile* UserFile::getNewFile (void)
	{

		long fileHandle = -1;
		for (long i = 0; i < MAX_USER_FILES; i++)
			if (!files[i]->inUse)
			{
				fileHandle = i;
				break;
			}
		return(files[i]);
	}

//---------------------------------------------------------------------------

	void UserFile::setup (void)
	{

		for (long i = 0; i < MAX_USER_FILES; i++)
		{
			files[i] = (UserFile*)gos_Malloc(sizeof(UserFile));
			files[i]->init();
			files[i]->handle = i;
			files[i]->inUse = false;
			files[i]->filePtr = new File;
			if (!files[i]->filePtr)
				ABL_Fatal(0, " ABL: Unable to malloc UserFiles ");
		}
	}

//---------------------------------------------------------------------------

	void UserFile::cleanup (void)
	{

		for (long i = 0; i < MAX_USER_FILES; i++)
		{
			if (files[i] && files[i]->inUse)
			{
				files[i]->close();
				files[i]->filePtr->close();
				delete files[i]->filePtr;
				files[i]->filePtr = NULL;
			}
		}
	}
#endif
//***************************************************************************
// MODULE REGISTRY routines
//***************************************************************************

	void initModuleRegistry (long maxModules)
	{

		//---------------------------------------------------------------------
		// First, set the max number of modules that may be loaded into the ABL
		// environment at a time...
		MaxModules = maxModules;

		//------------------------------
		// Create the module registry...
		ModuleRegistry = (ModuleEntryPtr)AblStackHeap->Malloc(sizeof(ModuleEntry) * MaxModules);
		if (!ModuleRegistry)
			ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc Module Registry ");
		memset(ModuleRegistry, 0, sizeof(ModuleEntry) * MaxModules);

		//-------------------------------------------------
		// Create the active (ABLModule) module registry...
		ModuleInstanceRegistry = (ABLModulePtr*)AblStackHeap->Malloc(sizeof(ABLModulePtr) * MaxModules);
		if (!ModuleInstanceRegistry)
			ABL_Fatal(0, " ABL: Unable to malloc AblStackHeap->Module Instance Registry ");
		memset(ModuleInstanceRegistry, 0, sizeof(ABLModulePtr) * MaxModules);
	}

//***************************************************************************

	void destroyModuleRegistry (void)
	{

		if (!AblStackHeap)
			return;

		//-----------------------------------------------------------
		// First, go through the registry, free'n each module and its
		// associated data...
		for (long i = 0; i < NumModulesRegistered; i++)
		{
			//destroyModule(ModuleRegistry[i].moduleIdPtr);
			AblStackHeap->Free(ModuleRegistry[i].fileName);
			ModuleRegistry[i].fileName = NULL;
			ModuleRegistry[i].moduleIdPtr = NULL;
			for (long j = 0; j < ModuleRegistry[i].numSourceFiles; j++)
			{
				AblStackHeap->Free(ModuleRegistry[i].sourceFiles[j]);
				ModuleRegistry[i].sourceFiles[j] = NULL;
			}
		}

		AblStackHeap->Free(ModuleRegistry);
		ModuleRegistry = NULL;

		AblStackHeap->Free(ModuleInstanceRegistry);
		ModuleInstanceRegistry = NULL;
	}

//***************************************************************************
// LIBRARY REGISTRY routines
//***************************************************************************

	void initLibraryRegistry (long maxLibraries)
	{

		//-----------------------------------------------------------------------
		// First, set the max number of libraries that may be loaded into the ABL
		// environment at a time...
		MaxLibraries = maxLibraries;

		//--------------------------------------------------
		// Create the active (ABLModule) library registry...
		LibraryInstanceRegistry = (ABLModulePtr*)AblStackHeap->Malloc(sizeof(ABLModulePtr) * MaxLibraries);
		if (!LibraryInstanceRegistry)
			ABL_Fatal(0, " ABL: Unable to malloc AblStackHeap->Library Instance Registry ");
		memset(LibraryInstanceRegistry, 0, sizeof(ABLModulePtr) * MaxLibraries);
	}

//***************************************************************************

	void destroyLibraryRegistry (void)
	{

		if (!AblStackHeap)
			return;

		//-----------------------------------------------------------------
		// Kinda need to do the same thing here as in the normal Registry.
		// Or leak o RAMA!!!!!!!
		// The actual data held by the pointer is removed in destroyModuleRegistry.
		// However, the classes holding the actual ABLModulePtr are then responsible
		// for deleting the ABLModulePtr.  Libraries have no owner class which, I'm guessing,
		// this class was supposed to do.  This class now does that!!!!
		// -fs		1/25/98
		for (long i=0;i<numLibrariesLoaded;i++)
		{
			delete LibraryInstanceRegistry[i];
			LibraryInstanceRegistry[i] = NULL;
		}

		AblStackHeap->Free(LibraryInstanceRegistry);
		LibraryInstanceRegistry = NULL;
	}

//***************************************************************************
// ABLMODULE class
//***************************************************************************
#if 0
	void* ABLModule::operator new (size_t mySize)
	{

		void* result = NULL;

		result = gos_Malloc(mySize);

		return(result);
	}

//---------------------------------------------------------------------------

	void ABLModule::operator delete (void* us)
	{

		gos_Free(us);
	}
#endif
//---------------------------------------------------------------------------

	ABLModule::reset (void)
	{
		long numStatics = ModuleRegistry[handle].numStaticVars;
		if (numStatics)
		{
			long* sizeList = ModuleRegistry[handle].sizeStaticVars;
			for (long i = 0; i < numStatics; i++)
				if (sizeList[i] > 0)
				{
					memset (staticData[i].address,0,sizeList[i]);
				}
				else
					staticData[i].integer	= 0;
		}

		SwitchStates("startstate");
	}

	long ABLModule::init (long moduleHandle)
	{

		init ();
		id = NumModules++;
		handle = moduleHandle;
		staticData = NULL;
		long numStatics = ModuleRegistry[handle].numStaticVars;
		if (numStatics)
		{
			staticData = (StackItemPtr)AblStackHeap->Malloc(sizeof(StackItem) * numStatics);
			if (!staticData)
			{
				char err[255];
				sprintf(err, "ABL: Unable to AblStackHeap->malloc staticData [Module %d]", id);
				ABL_Fatal(0, err);
			}
			long* sizeList = ModuleRegistry[handle].sizeStaticVars;
			for (long i = 0; i < numStatics; i++)
				if (sizeList[i] > 0)
				{
					staticData[i].address = (char*)AblStackHeap->Malloc(sizeList[i]);
					if (!staticData)
					{
						char err[255];
						sprintf(err, "ABL: Unable to AblStackHeap->malloc staticData address [Module %d]", id);
						ABL_Fatal(0, err);
					}
				}
				else
					staticData[i].integer	= 0;
		}
		ModuleRegistry[handle].numInstances++;
		initCalled = false;

		SymTableNodePtr moduleIdPtr = ModuleRegistry[handle].moduleIdPtr;
		if (moduleIdPtr->defn.key == DFN_FSM)
		{
			moduleIdPtr->defn.info.routine.state = NULL;
			curState = searchSymTable("startstate", moduleIdPtr->defn.info.routine.localSymTable);
			if (curState == NULL)
				ABL_Fatal (0,"Could not find start state");
		}

		//------------------------------------------------------
		// This Active Module is now on the instance registry...
		ModuleInstanceRegistry[NumModuleInstances++] = this;

		if (debugger)
		{
			watchManager = new WatchManager;
			if (!watchManager)
				ABL_Fatal(0, " Unable to AblStackHeap->malloc WatchManager ");
			long result = watchManager->init(MaxWatchesPerModule);
			if (result != NO_ERR)
				ABL_Fatal(0, " Unable to AblStackHeap->malloc WatchManager ");
			breakPointManager = new BreakPointManager;
			if (!breakPointManager)
				ABL_Fatal(0, " Unable to AblStackHeap->malloc BreakPointManager ");
			result = breakPointManager->init(MaxBreakPointsPerModule);
			if (result != NO_ERR)
				ABL_Fatal(0, " Unable to AblStackHeap->malloc BreakPointManager ");
		}

		//--------------------
		// Can this ever fail?
		return(NO_ERR);
	}

//---------------------------------------------------------------------------

	void ABLModule::write (MemoryStream *stream)
	{

		*stream << name;
		*stream << handle;
		if (curState)
		{
			*stream << true;
			*stream << strlen (curState->name);
			stream->WriteLine (curState->name);
		}
		else
			*stream << false;

		long numStatics = ModuleRegistry[handle].numStaticVars;
		long* sizeList = ModuleRegistry[handle].sizeStaticVars;
		for (long i = 0; i < numStatics; i++)
		{
			if (sizeList[i] > 0)
				stream->WriteBytes ((MemoryPtr)staticData[i].address, sizeList[i]);
			else
				stream->WriteBytes ((MemoryPtr)&staticData[i], sizeof(StackItem));
		}
	}

//---------------------------------------------------------------------------

	void ABLModule::read (MemoryStream *stream)
	{

		//----------------------------------------------------------------------------
		// If this is called on a newly init'd module, then it will do all appropriate
		// memory alloc, etc. If it's being called on a module that's already been
		// setup (via a call to init(moduleHandle)), then it simply loads the
		// module's data...
		char *state = NULL;
		char tname[MAX_ABLMODULE_NAME];
		stream->ReadLine (tname,MAX_ABLMODULE_NAME);
		Verify (!strcmp (name,tname));
		bool fresh = (id == -1);
		Verify (!fresh);
		if (fresh)
		{
			id = NumModules++;
			*stream >> handle;
//			handle = moduleFile->readLong();
			staticData = NULL;
		}
		else
		{
			#pragma warning (disable : 4189)
			long ignore;
			*stream >> ignore;
//			long ignore = moduleFile->readLong();
		}

		bool flag;
		*stream >> flag;
		if (flag)
		{
			int len;
			*stream >> len;
			state = new char[len+1];
			stream->ReadBytes (state,len+1);
		}

		long numStatics = ModuleRegistry[handle].numStaticVars;
		if (numStatics)
		{
			if (fresh)
			{
				staticData = (StackItemPtr)AblStackHeap->Malloc(sizeof(StackItem) * numStatics);
				if (!staticData)
				{
					char err[255];
					sprintf(err, "ABL: Unable to AblStackHeap->malloc staticData [Module %d]", id);
					ABL_Fatal(0, err);
				}
			}
			long* sizeList = ModuleRegistry[handle].sizeStaticVars;
			for (long i = 0; i < numStatics; i++)
				if (sizeList[i] > 0)
				{
					if (fresh)
					{
						staticData[i].address = (char*)AblStackHeap->Malloc(sizeList[i]);
						if (!staticData)
						{
							char err[255];
							sprintf(err, "ABL: Unable to AblStackHeap->malloc staticData address [Module %d]", id);
							ABL_Fatal(0, err);
						}
					}
					stream->ReadBytes ((MemoryPtr)staticData[i].address, sizeList[i]);
				}
				else
				{
					staticData[i].integer = 0;
					stream->ReadBytes ((MemoryPtr)&staticData[i], sizeof(StackItem));
				}
		}
		if (fresh)
		{
			ModuleRegistry[handle].numInstances++;
			initCalled = false;

			//------------------------------------------------------
			// This Active Module is now on the instance registry...
			ModuleInstanceRegistry[NumModuleInstances++] = this;

			if (debugger)
			{
				watchManager = new WatchManager;
				if (!watchManager)
					ABL_Fatal(0, " Unable to AblStackHeap->malloc WatchManager ");
				long result = watchManager->init(MaxWatchesPerModule);
				if (result != NO_ERR)
					ABL_Fatal(0, " Unable to AblStackHeap->malloc WatchManager ");
				breakPointManager = new BreakPointManager;
				if (!breakPointManager)
					ABL_Fatal(0, " Unable to AblStackHeap->malloc BreakPointManager ");
				result = breakPointManager->init(MaxBreakPointsPerModule);
				if (result != NO_ERR)
					ABL_Fatal(0, " Unable to AblStackHeap->malloc BreakPointManager ");
			}
		}

		SwitchStates (state);
		delete[] state;
	}

//---------------------------------------------------------------------------

	void ABLModule::setName (char* _name)
	{

		strncpy(name, _name, MAX_ABLMODULE_NAME);
		name[MAX_ABLMODULE_NAME] = NULL;
	}

//---------------------------------------------------------------------------
	void ABLModule::SwitchStates (const char *name)
	{
		SymTableNodePtr moduleIdPtr = ModuleRegistry[handle].moduleIdPtr;
		Verify (moduleIdPtr->defn.key == DFN_FSM);
		curState = searchSymTable(name, moduleIdPtr->defn.info.routine.localSymTable);
		if (curState == NULL)
			ABL_Fatal (0,"Could not find current state");
	}

	long ABLModule::execute (ABLParamPtr paramList)
	{

		CurModule = this;
		if (debugger)
			debugger->setModule(this);

		//--------------------------
		// Execute the ABL module...
		SymTableNodePtr moduleIdPtr = ModuleRegistry[handle].moduleIdPtr;

		//--------------------------------------------
		// Point to this module's static data space...
		StaticDataPtr = staticData;

		//---------------------------------
		// Init some important variables...
		CurModuleIdPtr = NULL;
		CurRoutineIdPtr = NULL;
		FileNumber = -1;
		errorCount = 0;
		execStatementCount = 0;
		NumExecutions++;

		//------------------
		// Init the stack...
		stackFrameBasePtr = tos = (stack + eternalOffset);

		//---------------------------------------
		// Initialize the module's stack frame...
		level = 1;
		CallStackLevel = 0;
		stackFrameBasePtr = tos + 1;

		//-------------------------
		// Function return value...
		pushInteger(0);
		//---------------
		// Static Link...
		pushAddress(NULL);
		//----------------
		// Dynamic Link...
		pushAddress(NULL);
		//------------------
		// Return Address...
		pushAddress(NULL);

		//initDebugger();

		//----------
		// Run it...

		if (paramList)
		{
			//------------------------------------------------------------------------------
			// NOTE: Currently, parameter passing of arrays is not functioning. This MUST be
			// done...
			long curParam = 0;
			for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(moduleIdPtr->defn.info.routine.params);
					formalIdPtr != NULL;
					formalIdPtr = formalIdPtr->next)
			{

				TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);

				if (formalIdPtr->defn.key == DFN_VALPARAM)
				{

					if (formalTypePtr == RealTypePtr)
					{
						if (paramList[curParam].type == ABL_PARAM_INTEGER)
						{
							//---------------------------------------------
							// Real formal parameter, but integer actual...
							pushReal((float)(paramList[curParam].integer));
						}
						else if (paramList[curParam].type == ABL_PARAM_REAL)
							pushReal(paramList[curParam].real);
					}
					else if (formalTypePtr == IntegerTypePtr)
					{
						if (paramList[curParam].type== ABL_PARAM_INTEGER)
							pushInteger(paramList[curParam].integer);
						else
							return(0);
					}

					//----------------------------------------------------------
					// Formal parameter is an array or record, so make a copy...
					if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
					{
						//------------------------------------------------------------------------------
						// The following is a little inefficient, but is kept this way to keep it clear.
						// Once it's verified to work, optimize...
						long size = formalTypePtr->size;
						char* dest = (char*)AblStackHeap->Malloc((size_t)size);
						if (!dest)
						{
							char err[255];
							sprintf(err, "ABL: Unable to AblStackHeap->malloc array parameter [Module %d]", id);
							ABL_Fatal(0, err);
						}
						char* src = tos->address;
						char* savePtr = dest;
						memcpy(dest, src, size);
						tos->address = savePtr;
					}
				}
				else
				{
					//-------------------------------
					// pass by reference parameter...
					if (formalTypePtr == RealTypePtr)
						pushAddress((Address)&(paramList[curParam].real));
					else if (formalTypePtr == IntegerTypePtr)
						pushAddress((Address)&(paramList[curParam].integer));
					else
						return(0);
					//SymTableNodePtr idPtr = getCodeSymTableNodePtr();
					//execVariable(idPtr, USE_REFPARAM);
				}
				curParam++;
			}
		}

		CurModuleHandle = handle;

		CallModuleInit = !initCalled;
		initCalled = true;

		Verify (moduleIdPtr->defn.info.routine.state == NULL);
		moduleIdPtr->defn.info.routine.state = curState;
		ABL::execute(moduleIdPtr);
		curState = moduleIdPtr->defn.info.routine.state;
		moduleIdPtr->defn.info.routine.state = NULL;

		memcpy(&returnVal, &returnValue, sizeof(StackItem));

		//-----------
		// Summary...
		return(execStatementCount);
	}

//---------------------------------------------------------------------------

	long ABLModule::execute (ABLParamPtr moduleParamList, SymTableNodePtr functionIdPtr)
	{

		CurModule = this;
		if (debugger)
			debugger->setModule(this);

		//--------------------------
		// Execute the ABL module...
		SymTableNodePtr moduleIdPtr = ModuleRegistry[handle].moduleIdPtr;

		//--------------------------------------------
		// Point to this module's static data space...
		StaticDataPtr = staticData;

		//---------------------------------
		// Init some important variables...
		CurModuleIdPtr = NULL;
		CurRoutineIdPtr = NULL;
		FileNumber = -1;
		errorCount = 0;
		execStatementCount = 0;
		NumExecutions++;

		//------------------
		// Init the stack...
		stackFrameBasePtr = tos = (stack + eternalOffset);

		//---------------------------------------
		// Initialize the module's stack frame...
		level = 1;
		CallStackLevel = 0;
		stackFrameBasePtr = tos + 1;

		//-------------------------
		// Function return value...
		pushInteger(0);
		//---------------
		// Static Link...
		pushAddress(NULL);
		//----------------
		// Dynamic Link...
		pushAddress(NULL);
		//------------------
		// Return Address...
		pushAddress(NULL);

		//initDebugger();

		//----------
		// Run it...

		if (moduleParamList)
		{
			//------------------------------------------------------------------------------
			// NOTE: Currently, parameter passing of arrays is not functioning. This MUST be
			// done...
			long curParam = 0;
			for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(moduleIdPtr->defn.info.routine.params);
					formalIdPtr != NULL;
					formalIdPtr = formalIdPtr->next)
			{

				TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);

				if (formalIdPtr->defn.key == DFN_VALPARAM)
				{

					if (formalTypePtr == RealTypePtr)
					{
						if (moduleParamList[curParam].type == ABL_PARAM_INTEGER)
						{
							//---------------------------------------------
							// Real formal parameter, but integer actual...
							pushReal((float)(moduleParamList[curParam].integer));
						}
						else if (moduleParamList[curParam].type == ABL_PARAM_REAL)
							pushReal(moduleParamList[curParam].real);
					}
					else if (formalTypePtr == IntegerTypePtr)
					{
						if (moduleParamList[curParam].type== ABL_PARAM_INTEGER)
							pushInteger(moduleParamList[curParam].integer);
						else
							return(0);
					}

					//----------------------------------------------------------
					// Formal parameter is an array or record, so make a copy...
					if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
					{
						//------------------------------------------------------------------------------
						// The following is a little inefficient, but is kept this way to keep it clear.
						// Once it's verified to work, optimize...
						long size = formalTypePtr->size;
						char* dest = (char*)AblStackHeap->Malloc((size_t)size);
						if (!dest)
						{
							char err[255];
							sprintf(err, "ABL: Unable to AblStackHeap->malloc array parameter [Module %d]", id);
							ABL_Fatal(0, err);
						}
						char* src = tos->address;
						char* savePtr = dest;
						memcpy(dest, src, size);
						tos->address = savePtr;
					}
				}
				else
				{
					//-------------------------------
					// pass by reference parameter...
					if (formalTypePtr == RealTypePtr)
						pushAddress((Address)&(moduleParamList[curParam].real));
					else if (formalTypePtr == IntegerTypePtr)
						pushAddress((Address)&(moduleParamList[curParam].integer));
					else
						return(0);
				}
				curParam++;
			}
		}

		CurModuleHandle = handle;

		CallModuleInit = !initCalled;
		initCalled = true;

		ABL::executeChild(moduleIdPtr, functionIdPtr);

		memcpy(&returnVal, &returnValue, sizeof(StackItem));

		//-----------
		// Summary...
		return(execStatementCount);
	}

//---------------------------------------------------------------------------

	SymTableNodePtr ABLModule::findSymbol (char* symbolName, SymTableNodePtr curFunction, bool searchLibraries)
	{

		if (curFunction)
		{
			SymTableNodePtr symbol = searchSymTable(strlwr(symbolName), curFunction->defn.info.routine.localSymTable);
			if (symbol)
				return(symbol);
		}

		SymTableNodePtr symbol = searchSymTable(strlwr(symbolName), ModuleRegistry[handle].moduleIdPtr->defn.info.routine.localSymTable);

		if (!symbol && searchLibraries)
		{
			for (long i = 0; i < ModuleRegistry[handle].numLibrariesUsed; i++)
			{
				symbol = searchSymTable(strlwr(symbolName), ModuleRegistry[ModuleRegistry[handle].librariesUsed[i]->handle].moduleIdPtr->defn.info.routine.localSymTable);
				if (symbol)
					break;
			}
		}
		if (!symbol)
		{
			symbol = searchSymTableDisplay(strlwr(symbolName));
		}

		return(symbol);
	}

//---------------------------------------------------------------------------

	SymTableNodePtr ABLModule::findFunction (char* functionName, bool searchLibraries)
	{

		SymTableNodePtr symbol = searchSymTable(functionName, ModuleRegistry[handle].moduleIdPtr->defn.info.routine.localSymTable);

		if (!symbol && searchLibraries)
		{
			for (long i = 0; i < ModuleRegistry[handle].numLibrariesUsed; i++)
			{
				symbol = searchSymTable(strlwr(functionName), ModuleRegistry[ModuleRegistry[handle].librariesUsed[i]->handle].moduleIdPtr->defn.info.routine.localSymTable);
				if (symbol)
					break;
			}
		}

		return(symbol);
	}

//---------------------------------------------------------------------------

	long ABLModule::setStaticInteger (char* name, long value)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(1);

		TypePtr baseTypePtr = baseType(symbol->typePtr);
		if (baseTypePtr != IntegerTypePtr)
			return(2);

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(3);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		*((long*)dataPtr) = value;
		return(0);
	}

//---------------------------------------------------------------------------

	long ABLModule::getStaticInteger (char* name)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(0xFFFFFFFF);

		TypePtr baseTypePtr = baseType(symbol->typePtr);
		if (baseTypePtr != IntegerTypePtr)
			return(0xFFFFFFFF);

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(0xFFFFFFFF);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		return(*((long*)dataPtr));
	}

//---------------------------------------------------------------------------

	long ABLModule::setStaticReal (char* name, float value)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(1);

		TypePtr baseTypePtr = baseType(symbol->typePtr);
		if (baseTypePtr != RealTypePtr)
			return(2);

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(3);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		*((float*)dataPtr) = value;
		return(0);
	}

//---------------------------------------------------------------------------

	float ABLModule::getStaticReal (char* name)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(-999999.0);

		TypePtr baseTypePtr = baseType(symbol->typePtr);
		if (baseTypePtr != RealTypePtr)
			return(-999999.0);

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(-999999.0);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		return(*((float*)dataPtr));
//	return(0);
	}

//---------------------------------------------------------------------------

	long ABLModule::setStaticIntegerArray (char* name, long numValues, long* values)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(1);

		// NOTE: This function is not dummy-proof. Essentially, this routine copies
		// the values data into the array's data space WITHOUT checking to make sure
		// the array really is an array (single or mult-dimensional) of reals. User
		// beware!
		/*
		TypePtr baseTypePtr = baseType(symbol->typePtr->info.array.elementTypePtr);
		if (baseTypePtr != IntegerTypePtr)
			return(2);
		*/

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(3);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		memcpy(dataPtr->address, values, 4 * numValues);

		return(0);
	}

//---------------------------------------------------------------------------

	long ABLModule::getStaticIntegerArray (char* name, long numValues, long* values)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(0);

		// NOTE: This function is not dummy-proof. Essentially, this routine copies
		// the values data into the array's data space WITHOUT checking to make sure
		// the array really is an array (single or mult-dimensional) of reals. User
		// beware!
		/*
		TypePtr baseTypePtr = baseType(symbol->typePtr->info.array.elementTypePtr);
		if (baseTypePtr != IntegerTypePtr)
			return(2);
		*/

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(0);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		memcpy(values, dataPtr->address, 4 * numValues);
		return(1);
	}

//---------------------------------------------------------------------------

	long ABLModule::setStaticRealArray (char* name, long numValues, float* values)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(1);

		// NOTE: This function is not dummy-proof. Essentially, this routine copies
		// the values data into the array's data space WITHOUT checking to make sure
		// the array really is an array (single or mult-dimensional) of reals. User
		// beware!
		/*
		TypePtr baseTypePtr = baseType(symbol->typePtr->info.array.elementTypePtr);
		if (baseTypePtr != RealTypePtr)
			return(2);
		*/

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(3);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		memcpy(dataPtr->address, values, 4 * numValues);

		return(0);
	}

//---------------------------------------------------------------------------

	long ABLModule::getStaticRealArray (char* name, long numValues, float* values)
	{

		SymTableNodePtr symbol = findSymbol(name);
		if (!symbol)
			return(0);

		// NOTE: This function is not dummy-proof. Essentially, this routine copies
		// the values data into the array's data space WITHOUT checking to make sure
		// the array really is an array (single or mult-dimensional) of reals. User
		// beware!
		/*
		TypePtr baseTypePtr = baseType(symbol->typePtr->info.array.elementTypePtr);
		if (baseTypePtr != RealTypePtr)
			return(2);
		*/

		if (symbol->defn.info.data.varType != VAR_TYPE_STATIC)
			return(0);

		StackItemPtr dataPtr = staticData + symbol->defn.info.data.offset;
		memcpy(values, dataPtr->address, 4 * numValues);

		return(1);
	}

//---------------------------------------------------------------------------

	char* ABLModule::getSourceFile (long fileNumber)
	{

		return(ModuleRegistry[handle].sourceFiles[fileNumber]);
	}

//---------------------------------------------------------------------------

	char* ABLModule::getSourceDirectory (long fileNumber, char* directory)
	{

		char* fileName = ModuleRegistry[handle].sourceFiles[fileNumber];
		long curChar = strlen(fileName);
		while ((curChar > -1) && (fileName[curChar] != '\\'))
			curChar--;
		if (curChar == -1)
			return(NULL);
		strcpy(directory, fileName);
		directory[curChar + 1] = NULL;
		return(directory);
	}

//---------------------------------------------------------------------------

	void buildRoutineList (SymTableNodePtr curSymbol, ModuleInfo* moduleInfo)
	{

		if (curSymbol)
		{
			buildRoutineList(curSymbol->left, moduleInfo);
			if (curSymbol->defn.key == DFN_FUNCTION)
			{
				if (moduleInfo->numRoutines < 1024)
				{
					strcpy(moduleInfo->routineInfo[moduleInfo->numRoutines].name, curSymbol->name);
					moduleInfo->routineInfo[moduleInfo->numRoutines].codeSegmentSize = curSymbol->defn.info.routine.codeSegmentSize;
					moduleInfo->numRoutines++;
				}
			}
			buildRoutineList(curSymbol->right, moduleInfo);
		}
	}

//---------------------------------------------------------------------------

	void ABLModule::getInfo (ModuleInfo* moduleInfo)
	{

		strcpy(moduleInfo->name, name);
		strcpy(moduleInfo->fileName, ModuleRegistry[handle].fileName);

		moduleInfo->numRoutines = 0;
		buildRoutineList(ModuleRegistry[handle].moduleIdPtr->defn.info.routine.localSymTable, moduleInfo);
		for (long i = 0; i < moduleInfo->numRoutines; i++)
			moduleInfo->totalCodeSegmentSize += moduleInfo->routineInfo[i].codeSegmentSize;

		moduleInfo->numStaticVars = ModuleRegistry[handle].numStaticVars;
		moduleInfo->totalSizeStaticVars = ModuleRegistry[handle].totalSizeStaticVars;

		if (ModuleRegistry[handle].sizeStaticVars)
		{
			long largest = 0;
			for (i = 0; i < moduleInfo->numStaticVars; i++)
			{
				if (ModuleRegistry[handle].sizeStaticVars[i] > ModuleRegistry[handle].sizeStaticVars[largest])
					largest = i;
			}
			moduleInfo->largestStaticVar.size = ModuleRegistry[handle].sizeStaticVars[largest];
			moduleInfo->largestStaticVar.name[0] = NULL;
		}
		else
		{
			moduleInfo->largestStaticVar.size = 0;
			moduleInfo->largestStaticVar.name[0] = NULL;
		}
	}

//---------------------------------------------------------------------------

	void ABLModule::destroy (void)
	{

		if ((id > -1) && ModuleInstanceRegistry)
		{
			//-----------------------------------------------
			// It's on the active registry, so pull it off...
			for (long i = 0; i < NumModuleInstances; i++)
				if (ModuleInstanceRegistry[i] == this)
				{
					ModuleInstanceRegistry[i] = ModuleInstanceRegistry[NumModuleInstances-1];
					ModuleInstanceRegistry[--NumModuleInstances] = NULL;
					break;
				}
		}

		if (watchManager)
		{
			delete watchManager;
			watchManager = NULL;
		}

		if (breakPointManager)
		{
			delete breakPointManager;
			breakPointManager = NULL;
		}

		if (staticData)
		{
			AblStackHeap->Free(staticData);
			staticData = NULL;
		}
	}

//***************************************************************************
// MISC routines
//***************************************************************************


	void ABLi_saveEnvironment (MemoryStream *stream)
	{

		*stream << numLibrariesLoaded;
		*stream << NumModulesRegistered;
		*stream << NumModules;
//		ablFile->writeLong(numLibrariesLoaded);
//		ablFile->writeLong(NumModulesRegistered);
//		ablFile->writeLong(NumModules);
		for (long i = 0; i < NumModulesRegistered; i++)
			*stream << ModuleRegistry[i].fileName;
//			ablFile->writeString(ModuleRegistry[i].fileName);
		*stream << 999;
//		ablFile->writeLong(999);
		for (i = 0; i < eternalOffset; i++)
		{
			StackItemPtr dataPtr = (StackItemPtr)stack + i;
			if (EternalVariablesSizes[i] > 0)
				stream->WriteBytes ((MemoryPtr)dataPtr->address, EternalVariablesSizes[i]);
//			ablFile->write((MemoryPtr)dataPtr->address, EternalVariablesSizes[i]);
			else
				stream->WriteBytes ((MemoryPtr)dataPtr, sizeof(StackItem));
//			ablFile->write((MemoryPtr)dataPtr, sizeof(StackItem));
		}
//blurt, each ai will do this
#if 0
		for (i = 0; i < NumModules; i++)
			ModuleInstanceRegistry[i]->write(ablFile);
#endif
	}

//---------------------------------------------------------------------------

	void ABLi_loadEnvironment (MemoryStream *stream)
	{

//		long numLibs = ablFile->readLong();
//		long numModsRegistered = ablFile->readLong();
//		long numMods = ablFile->readLong();
		long numLibs;
		long numModsRegistered;
		long numMods;
		*stream >> numLibs;
		*stream >> numModsRegistered;
		*stream >> numMods;

		for (long i = 0; i < numLibs; i++)
		{
			unsigned char fileName[1024];
			if (!stream->ReadLine ((char *) &(fileName[0]),1024))
			{
				char err[255];
				sprintf(err, "ABLi_loadEnvironment: Unable to read filename [Module %d]", i);
				ABL_Fatal(0, err);
			}
		}

		for (i = 0; i < (numModsRegistered - numLibs); i++)
		{
			unsigned char fileName[1024];
			if (!stream->ReadLine ((char *) &(fileName[0]),1024))
			{
				char err[255];
				sprintf(err, "ABLi_loadEnvironment: Unable to read filename [Module %d]", i);
				ABL_Fatal(0, err);
			}
		}
		#pragma warning (disable : 4189)
		long mark;
		*stream >> mark;
		for (i = 0; i < eternalOffset; i++)
		{
			StackItemPtr dataPtr = (StackItemPtr)stack + i;
			if (EternalVariablesSizes[i] > 0)
				stream->ReadBytes ((MemoryPtr)dataPtr->address, EternalVariablesSizes[i]);
			else
				stream->ReadBytes ((MemoryPtr)dataPtr, sizeof(StackItem));
		}
#if 0	// each ai will do this themselves
		for (i = 0; i < numMods; i++)
		{
			ABLModulePtr module = NULL;
			module = ModuleInstanceRegistry[i];
			module->read(ablFile);
		}
#endif
	}

//***************************************************************************


	}
