View raw
//integer instructions
IADD = 1; //number add
ISUB = 2; //number subtract
IMUL = 3; //number multiply
ILT = 4; //number less than
ILET = 5; //number less than
IEQ = 6; //number equal
BR = 7; //branch
BRT = 8; //branch if true
BRF = 9; //branch if false
CONST = 10; //number const
LOAD = 11; //load local
GLOAD = 12; //load from global
GSTORE = 13; //store in global
PRINT = 14;//output
POP = 15;//pop value from stack
CALL = 16;//call procedure at address
RET = 17;//return result from local computation
HALT = 18;//end execution
//BOOLEAN VALUES
B_TRUE = 1;
B_FALSE = 0;
var instructions={};// = {
instructions[IADD]= {name: "IADD", nop: 0};
instructions[ISUB]= {name: "ISUB", nop: 0};
instructions[IMUL]= {name: "IMUL", nop: 0};
instructions[ILT]= {name: "ILT", nop: 0};
instructions[ILET]= {name: "ILET", nop: 0};
instructions[IEQ]= {name: "IEQ", nop: 0};
instructions[CONST]= {name: "CONST", nop: 1};
instructions[BR]= {name: "BR", nop: 1};
instructions[BRT]= {name: "BRT", nop: 1};
instructions[BRF]= {name: "BRF", nop: 1};
instructions[LOAD]= {name: "LOAD", nop: 1};
instructions[GSTORE]= {name: "GSTORE", nop: 1};
instructions[GLOAD]= {name: "GLOAD", nop: 1};
instructions[PRINT]= {name: "PRINT", nop: 0};
instructions[POP]= {name: "POP", nop: 0};
instructions[CALL]= {name: "CALL", nop: 2};
instructions[RET]= {name: "RET", nop: 0};
instructions[HALT]= {name: "HALT", nop: 0};
var vm = function(program, startat, configs) {
//reset method
this.reset= function(program, startat, configs)
{
//REGISTERS
this.sp = -1;//stack pointer
this.fp = 0;//frame pointer
//read more from here: https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers
this.ip = startat || 0;//instruction pointer (program counter)
this.lastip= -1;
configs= configs || {};
var datasize= configs.datasize || 100;
var stacksize= configs.stacksize || 30;
this.trace= configs.trace || false;
configs.IO= configs.IO || {};
this.IO= {};
this.IO.print= (typeof configs.IO.print == "function")? configs.IO.print: (function(text, endl, color){
console.log(text);
});
this.IO.clear= (typeof configs.IO.clear == "function")? configs.IO.clear : (function(){});
//memory
this.data = new Array(datasize);
this.stack = new Array(stacksize);
//get program in memory
this.code = program || [];
this.isIdle= true;
}
this.reset(program, startat, configs);
//program executor
this.run = function() {
var stepped= false;
if(this.isIdle) this.isIdle= false;
while ((!this.trace && this.ip < this.code.length) || (!stepped && this.trace))
{
//fetch
var opcode = this.code[this.ip];
//update program counter
this.lastip= this.ip;
this.ip++;
//decode
switch (opcode)
{
case CONST:
var v = this.code[this.ip];
this.ip++; //jump over argument
this.sp++;
this.stack[this.sp] = v;
break;
case POP:
var a= this.stack[this.sp--];
case IADD:
var a= this.stack[this.sp--];
var b= this.stack[this.sp--];
this.stack[++this.sp]= a+b;
break;
case ISUB:
var b= this.stack[this.sp--];
var a= this.stack[this.sp--];
this.stack[++this.sp]= a-b;
break;
case IMUL:
var a= this.stack[this.sp--];
var b= this.stack[this.sp--];
this.stack[++this.sp]= a*b;
break;
case ILT:
var b= this.stack[this.sp--];
var a= this.stack[this.sp--];
this.stack[++this.sp]= (a>b)? B_TRUE: B_FALSE;
break;
case ILET:
var b= this.stack[this.sp--];
var a= this.stack[this.sp--];
this.stack[++this.sp]= (a>=b)? B_TRUE: B_FALSE;
break;
case IEQ:
var b= this.stack[this.sp--];
var a= this.stack[this.sp--];
this.stack[++this.sp]= (a==b)? B_TRUE: B_FALSE;
break;
case BR:
this.ip= this.code[this.ip++];
break;
case BRT:
var addr= this.code[this.ip++];
if(this.stack[this.sp--]== B_TRUE) this.ip= addr;
break;
case BRF:
var addr= this.code[this.ip++];
if(this.stack[this.sp--]== B_FALSE) this.ip= addr;
break;
case LOAD:
var offset= this.code[this.ip++];
this.stack[++this.sp]= this.stack[this.fp+offset];
break;
case GSTORE:
var v= this.stack[this.sp];
this.sp--;
var addr= this.code[this.ip];
this.ip++;
this.data[addr]= v;
break;
case GLOAD:
var addr= this.code[this.ip];
this.ip++;
var v= this.data[addr];
this.sp++;
this.stack[this.sp]=v;
break;
case PRINT:
var v = this.stack[this.sp];
this.sp--;
this.IO.print("OUT: " + v, 1);
break;
case CALL:
var addr= this.code[this.ip++];
var nargs= this.code[this.ip++];
this.stack[++this.sp]= nargs;//add the number of args
this.stack[++this.sp]= this.fp;//add the current frame pointer value
this.stack[++this.sp]= this.ip;//add the return address
this.fp= this.sp;//update the frame pointer value
this.ip= addr;//jump to the address of the procedure
break;
case RET:
var rvalue= this.stack[this.sp--];
this.sp= this.fp;//send the stak pointer to the beginning of the local frame
this.ip= this.stack[this.sp--];//get the program counter to its prev pos
this.fp= this.stack[this.sp--];//assign the frame pointer to its prev value, before the call
var nargs= this.stack[this.sp--];//get the number of args
this.sp-= nargs;//reduce the stack pointer to destroy the call args later on
this.stack[++this.sp]= rvalue;//push the return value on the stack
break;
case HALT:
this.isIdle= true;
}
stepped= true;
if(this.isIdle) return;
}
}
}