/*
....[@@@..[@@@..............[@.................. 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();
}