#include "ubermud.h" #include "externs.h" /* Copyright(C) 1990, Marcus J. Ranum, All Rights Reserved. This software may be freely used, modified, and redistributed, as long as this copyright message is left intact, and this software is not used to develop any commercial product, or used in any product that is provided on a pay-for-use basis. */ /* routines to handle running a compiled program in a virtual machine. also handled are stack operations, calling of functions, stack frames, and so on. */ /* some simple macros for manipulating machine addresses */ #define CURRINST(p,pc) ((p)->p_mem[pc]) #define CURRSTAK(m) ((m)->m_mem[(m)->m_pc]) #define CURRSTAB(m) ((m)->m_fram[(m)->m_fp].syms) /* used to flag a return/break out of if statement */ static int returning = 0; /* used to flag a serious problem (stop the run) */ static int runstate = RUN_OK; /* if CPU tick limiting is on, this is the limit counter */ #ifdef LIMITRUNCPU static int cputicks = 0; #endif /* #define CPUTICKDEBUG #define STACKDEBUG #define RUNDEBUG #define EXCEPTDEBUG #define FRAMEDEBUG */ /* modify the #self value on the machine stack - this SHOULD be built into stack_push/stack_pop, but it'd be a royal pain. */ static void stack_setowner(m,own) Machine *m; long own; { if(m->m_pc >= m->m_maxpc) { logf("stack_setowner: stack overflow!\n",0); runstate = RUN_STACK; return; } CURRSTAK(m).own = own; #if defined(RUNDEBUG) || defined(STACKDEBUG) printf("mark stacked element as object #%d\n",own); #endif } /* push a value on the machine stack */ static void stack_push(m,typ,val) Machine *m; int typ; Oper val; { if(m->m_pc >= m->m_maxpc) { /* increment it ANYWAY! this is important since stack_pop will give an error when it is accessed off the top of the stack */ logf("stack_push: stack overflow: ",ltoa(CURRSTAK(m).own), "is the current object\n",0); m->m_pc++; runstate = RUN_STACK; return; } CURRSTAK(m).typ = typ; CURRSTAK(m).oper = val; #if defined(RUNDEBUG) || defined(STACKDEBUG) switch(typ) { case TYP_FUNC: printf("pushed function %d\n",val.p); break; case TYP_UNDEF: printf("pushed undef\n"); break; case TYP_NULL: printf("pushed NULL (%d)\n",val.i); break; case TYP_NUM: printf("pushed num %d\n",val); break; case TYP_STR: printf("pushed str %s\n",val.c); break; case TYP_VAR: printf("pushed var %s\n",val.c); break; case TYP_OBJ: printf("pushed object reference %d\n",val.l); break; case TYP_OLIST: printf("pushed object list @%d\n",val.ol); break; case TYP_ELEM: printf("pushed elem %d.%s\n",ELENUM(val.c),ELENAM(val.c)); break; default: printf("pushed unknown/illegal type!\n"); } #endif m->m_pc++; } /* take a value off the machine stack */ static int stack_pop(m,op) Machine *m; Oper *op; { if(--(m->m_pc) < 0) { logf("stack_pop: stack underflow!\n",0); op->i = ERR_STACK; runstate = RUN_STACK; return(TYP_NULL); } if(m->m_pc >= m->m_maxpc) { logf("stack_pop: stack overflow handled\n",0); op->i = ERR_STACK; runstate = RUN_STACK; return(TYP_NULL); } *op = CURRSTAK(m).oper; #if defined(RUNDEBUG) || defined(STACKDEBUG) switch(CURRSTAK(m).typ) { case TYP_FUNC: printf("popped function %d\n",CURRSTAK(m).oper.p); break; case TYP_UNDEF: printf("popped undef\n"); break; case TYP_NULL: printf("popped NULL (%d)\n",CURRSTAK(m).oper.i); break; case TYP_NUM: printf("popped num %d\n",CURRSTAK(m).oper.i); break; case TYP_STR: printf("popped str %s\n",CURRSTAK(m).oper.c); break; case TYP_VAR: printf("popped var %s\n",CURRSTAK(m).oper.c); break; case TYP_OBJ: printf("popped object reference %d\n",CURRSTAK(m).oper.l); break; case TYP_OLIST: printf("popped object list @%d\n",CURRSTAK(m).oper.ol); break; case TYP_ELEM: printf("popped elem %d.%s\n", ELENUM(CURRSTAK(m).oper.c), ELENAM(CURRSTAK(m).oper.c)); break; default: printf("popped unknown! object\n"); } #endif return(CURRSTAK(m).typ); } static int frame_push(m,ret_sp,ret_ac,onum) Machine *m; int ret_sp; int ret_ac; long onum; { if(m->m_fp >= m->m_maxfp) { logf("frame_push: out of stack frames !\n",0); runstate = RUN_FRAME; return(-1); } #ifdef FRAMEDEBUG printf("push frame #%d, ret_sp=%d, ret_ac=%d, ",m->m_fp,ret_sp,ret_ac); printf("cur_obj=%ld, cur_uid=%ld, ",onum,*uid); #endif m->m_fram[m->m_fp].ret_sp = ret_sp; m->m_fram[m->m_fp].ret_ac = ret_ac; m->m_fram[m->m_fp].cur_obj = onum; m->m_fram[m->m_fp].cur_uid = 0; m->m_fp++; CURRSTAB(m) = (Sym *)0; return(0); } static Frame * frame_pop(m) Machine *m; { if(CURRSTAB(m) != (Sym *)0) { #ifdef FRAMEDEBUG printf("free frame #%d symbols\n",m->m_fp); #endif symfreelist(CURRSTAB(m)); } if(--(m->m_fp) < 0) { logf("frame_pop: frame underflow (this is a disaster!)\n",0); runstate = RUN_FRAME; return((Frame *)0); } #ifdef FRAMEDEBUG printf("pop frame #%d\n",m->m_fp); #endif return(&m->m_fram[m->m_fp]); } /* handle comparision of equality or non-equality */ static void test_equal(d1t,d1,d2t,d2,op) int *d1t; Oper *d1; int *d2t; Oper *d2; int op; { /* if one or the other is NULL... */ if(*d1t == TYP_OBJ && *d2t == TYP_OBJ) { d1->i = (d1->l == d2->l); *d1t = TYP_NUM; } else if(*d1t == TYP_NULL || *d2t == TYP_NULL) { d1->i = (*d1t == *d2t); *d1t = TYP_NUM; } else if(*d1t == TYP_STR && *d2t == TYP_STR) { d1->i = !(strcmp(d1->c,d2->c)); *d1t = TYP_NUM; } else if(*d1t != TYP_NUM || *d2t != TYP_NUM) { d1->i = 0; *d1t = TYP_NUM; return; } else d1->i = (d2->i == d1->i); if(op == OP_NE) d1->i = !(d1->i); *d1t = TYP_NUM; } /* return a meaningful boolean value from an object */ static int test_bool(dt,d) int dt; Oper d; { if(dt == TYP_NULL) return(0); if(dt == TYP_NUM && d.i == 0) return(0); if(dt == TYP_STR && *(d.c) == '\0') return(0); if(dt == TYP_OBJ && d.l == 0) return(0); if(dt == TYP_OLIST && d.ol->l_cnt == 0) return(0); return(1); } /* handle basic binary numeric operations. returning nonzero indicates an error condition (currently relevant only in division by zero). */ static int numeric_op(op,d1,d2) int op; Oper *d1; Oper *d2; { switch(op) { case OP_ADD: d1->i = d2->i + d1->i; break; case OP_SUB: d1->i = d2->i - d1->i; break; case OP_DIV: if(d1->i == 0) { d2->i = ERR_ZDIV; return(1); } else { d1->i = d2->i / d1->i; } break; case OP_MUL: d1->i = d2->i * d1->i; break; case OP_LT: d1->i = (d2->i < d1->i); break; case OP_LTE: d1->i = (d2->i <= d1->i); break; case OP_GT: d1->i = (d2->i > d1->i); break; case OP_GTE: d1->i = (d2->i >= d1->i); break; } return(0); } /* loop across a list of machine instructions, obeying them dutifully. we *COULD* get some extra speed out of stuff like ASGN, EVAL, and so on, by avoiding calls to stack_pop and stack_push, but that kind of hackery can be done if it proves necessary. */ run(mach,prog,pc,uid,euid) Machine *mach; Prog *prog; int pc; long *uid; long *euid; { Oper d1; /* stack data */ Oper d2; int d1t; /* types of stack data */ int d2t; Sym *sp; /* var ptr for locals. */ ObjList *lp; /* misc. list pointer for for statements */ long rp; /* misc. object ref */ Frame *framep; /* misc. frame pointer */ int op; /* saved operator */ int tmp1; int tmp2; int tmp3; int runval; #ifdef RUNDEBUG printf("running prog %d at %d\n",prog,pc); #endif /* unset returning */ returning = 0; while(CURRINST(prog,pc) != OP_STOP) { #ifdef LIMITRUNCPU if(cputicks >= MAXCPUTICKS && *uid != (long)0 && *euid != (long)0) { runstate = RUN_CPU; } cputicks++; #endif if(runstate != RUN_OK) { #if defined(RUNDEBUG) || defined(EXCEPTDEBUG) printf("RUN: bad run state, returning %d\n",runstate); #endif return(runstate); } switch(op = CURRINST(prog,pc)) { /* handle binary numeric operations */ case OP_ADD: case OP_SUB: case OP_DIV: case OP_MUL: case OP_LT: case OP_LTE: case OP_GT: case OP_GTE: #ifdef RUNDEBUG printf("num op %d at pc=%d in in prog %d\n",op,pc,prog); #endif d1t = stack_pop(mach,&d1); d2t = stack_pop(mach,&d2); if(d1t != TYP_NUM || d2t != TYP_NUM) { d2.i = ERR_NUM; stack_push(mach,TYP_NULL,d2); break; } if(numeric_op(op,&d1,&d2)) { d2.i = ERR_NUM; stack_push(mach,TYP_NULL,d1); } else stack_push(mach,TYP_NUM,d1); break; /* assign to a local/temporary variable. */ case OP_ASGN: #ifdef RUNDEBUG printf("OP_ASGN at pc=%d in in prog %d\n",pc,prog); #endif /* next instruction is name of variable */ pc++; d1.c = prog->p_str + CURRINST(prog,pc); /* and take the value to assign to off the stack. */ d2t = stack_pop(mach,&d2); sp = symlook(d1.c,CURRSTAB(mach)); if(sp == 0) { sp = symnew(d1.c,d2t,d2); /* assume we are out of memory, or something */ if(sp == (Sym *)0) { d2.i = ERR_OOM; stack_push(mach,TYP_NULL,d2); break; } CURRSTAB(mach) = symadd(sp,CURRSTAB(mach)); } sp->typ = d2t; sp->data = d2; stack_push(mach,sp->typ,sp->data); break; case OP_CALL: #ifdef RUNDEBUG printf("OP_CALL at pc=%d in in prog %d\n",pc,prog); #endif returning = 0; /* next instruction is the # of args */ pc++; tmp2 = CURRINST(prog,pc); tmp3 = (mach->m_pc - tmp2) - 1; #ifdef RUNDEBUG printf("CALL - find func at %d\n",tmp3); #endif /* stack our current running context */ if(mach->m_mem[tmp3].typ != TYP_FUNC) { #if defined(RUNDEBUG) || defined(EXCEPTDEBUG) printf("CALL - function not found\n"); #endif /* not a function. manually fix up the stack */ mach->m_mem[tmp3].typ = TYP_NULL; mach->m_mem[tmp3].oper.i = ERR_NOTHERE; mach->m_pc = tmp3 + 1; } else { if(frame_push(mach,tmp3,tmp2,mach->m_mem[tmp3].own)) { #if defined(RUNDEBUG) || defined(EXCEPTDEBUG) printf("CALL out of frames!\n"); #endif mach->m_mem[tmp3].typ = TYP_NULL; mach->m_mem[tmp3].oper.i = ERR_STACK; mach->m_pc = tmp3 + 1; } else { long tmpu = *euid; long savu = *uid; long saveu = *euid; /* handle setuid functions */ if(mach->m_mem[tmp3].oper.p->p_mode & PERM_SUID) { tmpu = mach->m_mem[tmp3].oper.p->p_uid; #if defined(RUNDEBUG) || defined(EXCEPTDEBUG) printf("CALL setuid as %d\n",tmpu); #endif } #ifdef RUNDEBUG printf("CALL run(%d,%d,0)\n",mach,mach->m_mem[tmp3].oper.p); #endif runval = run(mach,mach->m_mem[tmp3].oper.p,0,uid,&tmpu); if(runval != RUN_OK) return(runval); /* restore perms - at each level */ *uid = savu; *euid = saveu; } } returning = 0; break; case OP_BLTIN: #ifdef RUNDEBUG printf("OP_BLTIN at pc=%d in in prog %d\n",pc,prog); #endif /* next instruction is the # of the builtin */ pc++; tmp1 = CURRINST(prog,pc); /* next instruction is the # of args */ pc++; tmp2 = CURRINST(prog,pc); /* pretend it was a successful func. call */ returning = 0; /* result goes in what is now the first arg */ tmp3 = mach->m_pc - tmp2; (*bltintab[tmp1])(mach,tmp2,mach->m_pc - tmp2,tmp3,uid,euid); /* fake an increment of the stack pointer */ mach->m_pc = tmp3 + 1; break; /* push the parameter count on the stack. this will only occur in a function(guaranteed). sneak a look at the stack frame, and push the arg count. */ case OP_CPUSH: #ifdef RUNDEBUG printf("OP_CPUSH at pc=%d in in prog %d\n",pc,prog); #endif d2.i = mach->m_fram[mach->m_fp - 1].ret_ac; stack_push(mach,TYP_NUM,d2); break; case OP_ELPUSH: case OP_SELPUSH: #ifdef RUNDEBUG printf("OP_ELPUSH/SELPUSH at pc=%d in in prog %d\n",pc,prog); #endif /* next instruction is name of variable */ pc++; d1.c = prog->p_str + CURRINST(prog,pc); /* and the numeric part is on the stack */ if(op == OP_ELPUSH) { d2t = stack_pop(mach,&d2); /* must be a number or an error */ if(d2t != TYP_OBJ) { d2.i = ERR_BADOBJ; stack_push(mach,TYP_NULL,d2); break; } rp = d2.l; } else { rp = (long)0; } /* allocate storage */ d2.c = tmpalloc(strlen(d1.c) + 1 + (int)sizeof(long)); ELENUM(d2.c) = rp; (void)strcpy(ELENAM(d2.c),d1.c); stack_push(mach,TYP_ELEM,d2); break; /* the controversial string->varname operator basically, just pull the front half of the element off the stack, and the back half, and type-check them, make sure they're a number and a string, and build an element name out of 'em. easy as that. (but a major issue in language design). */ case OP_STRVAR: #ifdef RUNDEBUG printf("OP_STRVAR at pc=%d in in prog %d\n",pc,prog); #endif /* the string part is on the stack */ d1t = stack_pop(mach,&d1); /* the numeric part is on the stack */ d2t = stack_pop(mach,&d2); /* d2 must be a number or an error and d1 a string */ if(d2t != TYP_OBJ || d1t != TYP_STR) { d2.i = ERR_BADOBJ; stack_push(mach,TYP_NULL,d2); break; } rp = d2.l; /* some insanity checking - no strings too long, please */ if(strlen(d1.c) + 1 >= MAXIDENTLEN) { d2.i = ERR_BADOBJ; stack_push(mach,TYP_NULL,d2); break; } /* allocate storage and build the element buffer */ d2.c = tmpalloc(strlen(d1.c) + 1 + (int)sizeof(long)); ELENUM(d2.c) = rp; (void)strcpy(ELENAM(d2.c),d1.c); stack_push(mach,TYP_ELEM,d2); break; /* deref is a slimy (clever?) trick - by posing as a unary operator we allow the user to leave the name of an element on the stack. we simply check to ensure that the user has, in fact, provided us with a good element name. */ case OP_REF: #ifdef RUNDEBUG printf("OP_REF at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); if(d1t != TYP_ELEM) { d2.i = ERR_REF; stack_push(mach,TYP_NULL,d2); break; } /* all is well, push it back */ stack_push(mach,TYP_ELEM,d1); break; case OP_EEVAL: #ifdef RUNDEBUG printf("OP_EEVAL at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); /* thaw out the element if it is one */ if(d1t != TYP_ELEM) { d2.i = ERR_BADOBJ; stack_push(mach,TYP_NULL,d2); break; } if(ELENUM(d1.c) == 0) { int thawval; if(thawval = sys_thaw(d1.c,&d2t,&d2,*uid,*euid)) { d2.i = thawval; stack_push(mach,TYP_NULL,d2); break; } } else { int thawval; if(thawval = disk_thaw(d1.c,&d2t,&d2,*uid,*euid)) { d2.i = thawval; stack_push(mach,TYP_NULL,d2); break; } } /* MILD kludge. set the owner of the thing for #self. This MUST be done before the stack_push! */ stack_setowner(mach,ELENUM(d1.c)); stack_push(mach,d2t,d2); break; case OP_EASGN: case OP_ECASGN: #ifdef RUNDEBUG printf("OP_EASGN/ECASGN at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); d2t = stack_pop(mach,&d2); /* freeze the element if it is one */ if(d2t != TYP_ELEM || (op == OP_ECASGN && ELENUM(d2.c) == (long)0)) { d2.i = ERR_BADOBJ; stack_push(mach,TYP_NULL,d2); break; } if(ELENUM(d2.c) == (long)0) { int frzval; if(frzval = sys_freeze(d2.c,d1t,d1,*uid,*euid)) { d2.i = frzval; stack_push(mach,TYP_NULL,d2); break; } } else { int frzval; if(frzval = disk_freeze(d2.c,d1t,d1,op,*uid,*euid)) { d2.i = frzval; stack_push(mach,TYP_NULL,d2); break; } } stack_push(mach,d1t,d1); break; case OP_EQ: case OP_NE: #ifdef RUNDEBUG printf("OP_EQ/NE at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); d2t = stack_pop(mach,&d2); /* some operator overloading is done here */ test_equal(&d1t,&d1,&d2t,&d2,op); stack_push(mach,d1t,d1); break; case OP_NULPUSH: #ifdef RUNDEBUG printf("NULPUSH at pc=%d in in prog %d\n",pc,prog); #endif d2.i = ERR_USER; stack_push(mach,TYP_NULL,d2); break; case OP_EVAL: #ifdef RUNDEBUG printf("OP_EVAL at pc=%d in in prog %d\n",pc,prog); #endif /* next instruction is name of variable */ pc++; d1.c = prog->p_str + CURRINST(prog,pc); sp = symlook(d1.c,CURRSTAB(mach)); if(sp == 0) { d2.i = ERR_NOTHERE; stack_push(mach,TYP_NULL,d2); } else { stack_push(mach,sp->typ,sp->data); } break; /* cope with an if statement by unrolling the address in the program to jump to, making a recursive (gag, choke) call to run() with that program counter, and returning to execute. */ case OP_IF: #ifdef RUNDEBUG printf("OP_IF at pc=%d in in prog %d\n",pc,prog); #endif pc++; tmp1 = CURRINST(prog,pc); /* if/true part */ pc++; tmp2 = CURRINST(prog,pc); /* if/else part */ pc++; tmp3 = CURRINST(prog,pc); /* continuation */ #ifdef RUNDEBUG printf("IF cond=%d, ifpart=%d, else=%d, cont=%d\n", pc,tmp1,tmp2,tmp3); #endif /* this is subtle. incrementing the program counter points it to the assembled code for the condition. run the condition and resume operation at the continuation branch. */ pc++; if((runval = run(mach,prog,pc,uid,euid)) != RUN_OK) return(runval); d1t = stack_pop(mach,&d1); if(test_bool(d1t,d1)) { if(tmp1 != 0) { runval = run(mach,prog,tmp1,uid,euid); if(runval != RUN_OK) return(runval); } } else { if(tmp2 != 0) { runval = run(mach,prog,tmp2,uid,euid); if(runval != RUN_OK) return(runval); } } if(returning) return(0); /* cheat. this will be post-incremented, so frob it */ pc = tmp3 - 1; break; case OP_FOR: #ifdef RUNDEBUG printf("OP_FOR at pc=%d in in prog %d\n",pc,prog); #endif /* dt2 is used here as a scratch to hold the name of the symbol we stuff the values into. */ pc++; d2.c = prog->p_str + CURRINST(prog,pc); /* name of var */ pc++; tmp1 = CURRINST(prog,pc); /* do-part */ pc++; tmp2 = CURRINST(prog,pc); /* continuation */ /* as in the OP_IF, the next value past the program counter is the condition (in this case the loop list generator */ pc++; /* run the machine once to generate a list. */ if((runval = run(mach,prog,pc,uid,euid)) != RUN_OK) return(runval); /* the thing on the stack had BETTER be a list */ d1t = stack_pop(mach,&d1); if(d1t != TYP_OLIST) { pc = tmp2 - 1; break; } /* now locate - or create the iterator variable. */ sp = symlook(d2.c,CURRSTAB(mach)); if(sp == 0) { sp = symnew(d2.c,TYP_OBJ,d2); if(sp == (Sym *)0) break; CURRSTAB(mach) = symadd(sp,CURRSTAB(mach)); } /* tmp3 is used as list iterator */ if((lp = listtmpcopy(d1.ol)) == (ObjList *)0) return(1); tmp3 = 0; while(tmp3 < lp->l_cnt) { /* reset the iterator for each loop */ sp->data.l = lp->l_data[tmp3]; #ifdef RUNDEBUG printf("OP_FOR iterator %s now %d\n",sp->name,sp->data.l); #endif if(tmp1 != 0) { runval = run(mach,prog,tmp1,uid,euid); if(runval != RUN_OK) return(runval); if(returning) { return(0); } } tmp3++; } /* now continue */ pc = tmp2 - 1; break; case OP_FORARG: #ifdef RUNDEBUG printf("OP_FORARG at pc=%d in in prog %d\n",pc,prog); #endif /* d2 is used here as a scratch to hold the name of the symbol we stuff the values into. */ pc++; d2.c = prog->p_str + CURRINST(prog,pc); /* name of var */ pc++; tmp2 = CURRINST(prog,pc); /* continuation */ pc++; sp = symlook(d2.c,CURRSTAB(mach)); if(sp == 0) { sp = symnew(d2.c,TYP_OBJ,d2); if(sp == (Sym *)0) break; CURRSTAB(mach) = symadd(sp,CURRSTAB(mach)); } tmp3 = 0; while(tmp3 < mach->m_fram[mach->m_fp - 1].ret_ac) { int sa; /* reset the iterator for each loop */ sa = mach->m_fram[mach->m_fp - 1].ret_sp + tmp3 + 1; sp->data = mach->m_mem[sa].oper; sp->typ = mach->m_mem[sa].typ; #ifdef RUNDEBUG printf("OP_FORARG iterator %s now %d, type=%d, val=%d\n",sp->name,tmp3,sp->typ,sp->data); #endif runval = run(mach,prog,pc,uid,euid); if(runval != RUN_OK) return(runval); if(returning) { return(0); } tmp3++; } /* now continue */ pc = tmp2 - 1; break; /* push a numeric value from program space onto the stack. */ case OP_NPUSH: #ifdef RUNDEBUG printf("OP_NPUSH at pc=%d in in prog %d\n",pc,prog); #endif pc++; d2.i = CURRINST(prog,pc); stack_push(mach,TYP_NUM,d2); break; /* pop (and throw away) a value off the stack */ case OP_POP: #ifdef RUNDEBUG printf("OP_POP at pc=%d in in prog %d\n",pc,prog); #endif (void)stack_pop(mach,&d1); break; /* drop off the end of a function - paste the stack together */ case OP_RETF: #ifdef RUNDEBUG printf("OP_RETF at pc=%d in in prog %d\n",pc,prog); #endif if((framep = frame_pop(mach)) == 0) return(-1); mach->m_pc = framep->ret_sp; d2.i = ERR_NOVAL; stack_push(mach,TYP_NULL,d2); returning = 0; return(0); /* return from a function call. this is done by popping a stack frame, using it to reset the stack, and pushing a fake value onto the stack, or a real one, depending. */ case OP_RETNV: case OP_RETV: #ifdef RUNDEBUG printf("OP_RETNV/RETV at pc=%d in in prog %d\n",pc,prog); #endif /* if returning a value, acquire a value to return. */ if(op == OP_RETV) d1t = stack_pop(mach,&d1); if((framep = frame_pop(mach)) == 0) return(-1); mach->m_pc = framep->ret_sp; if(op == OP_RETNV) { d2.i = ERR_NOVAL; stack_push(mach,TYP_NULL,d2); } else { stack_push(mach,d1t,d1); } returning++; return(0); /* push #0 */ case OP_ROOTPUSH: #ifdef RUNDEBUG printf("OP_ROOTPUSH at pc=%d in in prog %d\n",pc,prog); #endif d2.l = (long)0; stack_push(mach,TYP_OBJ,d2); break; /* push the id# of an object */ case OP_RPUSH: #ifdef RUNDEBUG printf("OP_RPUSH at pc=%d in in prog %d\n",pc,prog); #endif pc++; d2.l = CURRINST(prog,pc); stack_push(mach,TYP_OBJ,d2); break; case OP_ACTPUSH: #ifdef RUNDEBUG printf("OP_ACTPUSH at pc=%d in in prog %d\n",pc,prog); #endif d1.l = *uid; stack_push(mach,TYP_OBJ,d1); break; case OP_SELFPUSH: #ifdef RUNDEBUG printf("OP_SELFPUSH at pc=%d in in prog %d\n",pc,prog); #endif if(mach->m_fp == 0) d1.l = *uid; else d1.l = mach->m_fram[mach->m_fp - 1].cur_obj; stack_push(mach,TYP_OBJ,d1); break; case OP_CALLPUSH: #ifdef RUNDEBUG printf("OP_CALLPUSH at pc=%d in in prog %d\n",pc,prog); #endif if(mach->m_fp < 2) d1.l = *uid; else d1.l = mach->m_fram[mach->m_fp - 2].cur_obj; stack_push(mach,TYP_OBJ,d1); break; /* push a parameter value onto the stack. this will only occur in a function(guaranteed). sneak a look at the stack frame, find the offset to the value in question, and push it again - unless it is invalid. */ case OP_PPUSH: #ifdef RUNDEBUG printf("OP_PPUSH at pc=%d in in prog %d\n",pc,prog); #endif pc++; /* sanity chex */ if(CURRINST(prog,pc) > mach->m_fram[mach->m_fp - 1].ret_ac || CURRINST(prog,pc) <= 0) { d2.i = ERR_NOPAR; stack_push(mach,TYP_NULL,d2); } else { int sa; sa = mach->m_fram[mach->m_fp - 1].ret_sp + CURRINST(prog,pc); stack_push(mach,mach->m_mem[sa].typ,mach->m_mem[sa].oper); } break; /* push a string onto the stack from program space. use the index into the program string space (the next value in the program space) to get a pointer to the string, which is pushed onto the stack. */ case OP_SPUSH: case OP_VPUSH: #ifdef RUNDEBUG printf("OP_SPUSH at pc=%d in in prog %d\n",pc,prog); #endif if(op == OP_SPUSH) d1t = TYP_STR; else d1t = TYP_VAR; pc++; d2.c = prog->p_str + CURRINST(prog,pc); stack_push(mach,d1t,d2); break; case OP_AND: case OP_OR: #ifdef RUNDEBUG printf("OP_OR/AND at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); d2t = stack_pop(mach,&d2); if(op == OP_AND) d1.i = (test_bool(d1t,d1) && test_bool(d2t,d2)); else d1.i = (test_bool(d1t,d1) || test_bool(d2t,d2)); stack_push(mach,TYP_NUM,d1); break; /* negate/invert by popping top value, neg/inverting it, and pushing the result. */ case OP_NEG: #ifdef RUNDEBUG printf("OP_NEG at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); if(d1t != TYP_NUM) { d2.i = ERR_NUM; stack_push(mach,TYP_NULL,d2); break; } d1.i = - (d1.i); stack_push(mach,d1t,d1); break; /* invert by popping top value, inverting it, and pushing the result. */ case OP_NOT: #ifdef RUNDEBUG printf("OP_NOT at pc=%d in in prog %d\n",pc,prog); #endif d1t = stack_pop(mach,&d1); d2.i = !(test_bool(d1t,d1)); stack_push(mach,TYP_NUM,d2); break; default: logf("run: unknown opcode ",itoa(CURRINST(prog,pc)),"\n",0); runstate = RUN_ILLEGAL; } pc++; #ifdef STACKDEBUG stack_dump(mach); #endif } #ifdef RUNDEBUG /* reached when we hit OP_STOP */ if(mach->m_pc != 0 && mach->m_fp == 0) printf("WARNING stack not drained!! ptr=%d\n",mach->m_pc); printf("returned from run, returning=%d\n",returning); #endif returning = 0; return(0); } void resetmachine(m) Machine *m; { #ifdef RUNDEBUG printf("reset machine\n"); #endif #ifdef CPUTICKDEBUG if(cputicks != 0) printf("CPU ticks used: %d ticks\n",cputicks); #endif /* clobber the symbols at lev. 0 if any */ m->m_fp = 0; if(CURRSTAB(m) != (Sym *)0) symfreelist(CURRSTAB(m)); CURRSTAB(m) = (Sym *)0; m->m_pc = 0; cputicks = 0; runstate = RUN_OK; } #ifdef STACKDEBUG stack_dump(m) Machine *m; { int f; printf("stack pointer is at ::%d\n",m->m_pc); for(f = m->m_pc - 1; f >= 0; f--) { switch(m->m_mem[f].typ) { case TYP_FUNC: printf("FUNC %d\t%d\n",f,m->m_mem[f].oper.p); break; case TYP_NULL: printf("NULL %d\n",f); break; case TYP_ELEM: printf("ELEM %d\t%d.%s\n",f, ELENUM(m->m_mem[f].oper.c), ELENAM(m->m_mem[f].oper.c)); break; case TYP_OBJ: printf("OBJ %d\t%d\n",f,m->m_mem[f].oper.l); break; case TYP_OLIST: printf("OBJLIST %d\t#items%d\n",f,m->m_mem[f].oper.ol->l_cnt); break; case TYP_STR: printf("STR %d\t%s\n",f,m->m_mem[f].oper.c); break; case TYP_VAR: printf("VAR %d\t%s\n",f,m->m_mem[f].oper.c); break; case TYP_NUM: printf("NUM %d\t%d\n",f,m->m_mem[f].oper.i); break; default: printf("???(%d) %d\t%d\n",m->m_mem[f].typ,f,m->m_mem[f].oper.i); } } } #endif