class eduProc { reset() { // initialise instruction and data memory this.memory = new Array(65536).fill(0); this.state = 0; // 0=fetch, 1=decode, 2=execute; // store the registers as private members this.programCounter = 0x0000; // 16 bits this.instructionRegister = 0x0000; // 16 bits this.memoryAddressRegister = 0x0000; // 16 bits //memoryDataRegister = 0x00; // 8 bits this.flags = 0x0; // 4 bits this.flagNames = { input: 4, overflow: 3, negative: 2, carry: 1, zero: 0 } this.registers = [0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00]; //8 bits // human friendly names for the four special purpose registers this.regNames = { instruction_page: 0, data_page: 1, stack_pointer: 2, serial_io: 3 }; this.stack_page = 0xFF; } freeze() { // todo } constructor() { reset(); } // handle flags setFlag(flagName, onoff) { // get bit position of flag const bitPosition = this.flagNames[flagName]; // ensure we have a boolean onoff = onoff ? 1 : 0; // clear the bit in the bitmask, so we can replace it with the provided value const clearMask = ~(1<=0 ? result % 255 : 255+result+1; // overflow if positive, underflow if negative // write result and flags if(operand!=6) { // don't write result of cmp this.registers[dest] = result; } if(opcode==7) { // move instruction: don't affect flags return; } this.setFlag("zero",result==0); this.setFlag("negative",getBit(result,7)); if(opcode==2||opcode==3) { // logic instruction: only zero and negative flag are touched return; } this.setFlag("carry", result>255||result<0); op1_sign = this.getBit(operand_1,7); op2_sign = this.getBit(operand_2,7); result_sign = this.getBit(result,7); this.setFlag("overflow",~(op1_sign^op2_sign) && (op1_sign^result_sign)); // sets flag if two inputs have same sign but output has different } // load a new program into memory and reset the processor loadProgram(bytecode) { reset(); this.memory.splice(0,bytecode.length, ...bytecode); freeze(); } // FETCH STAGE: load instruction from memory and increment PC fetch(stage=0) { if (stage=0) this.instructionRegister = this.memory[this.programCounter]<<8; else this.instructionRegister += this.memory[this.programCounter]; this.programCounter = (this.programCounter+1)%65535; } execute() { instruction = this.instructionRegister; opcode = instruction>>12; addressingMode = this.getBit(instruction, 4); // 0 = from reg, 1 = immediate operand_1 = (instruction>>8)&0x7 // extract operand 1 operand_2 = instruction&0xFF // extract second byte if(!addressingMode) { // not immediate data - load from register instead operand_2 = this.registers[operand_2>>5]; } if(opcode<8) { // this means ALU INSTRUCTION this.ALU(opcode,operand_1,operand_2); } else { data_address = this.regNames["data_page"]<<8+operand_2; branch_address = this.regNames["instruction_page"]<<8+operand_2; stack_pointer = this.stack_page+this.regNames["stack_pointer"]; switch(opcode){ case 8: // load this.registers[operand_1] = this.memory[data_address]; break; case 9: //store this.memory[data_address] = this.registers[operand_1]; break; case 10: // stack if(!addressingMode){ // push this.memory[stack_pointer] = this.registers[operand_1]; this.regNames["stack_pointer"] -= 1; if(this.regNames["stack_pointer"]<0) this.regNames["stack_pointer"]=255; } else { // pop this.registers[operand_1]=this.memory[stack_pointer]; this.regNames["stack_pointer"]=(this.regNames["stack_pointer"]+1)%256 } break; case 11: // beqz if(this.getFlag("zero")) this.programCounter = branch_address; break; case 12: // bneg if(this.getFlag("negative")) this.programCounter = branch_address; break; case 13: // binput if(this.getFlag("input")) this.programCounter = branch_address; break; case 14: if(this.getFlag("overflow")) this.programCounter = branch_address; break; case 15: this.programCounter = branch_address; break; } } } cycle(instructionStep=false) { if(this.state==0||instructionStep) this.fetch(0); if(this.state==1||instructionStep) this.fetch(1); if(this.state==2||instructionStep) this.execute(); this.state = instructionStep ? 0 :(this.state+1)%3; } }