mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			497 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			497 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// DR. ROBOTNIK'S RING RACERS
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// Copyright (C) 2016 by James Haley, David Hill, et al. (Team Eternity)
 | 
						|
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
 | 
						|
// Copyright (C) 2022 by Kart Krew
 | 
						|
//
 | 
						|
// This program is free software distributed under the
 | 
						|
// terms of the GNU General Public License, version 2.
 | 
						|
// See the 'LICENSE' file for more details.
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
/// \file  k_acs.c
 | 
						|
/// \brief Action Code Script implementation using ACSVM
 | 
						|
 | 
						|
#include "k_acs.h"
 | 
						|
 | 
						|
#include "doomtype.h"
 | 
						|
#include "doomdef.h"
 | 
						|
#include "doomstat.h"
 | 
						|
#include "z_zone.h"
 | 
						|
#include "w_wad.h"
 | 
						|
#include "i_system.h"
 | 
						|
#include "r_defs.h"
 | 
						|
#include "r_state.h"
 | 
						|
#include "p_polyobj.h"
 | 
						|
#include "taglist.h"
 | 
						|
 | 
						|
#include "CAPI/BinaryIO.h"
 | 
						|
#include "CAPI/Environment.h"
 | 
						|
#include "CAPI/Module.h"
 | 
						|
#include "CAPI/PrintBuf.h"
 | 
						|
#include "CAPI/Scope.h"
 | 
						|
#include "CAPI/String.h"
 | 
						|
#include "CAPI/Thread.h"
 | 
						|
 | 
						|
static ACSVM_Environment *ACSenv = NULL;
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	ACSVM_Environment *ACS_GetEnvironment(void)
 | 
						|
 | 
						|
		See header file for description.
 | 
						|
--------------------------------------------------*/
 | 
						|
ACSVM_Environment *ACS_GetEnvironment(void)
 | 
						|
{
 | 
						|
	return ACSenv;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_EnvBadAlloc(ACSVM_Environment *env, char const *what)
 | 
						|
 | 
						|
		ACSVM Environment hook. Runs in case of a memory
 | 
						|
		allocation failure occuring. Environment state
 | 
						|
		afterwards is unusable; the only thing safe to do
 | 
						|
		is using ACSVM_FreeEnvironment.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data.
 | 
						|
		what - Error string.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_EnvBadAlloc(ACSVM_Environment *env, char const *what)
 | 
						|
{
 | 
						|
	(void)env;
 | 
						|
 | 
						|
	CONS_Alert(CONS_ERROR, "Error allocating memory for ACS (%s)\n", what);
 | 
						|
 | 
						|
	if (env == ACSenv)
 | 
						|
	{
 | 
						|
		// Restart the main environment.
 | 
						|
		ACS_Shutdown();
 | 
						|
		I_RemoveExitFunc(ACS_Shutdown); // Since ACS_Init will add it again.
 | 
						|
		ACS_Init();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_EnvReadError(ACSVM_Environment *env, char const *what)
 | 
						|
 | 
						|
		ACSVM Environment hook. Runs when an ACS module
 | 
						|
		fails to read. Environment state should be safe
 | 
						|
		afterwards.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data.
 | 
						|
		what - Error string.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_EnvReadError(ACSVM_Environment *env, char const *what)
 | 
						|
{
 | 
						|
	(void)env;
 | 
						|
	CONS_Alert(CONS_WARNING, "Error reading ACS module (%s)\n", what);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_EnvSerialError(ACSVM_Environment *env, char const *what)
 | 
						|
 | 
						|
		ACSVM Environment hook. Runs when the ACS state
 | 
						|
		fails to save or load. Environment state is
 | 
						|
		safe in that it shouldn't be causing crashes,
 | 
						|
		but it is indeterminate.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data.
 | 
						|
		what - Error string.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_EnvSerialError(ACSVM_Environment *env, char const *what)
 | 
						|
{
 | 
						|
	(void)env;
 | 
						|
	CONS_Alert(CONS_WARNING, "Error serializing ACS state (%s)\n", what);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_EnvConstruct(ACSVM_Environment *env)
 | 
						|
 | 
						|
		ACSVM Environment hook. Runs when the
 | 
						|
		environment is initally created.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data to construct.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_EnvConstruct(ACSVM_Environment *env)
 | 
						|
{
 | 
						|
	ACSVM_GlobalScope *global = ACSVM_Environment_GetGlobalScope(env, 0);
 | 
						|
 | 
						|
	// Activate global scope immediately, since we don't want it off.
 | 
						|
	// Not that we're adding any modules to it, though. :p
 | 
						|
	ACSVM_GlobalScope_SetActive(global, true);
 | 
						|
 | 
						|
	// Add the data & function pointers
 | 
						|
	// See also:
 | 
						|
	// - https://doomwiki.org/wiki/ACS0_instruction_set
 | 
						|
	// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeData.hpp
 | 
						|
	// - https://github.com/DavidPH/ACSVM/blob/master/ACSVM/CodeList.hpp
 | 
						|
 | 
						|
	//  0 to 56: Implemented by ACSVM
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   57, "",        ACSVM_Code_CallFunc, 2, ACSVM_Environment_AddCallFunc(env, ACS_CF_Random));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   58, "WW",      ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_Random));
 | 
						|
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   61, "",        ACSVM_Code_CallFunc, 1, ACSVM_Environment_AddCallFunc(env, ACS_CF_TagWait));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   62, "W",       ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_TagWait));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   63, "",        ACSVM_Code_CallFunc, 1, ACSVM_Environment_AddCallFunc(env, ACS_CF_PolyWait));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   64, "W",       ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_PolyWait));
 | 
						|
	// 69 to 79: Implemented by ACSVM
 | 
						|
 | 
						|
	// 81 to 82: Implemented by ACSVM
 | 
						|
 | 
						|
	// 84 to 85: Implemented by ACSVM
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   86, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_EndPrint));
 | 
						|
	// 87 to 89: Implemented by ACSVM
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   90, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_PlayerCount));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   91, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_GameType));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   92, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_GameSpeed));
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,   93, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_Timer));
 | 
						|
	// 136 to 137: Implemented by ACSVM
 | 
						|
 | 
						|
	// 157: Implemented by ACSVM
 | 
						|
 | 
						|
	// 167 to 173: Implemented by ACSVM
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,  174, "BB",      ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_Random));
 | 
						|
	// 175 to 179: Implemented by ACSVM
 | 
						|
 | 
						|
	// 181 to 189: Implemented by ACSVM
 | 
						|
 | 
						|
	// 203 to 217: Implemented by ACSVM
 | 
						|
 | 
						|
	// 225 to 243: Implemented by ACSVM
 | 
						|
 | 
						|
	// 253: Implemented by ACSVM
 | 
						|
 | 
						|
	// 256 to 257: Implemented by ACSVM
 | 
						|
 | 
						|
	// 263: Implemented by ACSVM
 | 
						|
	ACSVM_Environment_AddCodeDataACS0(env,  270, "",        ACSVM_Code_CallFunc, 0, ACSVM_Environment_AddCallFunc(env, ACS_CF_EndPrint));
 | 
						|
	// 273 to 275: Implemented by ACSVM
 | 
						|
 | 
						|
	// 291 to 325: Implemented by ACSVM
 | 
						|
 | 
						|
	// 330: Implemented by ACSVM
 | 
						|
 | 
						|
	// 349 to 361: Implemented by ACSVM
 | 
						|
 | 
						|
	// 363 to 380: Implemented by ACSVM
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_EnvLoadModule(ACSVM_Environment *env, ACSVM_Module *module)
 | 
						|
 | 
						|
		ACSVM Environment hook. Runs when a ACS
 | 
						|
		module is being loaded.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data.
 | 
						|
		module - The ACS module being loaded.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true when successful, otherwise false.
 | 
						|
		Returning false will also call the
 | 
						|
		ACS_EnvReadError hook.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_EnvLoadModule(ACSVM_Environment *env, ACSVM_Module *module)
 | 
						|
{
 | 
						|
	ACSVM_ModuleName name = ACSVM_Module_GetName(module);
 | 
						|
	const char *str = ACSVM_String_GetStr(name.s);
 | 
						|
 | 
						|
	size_t lumpLen = 0;
 | 
						|
 | 
						|
	ACSVM_Byte *data = NULL;
 | 
						|
	size_t size = 0;
 | 
						|
 | 
						|
	bool ret = false;
 | 
						|
 | 
						|
	(void)env;
 | 
						|
 | 
						|
	if (name.i == (size_t)LUMPERROR)
 | 
						|
	{
 | 
						|
		// No lump given for module.
 | 
						|
		CONS_Alert(CONS_ERROR, "Bad lump for ACS module \"%s\"\n", str);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	lumpLen = W_LumpLength(name.i);
 | 
						|
 | 
						|
	if (W_IsLumpWad(name.i) == true || lumpLen == 0)
 | 
						|
	{
 | 
						|
		// The lump given is a virtual resource.
 | 
						|
		// Try to grab it from there.
 | 
						|
		virtres_t *vRes = vres_GetMap(name.i);
 | 
						|
		virtlump_t *vLump = vres_Find(vRes, "BEHAVIOR");
 | 
						|
 | 
						|
		CONS_Printf("Attempting to load ACS module from map's virtual resource...\n");
 | 
						|
 | 
						|
		if (vLump != NULL)
 | 
						|
		{
 | 
						|
			data = Z_Calloc(vLump->size, PU_STATIC, NULL);
 | 
						|
			memcpy(data, vLump->data, vLump->size);
 | 
						|
			size = vLump->size;
 | 
						|
			CONS_Printf("Successfully found BEHAVIOR lump.\n");
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			CONS_Printf("No BEHAVIOR lump found.\n");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		// It's a real lump.
 | 
						|
		data = Z_Calloc(lumpLen, PU_STATIC, NULL);
 | 
						|
		W_ReadLump(name.i, data);
 | 
						|
		size = lumpLen;
 | 
						|
		CONS_Printf("Loading ACS module directly from lump.\n");
 | 
						|
	}
 | 
						|
 | 
						|
	if (data != NULL && size > 0)
 | 
						|
	{
 | 
						|
		CONS_Printf("Reading bytecode of ACS module...\n");
 | 
						|
		ret = ACSVM_Module_ReadBytecode(module, data, size);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		// Unlike Hexen, BEHAVIOR is not required.
 | 
						|
		// Simply ignore in this instance.
 | 
						|
		CONS_Printf("No data received, ignoring...\n");
 | 
						|
		ret = true;
 | 
						|
	}
 | 
						|
 | 
						|
	Z_Free(data);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static bool ACS_EnvCheckTag(ACSVM_Environment const *env, ACSVM_Word type, ACSVM_Word tag)
 | 
						|
 | 
						|
		ACSVM Environment hook. Ran to determine
 | 
						|
		whenever or not a thread should still be
 | 
						|
		waiting on a tag movement.
 | 
						|
		See: TagWait, PolyWait.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		env - The ACS environment data.
 | 
						|
		type - The kind of level data we're waiting on. See also: acs_tagType_e.
 | 
						|
		tag - The tag of said level data.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		true when the tag is done moving and
 | 
						|
		execution can continue, or false to keep
 | 
						|
		the thread paused.
 | 
						|
--------------------------------------------------*/
 | 
						|
static bool ACS_EnvCheckTag(ACSVM_Environment const *env, ACSVM_Word type, ACSVM_Word tag)
 | 
						|
{
 | 
						|
	(void)env;
 | 
						|
 | 
						|
	switch (type)
 | 
						|
	{
 | 
						|
		case ACS_TAGTYPE_SECTOR:
 | 
						|
		{
 | 
						|
			INT32 secnum = -1;
 | 
						|
 | 
						|
			TAG_ITER_SECTORS(tag, secnum)
 | 
						|
			{
 | 
						|
				sector_t *sec = §ors[secnum];
 | 
						|
 | 
						|
				if (sec->floordata != NULL || sec->ceilingdata != NULL)
 | 
						|
				{
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
 | 
						|
		case ACS_TAGTYPE_POLYOBJ:
 | 
						|
		{
 | 
						|
			const polyobj_t *po = Polyobj_GetForNum(tag);
 | 
						|
			return (po == NULL || po->thinker == NULL);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	void ACS_Init(void)
 | 
						|
 | 
						|
		See header file for description.
 | 
						|
--------------------------------------------------*/
 | 
						|
void ACS_Init(void)
 | 
						|
{
 | 
						|
	// Initialize ACS on engine start-up.
 | 
						|
	ACSVM_EnvironmentFuncs funcs = {0};
 | 
						|
 | 
						|
	funcs.bad_alloc = ACS_EnvBadAlloc;
 | 
						|
	funcs.readError = ACS_EnvReadError;
 | 
						|
	funcs.serialError = ACS_EnvSerialError;
 | 
						|
	funcs.ctor = ACS_EnvConstruct;
 | 
						|
	funcs.loadModule = ACS_EnvLoadModule;
 | 
						|
	funcs.checkTag = ACS_EnvCheckTag;
 | 
						|
 | 
						|
	ACSenv = ACSVM_AllocEnvironment(&funcs, NULL);
 | 
						|
 | 
						|
	I_AddExitFunc(ACS_Shutdown);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	void ACS_Shutdown(void)
 | 
						|
 | 
						|
		See header file for description.
 | 
						|
--------------------------------------------------*/
 | 
						|
void ACS_Shutdown(void)
 | 
						|
{
 | 
						|
	// Delete ACS environment.
 | 
						|
	ACSVM_FreeEnvironment(ACSenv);
 | 
						|
	ACSenv = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_ResetHub(ACSVM_GlobalScope *global)
 | 
						|
 | 
						|
		Shortcut function to quickly free the
 | 
						|
		only hub scope Ring Racers uses.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		global - The global scope to free the hub from.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_ResetHub(ACSVM_GlobalScope *global)
 | 
						|
{
 | 
						|
	ACSVM_HubScope *hub = ACSVM_GlobalScope_GetHubScope(global, 0);
 | 
						|
	ACSVM_GlobalScope_FreeHubScope(global, hub);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	static void ACS_ResetMap(ACSVM_HubScope *hub)
 | 
						|
 | 
						|
		Shortcut function to quickly free the
 | 
						|
		only map scope Ring Racers uses.
 | 
						|
 | 
						|
	Input Arguments:-
 | 
						|
		hub - The hub scope to free the map from.
 | 
						|
 | 
						|
	Return:-
 | 
						|
		N/A
 | 
						|
--------------------------------------------------*/
 | 
						|
static void ACS_ResetMap(ACSVM_HubScope *hub)
 | 
						|
{
 | 
						|
	ACSVM_MapScope *map = ACSVM_HubScope_GetMapScope(hub, 0);
 | 
						|
	ACSVM_HubScope_FreeMapScope(hub, map);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	void ACS_LoadLevelScripts(size_t mapID)
 | 
						|
 | 
						|
		See header file for description.
 | 
						|
--------------------------------------------------*/
 | 
						|
void ACS_LoadLevelScripts(size_t mapID)
 | 
						|
{
 | 
						|
	ACSVM_Environment *env = ACSenv;
 | 
						|
	ACSVM_StringTable *strTab = ACSVM_Environment_GetStringTable(env);
 | 
						|
 | 
						|
	ACSVM_GlobalScope *global = NULL;
 | 
						|
	ACSVM_HubScope *hub = NULL;
 | 
						|
	ACSVM_MapScope *map = NULL;
 | 
						|
 | 
						|
	ACSVM_Module **modules = NULL;
 | 
						|
	size_t modules_len = 0;
 | 
						|
	size_t modules_size = 4;
 | 
						|
 | 
						|
	global = ACSVM_Environment_GetGlobalScope(ACSenv, 0);
 | 
						|
 | 
						|
	// Just some notes on how Hexen's scopes work, if anyone
 | 
						|
	// intends to implement proper hub logic:
 | 
						|
 | 
						|
	// The integer is an ID for which hub / map it is,
 | 
						|
	// and instead sets active according to which ones
 | 
						|
	// should run, since you can go between them.
 | 
						|
 | 
						|
	// But I didn't intend on implementing these features,
 | 
						|
	// since hubs aren't planned for Ring Racers (although
 | 
						|
	// they might be useful for SRB2), and I intentionally
 | 
						|
	// avoided implementing global ACS (since Lua would be
 | 
						|
	// a better language to do that kind of code).
 | 
						|
 | 
						|
	// Since we literally only are using map scope, we can
 | 
						|
	// just free everything between every level. But if
 | 
						|
	// hubs are to be implemented, this logic would need
 | 
						|
	// to be far more sophisticated.
 | 
						|
 | 
						|
	// Reset hub scope, even if we are not using it.
 | 
						|
	ACS_ResetHub(global);
 | 
						|
	hub = ACSVM_GlobalScope_GetHubScope(global, 0);
 | 
						|
	ACSVM_HubScope_SetActive(hub, true);
 | 
						|
 | 
						|
	// Start up new map scope.
 | 
						|
	ACS_ResetMap(hub);
 | 
						|
	map = ACSVM_HubScope_GetMapScope(hub, 0);
 | 
						|
	ACSVM_MapScope_SetActive(map, true);
 | 
						|
 | 
						|
	// Allocate module list.
 | 
						|
	modules = Z_Calloc(modules_size * sizeof(ACSVM_Module *), PU_STATIC, NULL);
 | 
						|
 | 
						|
	// Insert BEHAVIOR lump into the list.
 | 
						|
	{
 | 
						|
		char const *str = mapheaderinfo[mapID]->lumpname;
 | 
						|
		size_t len = strlen(str);
 | 
						|
		size_t hash = ACSVM_StrHash(str, len);
 | 
						|
 | 
						|
		ACSVM_ModuleName name = {0};
 | 
						|
 | 
						|
		name.s = ACSVM_StringTable_GetStringByData(strTab, str, len, hash);
 | 
						|
		name.i = mapheaderinfo[mapID]->lumpnum;
 | 
						|
 | 
						|
		if (modules_len >= modules_size)
 | 
						|
		{
 | 
						|
			modules_size *= 2;
 | 
						|
			modules = Z_Realloc(modules, modules_size * sizeof(ACSVM_Module *), PU_STATIC, &modules);
 | 
						|
		}
 | 
						|
 | 
						|
		modules[modules_len] = ACSVM_Environment_GetModule(env, name);
 | 
						|
		modules_len++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (modules_len > 0)
 | 
						|
	{
 | 
						|
		// Register the modules with map scope.
 | 
						|
		ACSVM_MapScope_AddModules(map, modules, modules_len);
 | 
						|
	}
 | 
						|
 | 
						|
	// Start OPEN scripts.
 | 
						|
	ACSVM_MapScope_ScriptStartType(map, 1, NULL, 0, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*--------------------------------------------------
 | 
						|
	void ACS_Tick(void)
 | 
						|
 | 
						|
		See header file for description.
 | 
						|
--------------------------------------------------*/
 | 
						|
void ACS_Tick(void)
 | 
						|
{
 | 
						|
	ACSVM_Environment *env = ACSenv;
 | 
						|
 | 
						|
	if (ACSVM_Environment_HasActiveThread(env) == false)
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ACSVM_Environment_Exec(env);
 | 
						|
}
 |