/*
....[@@@..[@@@..............[@.................. 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
------------------------------------------------------------------------------
vmrun.cc
*/
#include "config.h"
#include "string.h"
#include "io.h"
#include "memoryblock.h"
#include "vmopcodes.h"
#include "vmachine.h"
#include "asmloader.h"
#include "vmarray.h"
#define PUSH_I( x ) psobj++; psobj->type = VMT_INT; psobj->val.i = x;
#define PUSH_F( x ) psobj++; psobj->type = VMT_FLOAT; psobj->val.f = x;
void VMachine::run()
{
int itmp,itmp2;
float ftmp;
const char * txt;
vmstack otmp,otmp2;
vmstack * ptmp;
memorycell cell;
VMarray * array;
// Main execution loop
while ( live )
{
// Cout << (int) pmem << " " << (int) low_border << " " << (int) high_border << endl;
VM_SECURE_2( (pmem < low_border) || (pmem >= high_border ),
"Scope borders broken." );
cell = getCell();
switch( cell.s.opcode )
{
case VMOPC_AADD:
ASSURE_SSIZE(2);
VM_SECURE_2( !IS_ARRAY( (psobj-1)->type ) , "non-narray" );
((VMarray*)(psobj-1)->val.o)->add(psobj->val);
psobj--;
break;
case VMOPC_ABORT:
crit_error("Abort hit");
break;
case VMOPC_ADD:
ASSURE_SSIZE( 2 );
if ( psobj->type == VMT_INT )
{
psobj--;
VM_SECURE_2( psobj->type != VMT_INT,
"Tried to add different types" );
psobj->val.i += (psobj + 1)->val.i;
}
else
{
psobj--;
VM_SECURE_2( (psobj + 1)->type != VMT_FLOAT,
"Tried to add strange type" );
VM_SECURE_2( psobj->type != VMT_FLOAT,
"Tried to add different types" );
psobj->val.f += (psobj + 1)->val.f;
}
break;
case VMOPC_ALEN:
NEED_OSTACK( 1 );
VM_SECURE_2( !IS_ARRAY( psobj->type ) , "non-narray" );
psobj++;
psobj->type = VMT_INT;
psobj->val.i = ((VMarray*)(psobj-1)->val.o)->count();
break;
case VMOPC_APOP:
ASSURE_SSIZE( 3 );
VM_SECURE_2( !IS_ARRAY( (psobj-2)->type ) , "non-narray" );
VM_SECURE_2( (psobj-1)->type != VMT_INT, "array can be indexed only by ints.");
itmp = (psobj-1)->val.i;
array = (VMarray *)(psobj-2)->val.o;
if ( (itmp < 0) || (itmp >= array->count()) )
{
crit_error( "index of of bounds" );
break;
}
VM_SECURE_2( IS_VMOBJ(psobj->type) && psobj->val.o &&
!canCastTo( psobj->val.o->getVMType(), array->type),
"wrong vmtype put into array");
dereference_obj( array->type, array->table()[itmp].val );
array->table()[itmp].val = psobj->val;
array->table()[itmp].type = array->type;
psobj--;
break;
case VMOPC_APUSH:
ASSURE_SSIZE(2);
VM_SECURE_2( !IS_ARRAY( (psobj-1)->type ) , "non-narray" );
VM_SECURE_2( psobj->type != VMT_INT, "array can be indexed only by ints.");
itmp = psobj->val.i;
array = (VMarray*)(psobj-1)->val.o;
if ( (itmp < 0) || (itmp >= array->count()) )
{
crit_error( (String(" index ") + itmp + " of out bounds").chars() );
break;
}
*psobj = array->table()[itmp];
psobj->val = reference_obj(psobj);
break;
case VMOPC_CALL:
crit_error("CALL opcode hit - illegal outside obj file");
break;
case VMOPC_CAST2F:
ASSURE_STACK1( VMT_INT );
psobj->type = VMT_FLOAT;
psobj->val.f = (float) psobj->val.i;
break;
case VMOPC_CAST2I:
ASSURE_STACK1( VMT_FLOAT );
psobj->type = VMT_INT;
psobj->val.i = (s32) psobj->val.f;
break;
case VMOPC_CHECKCAST:
ASSURE_SSIZE( 1 );
VM_SECURE_2( !IS_VMOBJ(psobj->type), "Tried to checkcast non-vmobject" );
if ( !psobj->val.o )
{
psobj->type = VMT_INT;
psobj->val.i = 1;
}
else
{
if ( canCastTo( psobj->val.o->getVMType(),
((vmtype) cell.s.u.number) ) )
psobj->val.i = 1;
else
psobj->val.i = 0;
psobj->type = VMT_INT;
}
break;
case VMOPC_CLONE:
ASSURE_SSIZE( 1 );
NEED_OSTACK( 1 );
psobj++;
psobj->type = (psobj - 1)->type;
psobj->val = reference_obj( psobj - 1 );
break;
case VMOPC_DEC:
ASSURE_SSIZE( 1 );
if ( psobj->type == VMT_INT )
{
psobj->val.i--;
}
else
{
VM_SECURE_2( psobj->type != VMT_FLOAT, "Tried to decrease non-numeric object");
psobj->val.f--;
}
break;
case VMOPC_DECL:
ACCESS_VSTACK( cell.s.u.number );
if ( psvar[cell.s.u.number].type == VMT_INT )
{
psvar[cell.s.u.number].val.i--;
}
else
{
VM_SECURE_2(psvar[cell.s.u.number].type != VMT_FLOAT, "Tried to decrease non-numeric local");
psvar[cell.s.u.number].val.f--;
}
break;
case VMOPC_DIV:
ASSURE_SSIZE( 2 );
if ( psobj->type == VMT_INT )
{
psobj--;
VM_SECURE_2( psobj->type != VMT_INT,
"Tried to div different types" );
psobj->val.i /= (psobj + 1)->val.i;
}
else
{
psobj--;
VM_SECURE_2( (psobj + 1)->type != VMT_FLOAT,
"Tried to div strange type" );
VM_SECURE_2( psobj->type != VMT_FLOAT,
"Tried to div different types" );
psobj->val.f /= (psobj + 1)->val.f;
}
break;
case VMOPC_EXIT:
exitCode = cell.s.conds;
live = false;
break;
case VMOPC_FCALL:
NEED_TSTACK();
leave_trace();
funID = cell.s.u.number;
low_border = vmfun_table[funID].low_border;
high_border = vmfun_table[funID].high_border;
pmem = low_border;
break;
case VMOPC_FCMP:
ASSURE_STACK2( VMT_FLOAT, VMT_FLOAT );
ftmp = (psobj-1)->val.f - psobj->val.f;
psobj -= 2;
if ( FLAG_CONDITION( ftmp ) )
pmem = low_border + cell.s.u.number;
break;
case VMOPC_FEVAL:
ASSURE_STACK1( VMT_FLOAT );
if ( FLAG_CONDITION( psobj->val.f ) )
pmem = low_border + cell.s.u.number;
psobj--;
break;
case VMOPC_GETFIELD:
ASSURE_SSIZE( 1 );
otmp = *psobj;
psobj--;
live = (vmtype_table[ otmp.type ].getfield) (this, otmp.val.o, cell.s.u.number);
dereference_obj( otmp.type, otmp.val );
if ( !live )
Cout << "GetField returned false";
break;
case VMOPC_ICALL:
NEED_TSTACK();
leave_trace();
//Cout << "Calling native fun " << cell.s.u.number << endl;
(vm_interface_table[cell.s.u.number].fun) (this);
collect_trace();
break;
case VMOPC_ICMP:
ASSURE_STACK2( VMT_INT, VMT_INT );
itmp = (psobj-1)->val.i - psobj->val.i;
psobj -= 2;
if ( FLAG_CONDITION( itmp ) )
pmem = low_border + cell.s.u.number;
break;
case VMOPC_IEVAL:
ASSURE_STACK1( VMT_INT );
if ( FLAG_CONDITION( psobj->val.i ) )
pmem = low_border + cell.s.u.number;
psobj--;
break;
case VMOPC_ILOOKUPSWITCH:
ASSURE_STACK1( VMT_INT );
itmp = cell.s.conds;
itmp2 = cell.s.u.number;
for ( ; itmp > 0; itmp-- )
{
if ( psobj->val.i == getCell().vmint )
{
pmem = low_border + getCell().s.u.number;
itmp2 = -1;
break;
}
else
pmem++;
}
if ( itmp2 != -1)
pmem = low_border + itmp2;
break;
case VMOPC_INC:
ASSURE_SSIZE( 1 );
if ( psobj->type == VMT_INT )
{
psobj->val.i++;
}
else
{
VM_SECURE_2( psobj->type != VMT_FLOAT , "Tried to increase non-numeric object" );
psobj->val.f++;
}
break;
case VMOPC_INCL:
ACCESS_VSTACK( cell.s.u.number );
if (psvar[cell.s.u.number].type == VMT_INT )
{
psvar[cell.s.u.number].val.i++;
}
else
{
VM_SECURE_2( psvar[cell.s.u.number].type != VMT_FLOAT, "Tried to increase non-numeric local");
psvar[cell.s.u.number].val.f++;
}
break;
case VMOPC_INITL:
NEED_VSTACK( cell.s.u.number );
// maybe memset ?
otmp.type = VMT_NULL;
otmp.val.o = NULL;
for ( ; cell.s.u.number > 0 ; cell.s.u.number-- )
{
psvar[var_count] = otmp;
var_count++;
}
break;
case VMOPC_INITP:
ASSURE_SSIZE( cell.s.u.number );
NEED_VSTACK( cell.s.u.number );
for ( ; cell.s.u.number > 0 ; cell.s.u.number-- )
{
psvar[var_count] = *psobj;
var_count++;
psobj--;
}
break;
case VMOPC_ISNULL:
ASSURE_SSIZE( 1 );
VM_SECURE_2( !IS_VMOBJ( psobj->type ), "Non VMObject compared to null" );
if ( BOOL_FLAG_CONDITION( !psobj->val.o ) )
pmem = low_border + cell.s.u.number;
psobj--;
break;
case VMOPC_ITABLESWITCH:
ASSURE_STACK1( VMT_INT );
itmp2 = cell.s.u.number; // default jump
if ( (psobj->val.i < (itmp = getCell().vmint) ) ||
(psobj->val.i > getCell().vmint) )
{
pmem = low_border + itmp2; // jump to default
break;
}
pmem += psobj->val.i - itmp; // value - minimal
pmem = low_border + getCell().s.u.number;
break;
case VMOPC_JMP:
pmem = low_border + cell.s.u.number;
break;
case VMOPC_LICMP:
ASSURE_STACK1( VMT_INT );
ACCESS_VSTACK( cell.s.u.number );
// vmstack == int ??
itmp = psvar[cell.s.u.number].val.i - psobj->val.i;
psobj--;
if ( FLAG_CONDITION( itmp ) )
pmem = low_border + getCell().s.u.number;
else
pmem++;
break;
case VMOPC_LIEVAL:
ACCESS_VSTACK( cell.s.u.number );
// vstack == int ???
itmp = psvar[cell.s.u.number].val.i;
if ( FLAG_CONDITION( itmp ) )
pmem = low_border + getCell().s.u.number;
else
pmem++;
break;
case VMOPC_MOD:
ASSURE_STACK2( VMT_INT, VMT_INT );
psobj--;
psobj->val.i %= (psobj + 1)->val.i;
break;
case VMOPC_MOVC:
ACCESS_VSTACK( cell.s.u.number );
dereference_obj( psvar + cell.s.u.number );
psvar[cell.s.u.number] = vmconstant_table[getCell().s.u.number];
psvar[cell.s.u.number].val = reference_obj(psvar + cell.s.u.number);
break;
case VMOPC_MOVF:
ACCESS_VSTACK( cell.s.u.number );
dereference_obj( psvar + cell.s.u.number );
psvar[cell.s.u.number].type = VMT_FLOAT;
psvar[cell.s.u.number].val.f = getCell().vmfloat;
break;
case VMOPC_MOVI:
ACCESS_VSTACK( cell.s.u.number );
dereference_obj( psvar + cell.s.u.number );
psvar[cell.s.u.number].type = VMT_INT;
psvar[cell.s.u.number].val.i = getCell().vmint;
break;
case VMOPC_MPOP:
ASSURE_SSIZE( cell.s.conds );
for ( ;cell.s.conds > 0; cell.s.conds--)
{
dereference_obj( psobj );
psobj--;
}
break;
case VMOPC_MUL:
ASSURE_SSIZE( 2 );
if ( psobj->type == VMT_INT )
{
psobj--;
VM_SECURE_2( psobj->type != VMT_INT,
"Tried to mul different types" );
psobj->val.i *= (psobj + 1)->val.i;
}
else
{
psobj--;
VM_SECURE_2( (psobj + 1)->type != VMT_FLOAT,
"Tried to mul strange type" );
VM_SECURE_2( psobj->type != VMT_FLOAT,
"Tried to mul different types" );
psobj->val.f *= (psobj + 1)->val.f;
}
break;
case VMOPC_NOP:
break;
case VMOPC_OCMP:
ASSURE_SSIZE( 2 );
VM_SECURE_2( !IS_VMOBJ(psobj->type),
"Tried to ocmp non-vmobject" );
VM_SECURE_2( !IS_VMOBJ((psobj-1)->type),
"Tried to ocmp non-vmobject" );
if ( BOOL_FLAG_CONDITION( (psobj - 1)->val.o == psobj->val.o) )
pmem = low_border + cell.s.u.number;
psobj -= 2;
break;
case VMOPC_POP:
ASSURE_SSIZE( 1 );
dereference_obj( psobj );
psobj--;
break;
case VMOPC_POPL:
ACCESS_VSTACK( cell.s.u.number );
// Do I need to deref and then ref ? - for now just copy
ptmp = (psvar + cell.s.u.number);
ptmp->type = psobj->type;
ptmp->val = psobj->val;
psobj--;
break;
case VMOPC_POPSTAT:
itmp = cell.s.u.number;
dereference_obj( &vmstatvar_table[itmp] );
vmstatvar_table[itmp] = *psobj;
psobj--;
break;
case VMOPC_PUSHC:
NEED_OSTACK( 1 );
psobj++;
*psobj = vmconstant_table[cell.s.u.number];
psobj->val = reference_obj(psobj);
break;
case VMOPC_PUSHF:
NEED_OSTACK( 1 );
PUSH_F( getCell().vmfloat );
break;
case VMOPC_PUSHFZ:
NEED_OSTACK( 1 );
PUSH_F( 0 );
break;
case VMOPC_PUSHI:
NEED_OSTACK( 1 );
PUSH_I( getCell().vmint );
break;
case VMOPC_PUSHIZ:
NEED_OSTACK( 1 );
PUSH_I( 0 );
break;
case VMOPC_PUSHL:
NEED_OSTACK( 1 );
ACCESS_VSTACK( cell.s.u.number );
psobj++;
ptmp = (psvar + cell.s.u.number);
psobj->type = ptmp->type;
psobj->val = reference_obj( ptmp );
break;
case VMOPC_PUSHSTAT:
NEED_OSTACK( 1 );
itmp = cell.s.u.number;
psobj++;
*psobj = vmstatvar_table[itmp];
psobj->val = reference_obj( psobj );
break;
case VMOPC_PUSHTHIS:
NEED_OSTACK( 1 );
// this is always variable number 0
psobj++;
psobj->type = psvar->type;
psobj->val = reference_obj(psvar);
break;
case VMOPC_RCALL:
crit_error("Opcode not longer supported");
break;
// obsolete
case VMOPC_REDUCEL:
crit_error("Opcode not longer supported");
break;
case VMOPC_RET:
collect_trace();
break;
case VMOPC_SCHARAT:
ASSURE_STACK2( VMT_STRING, VMT_INT );
itmp = psobj->val.i;
psobj--;
if ( itmp < psobj->val.s->len() )
itmp = (psobj->val.s->chars())[itmp];
else
itmp = 0;
delete psobj->val.s;
psobj->type = VMT_INT;
psobj->val.i = itmp;
break;
case VMOPC_SCHARATL:
ASSURE_STACK1( VMT_INT );
ACCESS_VSTACKT( cell.s.u.number, VMT_STRING );
itmp = psobj->val.i;
if ( itmp < psvar[cell.s.u.number].val.s->len() )
itmp = (psvar[cell.s.u.number].val.s->chars())[itmp];
else
itmp = 0;
psobj->val.i = itmp;
break;
case VMOPC_SCMP:
ASSURE_STACK2( VMT_STRING, VMT_STRING );
if ( BOOL_FLAG_CONDITION( *(psobj->val.s) == *((psobj-1)->val.s) ) )
pmem = low_border + cell.s.u.number;
delete psobj->val.s;
psobj--;
delete psobj->val.s;
psobj--;
break;
case VMOPC_SCONCAT:
ASSURE_STACK2( VMT_STRING, VMT_STRING );
*(psobj-1)->val.s += *psobj->val.s;
delete psobj->val.s;
psobj--;
break;
case VMOPC_SETFIELD:
ASSURE_SSIZE( 2 );
otmp2 = *psobj;
psobj--;
otmp = *psobj;
psobj--;
txt = (vmtype_table[ otmp2.type ].setfield)
(this, otmp2.val.o, cell.s.u.number, otmp.val);
dereference_obj( otmp.type, otmp.val );
dereference_obj( otmp2.type, otmp2.val );
if ( txt )
crit_error(txt);
break;
case VMOPC_SFLOATCAT:
ASSURE_STACK2( VMT_STRING, VMT_FLOAT );
itmp = (int) psobj->val.f;
psobj--;
*(psobj->val.s) += itmp;
break;
case VMOPC_SGETARG:
NEED_OSTACK( 1 );
psobj++;
psobj->type = VMT_STRING;
// memory leak ?
psobj->val.s = new String( current_args->getArg() );
break;
case VMOPC_SGETARGREST:
NEED_OSTACK( 1 );
psobj++;
psobj->type = VMT_STRING;
// memory leak ?
psobj->val.s = new String( current_args->getArgRest() );
break;
case VMOPC_SINTCAT:
ASSURE_STACK2( VMT_STRING, VMT_INT );
itmp = psobj->val.i;
psobj--;
*(psobj->val.s) += itmp;
break;
case VMOPC_SISEMPTY:
ASSURE_STACK1( VMT_STRING );
if ( BOOL_FLAG_CONDITION( !(bool)*(psobj->val.s) ) )
pmem = low_border + cell.s.u.number;
delete psobj->val.s;
psobj--;
break;
case VMOPC_SISNUMBER:
ASSURE_STACK1( VMT_STRING );
if ( BOOL_FLAG_CONDITION( psobj->val.s->isNumber() ) )
pmem = low_border + cell.s.u.number;
delete psobj->val.s;
psobj--;
break;
case VMOPC_SLEEP:
ASSURE_STACK1( VMT_FLOAT );
putToSleep( psobj->val.f );
psobj--;
return;
case VMOPC_SLOOKUPSWITCH:
ASSURE_STACK1( VMT_STRING );
itmp = cell.s.conds;
itmp2 = cell.s.u.number;
for ( ; itmp > 0; itmp-- )
{
if ( *(psobj->val.s) == *(vmconstant_table[getCell().s.u.number].val.s) )
{
pmem = low_border + getCell().s.u.number;
itmp2 = -1;
break;
}
else
pmem++;
}
if ( itmp2 != -1)
pmem = low_border + itmp2;
break;
case VMOPC_SPUSHEMPTY:
NEED_OSTACK( 1 );
psobj++;
psobj->type = VMT_STRING;
psobj->val.s = new String();
break;
case VMOPC_SSTARTARGS:
ASSURE_STACK1( VMT_STRING );
psobj->val.s->startArgs();
if ( current_args )
delete current_args;
current_args = psobj->val.s;
psobj--;
break;
case VMOPC_STOINT:
ASSURE_STACK1( VMT_STRING );
itmp = psobj->val.s->asInt();
delete psobj->val.s;
psobj->val.i = itmp;
psobj->type = VMT_INT;
break;
case VMOPC_SUB:
ASSURE_SSIZE( 2 );
if ( psobj->type == VMT_INT )
{
psobj--;
VM_SECURE_2( psobj->type != VMT_INT,
"Tried to sub different types" );
psobj->val.i -= (psobj + 1)->val.i;
}
else
{
psobj--;
VM_SECURE_2( (psobj + 1)->type != VMT_FLOAT,
"Tried to sub strange type" );
VM_SECURE_2( psobj->type != VMT_FLOAT,
"Tried to sub different types" );
psobj->val.f -= (psobj + 1)->val.f;
}
break;
case VMOPC_SWAP:
ASSURE_SSIZE( 2 );
otmp = *psobj;
*psobj = *(psobj - 1);
*(psobj-1) = otmp;
break;
case VMOPC_UPCAST:
ASSURE_SSIZE(1);
VM_SECURE_2( !IS_VMOBJ(psobj->type), "Tried to upcast non-vmobject" );
if ( !psobj->val.o )
{
psobj->type = (vmtype) cell.s.u.number;
}
else
{
otmp.type = psobj->val.o->getVMType();
if ( canCastTo( otmp.type, ((vmtype) cell.s.u.number) ) )
psobj->type = (vmtype) cell.s.u.number;
else
crit_error("Illegal upcast");
}
break;
default:
char buf[100];
sprintf(buf, "Unrecognized opcode %d." , cell.s.opcode );
crit_error( buf );
break;
}
}
// dereference objects on stack and variable stack
itmp = ( psobj - oStack );
// Cout << itmp << " objects on oStack"<<endl;
for ( ; itmp > 0; itmp--, psobj--)
{
dereference_obj(psobj);
}
itmp = ( psvar + var_count - vStack );
// Cout << itmp << " objects on vStack"<<endl;
for ( itmp--; itmp >=0; itmp-- )
{
// Cout << "Dereferencing ...";
dereference_obj( &vStack[itmp] );
// Cout << "done" <<endl;
}
if ( current_args )
delete current_args;
current_args = NULL;
Cout << "VMachine exited with code " << (int) exitCode << endl;
return;
}