/* xsint.c - xscheme bytecode interpreter */ /* Copyright (c) 1988, by David Michael Betz All Rights Reserved Permission is granted for unrestricted non-commercial use */ #include "xscheme.h" #include "xsbcode.h" /* sample rate (instructions per sample) */ #define SRATE 1000 /* macros to get the address of the code string for a code object */ #define getcodestr(x) ((unsigned char *)getstring(getbcode(x))) /* globals */ int trace=FALSE; /* trace enable */ int xlargc; /* argument count */ jmp_buf bc_dispatch; /* bytecode dispatcher */ /* external variables */ extern LVAL xlfun,xlenv,xlval; extern LVAL s_stdin,s_stdout,s_unbound; extern LVAL s_unassigned,default_object,true; /* external routines */ extern LVAL xadd(),xsub(),xmul(),xdiv(),xlss(),xeql(),xgtr(); /* local variables */ static unsigned char *base,*pc; static int sample=SRATE; /* xtraceon - built-in function 'trace-on' */ LVAL xtraceon() { xllastarg() trace = TRUE; return (NIL); } /* xtraceoff - built-in function 'trace-off' */ LVAL xtraceoff() { xllastarg() trace = FALSE; return (NIL); } /* xlexecute - execute byte codes */ xlexecute(fun) LVAL fun; { LVAL findvar(),make_continuation(); register LVAL tmp; register unsigned int i; register int k; int off; /* initialize the registers */ xlfun = getcode(fun); xlenv = getenv(fun); xlval = NIL; /* initialize the argument count */ xlargc = 0; /* set the initial pc */ base = pc = getcodestr(xlfun); /* setup a target for the error handler */ setjmp(bc_dispatch); /* execute the code */ for (;;) { /* check for control codes */ if (--sample <= 0) { sample = SRATE; oscheck(); } /* print the trace information */ if (trace) decode_instruction(curoutput(),xlfun,(int)(pc-base)); /* execute the next bytecode instruction */ switch (*pc++) { case OP_BRT: i = *pc++ << 8; i |= *pc++; if (xlval) pc = base + i; break; case OP_BRF: i = *pc++ << 8; i |= *pc++; if (!xlval) pc = base + i; break; case OP_BR: i = *pc++ << 8; i |= *pc++; pc = base + i; break; case OP_LIT: xlval = getelement(xlfun,*pc++); break; case OP_GREF: tmp = getelement(xlfun,*pc++); if ((xlval = getvalue(tmp)) == s_unbound) { if (xlval = getvalue(xlenter("*UNBOUND-HANDLER*"))) { oscheck(); pc -= 2; /* backup the pc */ tmp = make_continuation(); check(2); push(tmp); push(getelement(xlfun,pc[1])); xlargc = 2; xlapply(); } else xlerror("unbound variable",tmp); } break; case OP_GSET: setvalue(getelement(xlfun,*pc++),xlval); break; case OP_EREF: k = *pc++; tmp = xlenv; while (--k >= 0) tmp = cdr(tmp); xlval = getelement(car(tmp),*pc++); break; case OP_ESET: k = *pc++; tmp = xlenv; while (--k >= 0) tmp = cdr(tmp); setelement(car(tmp),*pc++,xlval); break; case OP_AREF: i = *pc++; tmp = xlval; if (!envp(tmp)) badargtype(tmp); if ((tmp = findvar(tmp,getelement(xlfun,i),&off)) != NIL) xlval = getelement(car(tmp),off); else xlval = s_unassigned; break; case OP_ASET: i = *pc++; tmp = pop(); if (!envp(tmp)) badargtype(tmp); if ((tmp = findvar(tmp,getelement(xlfun,i),&off)) == NIL) xlerror("no binding for variable",getelement(xlfun,i)); setelement(car(tmp),off,xlval); break; case OP_SAVE: /* save a continuation */ i = *pc++ << 8; i |= *pc++; check(3); push(cvsfixnum((FIXTYPE)i)); push(xlfun); push(xlenv); break; case OP_CALL: /* call a function (or built-in) */ xlargc = *pc++; /* get argument count */ xlapply(); /* apply the function */ break; case OP_RETURN: /* return to the continuation on the stack */ xlreturn(); break; case OP_FRAME: /* create an environment frame */ i = *pc++; /* get the frame size */ xlenv = newframe(xlenv,i); setelement(car(xlenv),0,getvnames(xlfun)); break; case OP_MVARG: /* move required argument to frame slot */ i = *pc++; /* get the slot number */ if (--xlargc < 0) xlfail("too few arguments"); setelement(car(xlenv),i,pop()); break; case OP_MVOARG: /* move optional argument to frame slot */ i = *pc++; /* get the slot number */ if (xlargc > 0) { setelement(car(xlenv),i,pop()); --xlargc; } else setelement(car(xlenv),i,default_object); break; case OP_MVRARG: /* build rest argument and move to frame slot */ i = *pc++; /* get the slot number */ for (xlval = NIL, k = xlargc; --k >= 0; ) xlval = cons(xlsp[k],xlval); setelement(car(xlenv),i,xlval); drop(xlargc); break; case OP_ALAST: /* make sure there are no more arguments */ if (xlargc > 0) xlfail("too many arguments"); break; case OP_T: xlval = true; break; case OP_NIL: xlval = NIL; break; case OP_PUSH: cpush(xlval); break; case OP_CLOSE: if (!codep(xlval)) badargtype(xlval); xlval = cvclosure(xlval,xlenv); break; case OP_DELAY: if (!codep(xlval)) badargtype(xlval); xlval = cvpromise(xlval,xlenv); break; case OP_ATOM: xlval = (atom(xlval) ? true : NIL); break; case OP_EQ: xlval = (xlval == pop() ? true : NIL); break; case OP_NULL: xlval = (xlval ? NIL : true); break; case OP_CONS: xlval = cons(xlval,pop()); break; case OP_CAR: if (!listp(xlval)) badargtype(xlval); xlval = (xlval ? car(xlval) : NIL); break; case OP_CDR: if (!listp(xlval)) badargtype(xlval); xlval = (xlval ? cdr(xlval) : NIL); break; case OP_SETCAR: if (!consp(xlval)) badargtype(xlval); rplaca(xlval,pop()); break; case OP_SETCDR: if (!consp(xlval)) badargtype(xlval); rplacd(xlval,pop()); break; case OP_ADD: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = cvfixnum(getfixnum(xlval) + getfixnum(tmp)); else { push(tmp); push(xlval); xlargc = 2; xlval = xadd(); } break; case OP_SUB: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = cvfixnum(getfixnum(xlval) - getfixnum(tmp)); else { push(tmp); push(xlval); xlargc = 2; xlval = xsub(); } break; case OP_MUL: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = cvfixnum(getfixnum(xlval) * getfixnum(tmp)); else { push(tmp); push(xlval); xlargc = 2; xlval = xmul(); } break; case OP_QUO: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = cvfixnum(getfixnum(xlval) / getfixnum(tmp)); else if (fixp(xlval)) badargtype(tmp); else badargtype(xlval); break; case OP_LSS: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = (getfixnum(xlval) < getfixnum(tmp) ? true : NIL); else { push(tmp); push(xlval); xlargc = 2; xlval = xlss(); } break; case OP_EQL: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = (getfixnum(xlval) == getfixnum(tmp) ? true : NIL); else { push(tmp); push(xlval); xlargc = 2; xlval = xeql(); } break; case OP_GTR: tmp = pop(); if (fixp(xlval) && fixp(tmp)) xlval = (getfixnum(xlval) > getfixnum(tmp) ? true : NIL); else { push(tmp); push(xlval); xlargc = 2; xlval = xgtr(); } break; default: xlerror("bad opcode",cvsfixnum((FIXTYPE)*--pc)); break; } } } /* findvar - find a variable in an environment */ LOCAL LVAL findvar(env,var,poff) LVAL env,var; int *poff; { LVAL names; int lev,off; for (lev = 0; env != NIL; ++lev, env = cdr(env)) { names = getelement(car(env),0); for (off = 1; names != NIL; ++off, names = cdr(names)) if (var == car(names)) { *poff = off; return (env); } } return (NIL); } /* xlapply - apply a function to arguments */ /* The function should be in xlval and the arguments should be on the stack. The number of arguments should be in xlargc. */ xlapply() { LVAL tmp; /* check for null function */ if (null(xlval)) badfuntype(xlval); /* dispatch on function type */ switch (ntype(xlval)) { case SUBR: xlval = (*getsubr(xlval))(); xlreturn(); break; case XSUBR: (*getsubr(xlval))(); break; case CLOSURE: xlfun = getcode(xlval); xlenv = getenv(xlval); base = pc = getcodestr(xlfun); break; case OBJECT: xlsend(xlval,xlgasymbol()); break; case METHOD: xlfun = getcode(xlval); xlenv = cons(top(),getenv(xlval)); base = pc = getcodestr(xlfun); break; case CONTINUATION: tmp = xlgetarg(); xllastarg(); restore_continuation(); xlval = tmp; xlreturn(); break; default: badfuntype(xlval); } } /* xlreturn - return to a continuation on the stack */ xlreturn() { LVAL tmp; /* restore the enviroment and the continuation function */ xlenv = pop(); tmp = pop(); /* dispatch on the function type */ switch (ntype(tmp)) { case CODE: xlfun = tmp; tmp = pop(); base = getcodestr(xlfun); pc = base + (int)getsfixnum(tmp); break; case CSUBR: (*getsubr(tmp))(); break; default: xlerror("bad continuation",tmp); } } /* make_continuation - make a continuation */ LOCAL LVAL make_continuation() { LVAL cont,*src,*dst; int size; /* save a continuation on the stack */ check(3); push(cvsfixnum((FIXTYPE)(pc - base))); push(xlfun); push(xlenv); /* create and initialize a continuation object */ size = (int)(xlstktop - xlsp); cont = newcontinuation(size); for (src = xlsp, dst = &cont->n_vdata[0]; --size >= 0; ) *dst++ = *src++; /* return the continuation */ return (cont); } /* restore_continuation - restore a continuation to the stack */ /* The continuation should be in xlval. */ LOCAL restore_continuation() { LVAL *src; int size; size = getsize(xlval); for (src = &xlval->n_vdata[size], xlsp = xlstktop; --size >= 0; ) *--xlsp = *--src; } /* gc_protect - protect the state of the interpreter from the collector */ gc_protect(protected_fcn) int (*protected_fcn)(); { int pcoff; pcoff = pc - base; (*protected_fcn)(); if (xlfun) { base = getcodestr(xlfun); pc = base + pcoff; } } /* badfuntype - bad function error */ LOCAL badfuntype(arg) LVAL arg; { xlerror("bad function type",arg); } /* badargtype - bad argument type error */ LOCAL badargtype(arg) LVAL arg; { xlbadtype(arg); } /* xlstkover - value stack overflow */ xlstkover() { xlabort("value stack overflow"); }