mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@hom.net
MUD++ development mailing list    mudpp@van.ml.org
------------------------------------------------------------------------------
vmachine.cpp
*/

#include "config.h"
#include "io.h"
#include "vmachine.h"
#include "asmparser.h"
#include "asmloader.h"
#include "properties.h"

Array <VMachine *> freeVMs;
Array <VMachine *> dozingVMs;
Array <VMachine *> sleepingVMs;



// for opcode execution loop check vmrun.cpp

void VMachine::buildStackTrace( String & str )
{
	if ( !live )
	{
		str << "VMachine is not alive" << endl;
		return;
	}
	vmtrace * vmt;
	str << "VMachine at " << vmfun_table[funID].name << ':' << 
		(int) (pmem - low_border -1) << endl;
	for ( vmt = pstrace-1; vmt >= tStack; vmt--)
	{
		str << "called from " << vmfun_table[vmt->funID].name << ':' <<
			(int) (vmt->focus - vmfun_table[vmt->funID].low_border -1) << endl;
	}
	str << "called from mud++" << endl;
}


void VMachine::crit_error( const char * txt )
{
	if ( pmem == 0 )
	{
		Cout << "VM Error: focus at NULL pointer" << endl;
	}
	else
	{
	Cout << "VM Error: opcode " << lookupOpcodeName((pmem-1)->s.opcode) <<
	" : "<< txt << endl;
	}
	String str;
	buildStackTrace(str);
	Cout << str;
	live = false;
	exitCode = 1;
	// mark functions as bad ?
}


void VMachine::leave_trace(int offset)
{
	pstrace->funID = funID;
	pstrace->focus = pmem + offset;
	pstrace->var_count = var_count;
	psvar += var_count;
	var_count = 0;
	pstrace++;
}

void VMachine::collect_trace()
{
	if ( pstrace <= tStack )
	{
		live = false;
		return;
	}
	pstrace--;
	funID = pstrace->funID;
	low_border = vmfun_table[funID].low_border;
	high_border = vmfun_table[funID].high_border;
	pmem = pstrace->focus;
	for ( var_count--; var_count >= 0; var_count-- )
	{
		dereference_obj( psvar + var_count );
	}
	var_count = pstrace->var_count;
	psvar -= var_count;
}

vmstack * VMachine::initp( int number )
{
#if VM_SECURITY_LEVEL >= 1
	if ( psvar + number > vStackCeiling )
	{
		crit_error("Variable stack exceeded");
		return NULL;
	}
#if VM_SECURITY_LEVEL >= 2
	if ( psobj < &oStack[number] )
	{
		crit_error("Inited more parameters than was on stack.\n\r");
		return NULL;
	}
#endif
#endif

	for ( ; number > 0 ; number-- )
	{
		*(psvar + var_count) = *psobj;
		var_count++;
		psobj--;
	}
	return psvar;
}

void VMachine::putToSleep( float seconds )
{
	// traslate seconds to pulses or to vm_update_pulses (?)
	to_sleep = (int) (seconds);
}



void VMachine::defaultValues()
{
	psobj = oStack;
	psvar = vStack;
	pstrace = tStack;
	var_count = 0;
	current_args = NULL;
	exitCode = 0;
	funID = -1;
	live = false;
}

void VMachine::run( int funnumber )
{
	funID = funnumber;
	low_border = vmfun_table[funID].low_border;
	high_border = vmfun_table[funID].high_border;
	pmem = low_border;
	live = true;
	schedule();
}

void VMachine::schedule()
{
	static Property * prop = 0;
	
	run();

	UseProperty( &prop, "VM_dozing_barrier", PROP_INT, "60", nf_gtz, true);
	if ( !live )
		freeVMs.add( this );
	else if ( to_sleep < prop->getInt() )
		dozingVMs.add( this );
	else
		sleepingVMs.add(this);
}


VMachine * getFreeVM()
{
	VMachine * vm;
	if ( (vm = freeVMs.removeTop()) != NULL )
		return vm;
	vm = new VMachine();
	return vm;
}

void VMstatistic( String & str )
{
	str << freeVMs.length() << " VMs are free" << endl << 
		dozingVMs.length() << " VMs are dozing" << endl << 
		sleepingVMs.length() << " VMs are sleeping" << endl;
}

void createVMs()
{
	static Property * prop =0;
	UseProperty( &prop, "starting_number_of_VMs", PROP_INT, "10", NULL, false);
	int i;
	i = prop->getInt();
	for ( ; i > 0; i-- )
	{
		freeVMs.add( new VMachine() );
	}
}


void pulseVM()
{	
	VMachine * vm;
	int i;
	// it has to go back, as it can be added to top after schedule
	for ( i = dozingVMs.length()-1 ; i >= 0; i -- )
	{
		vm = dozingVMs[i];
		if ( !vm->tick() )
		{
			dozingVMs.remove(i);
			vm->schedule();
		}
	}
}

int VMachine::bigTick(int doze)
{
	to_sleep -= doze;
	return to_sleep;
}

void VMachine::mark()
{
	int i;
	for (i = psobj - oStack; i > 0; i-- )
	{
		if (IS_VMOBJ(oStack[i].type) && oStack[i].val.o)
			oStack[i].val.o->GCMark();
	}

	for (i = var_count + psvar - oStack - 1; i >= 0; i-- )
	{
		if (IS_VMOBJ(vStack[i].type) && vStack[i].val.o)
			vStack[i].val.o->GCMark();
	}
}

void wakeSleepingVM()
{
	static Property * prop = 0;
	UseProperty( &prop, "VM_dozing_barrier", PROP_INT, "60", nf_gtz, true);
	int i;
	int doze = prop->getInt();

	VMachine * vm;
	for ( i = sleepingVMs.length()-1 ; i >= 0; i -- )
	{
		vm = sleepingVMs[i];
		if ( vm->bigTick(doze) < 0 )
		{
			sleepingVMs.remove(i);
			dozingVMs.add(vm);
		}
	}

}

void VMmark4GC()
{
	int i;
	for ( i=dozingVMs.length()-1; i >=0; i--)
		dozingVMs[i]->mark();
	
	for ( i=sleepingVMs.length()-1; i >=0; i--)
		sleepingVMs[i]->mark();

}