// This is a sketch of a simulator for my paper computer, // made for two purposes: // // (1) figuring out the basic structure for a simple simulator // (2) testing some simple programs (hopefully) // // NOTA BENE: this simple version makes naive use of // Javascript numbers for opcodes, and a Javascript array // for the computer's "memory." This may cause some problems. function CPU(mem) { this.memory = mem; this.running = false; this.instructionPointer = 0; this.carryFlag = 0; this.acc = 0; this.instructions = { end: () => { console.log('END'); this.running = false }, store_lit: (lit) => { console.log('STO lit#'); this.memory[lit] = this.acc; log_table_with_title(this.memory, 'Current memory'); this.instructionPointer = this.instructionPointer += 1; }, store_addr: (addr) => { console.log('STO addr'); this.memory[this.memory[addr]] = this.acc; log_table_with_title(this.memory, 'Memory'); this.instructionPointer = this.instructionPointer += 1; }, load_lit: (lit) => { console.log('LDA lit#'); this.acc = lit; this.instructionPointer = this.instructionPointer += 1; }, load_addr: (addr) => { console.log('LDA addr'); console.log('mem at addr: ', this.memory[addr]); this.acc = this.memory[addr]; this.instructionPointer = this.instructionPointer += 1; }, add_lit: (lit) => { console.log("ADD lit"); if ( (this.acc + lit) > 15 ) { this.carryFlag = 1; } this.acc = ((this.acc + lit) % 15); this.instructionPointer = this.instructionPointer += 1; }, add_addr: (addr) => { console.log("ADD addr"); if ( (this.acc + this.memory[addr]) > 15 ) { this.carryFlag = 1; } this.acc = ((this.acc + this.memory[addr]) % 15); this.instructionPointer = this.instructionPointer += 1; }, sub_lit: (lit) => { // TODO: carry flag console.log("SUB lit"); this.acc = this.acc - lit; this.instructionPointer = this.instructionPointer += 1; }, sub_addr: (addr) => { // TODO: carry flag console.log("SUB addr"); this.acc = this.acc - this.memory[addr]; this.instructionPointer = this.instructionPointer += 1; }, hop_lit: (lit) => { console.log("HOP lit"); console.log(" ↳ Memory at IP+1:", this.memory[this.instructionPointer+1]); if (this.acc === lit) { this.instructionPointer += 2; } else { this.instructionPointer += 1; } }, hop_addr: (addr) => { console.log("HOP addr"); if (this.acc === this.memory[addr]) { this.instructionPointer += 2; } else { this.instructionPointer += 1; } }, jump_lit: (lit) => { console.log("JMP lit"); this.instructionPointer = lit; }, jump_addr: (addr) => { console.log("JMP addr"); this.instructionPointer = this.memory[addr]; }, carry_clear: () => { console.log("CFC"); this.carryFlag = 0; this.instructionPointer += 1; }, carry_hop: () => { console.log("CHP"); if (this.carryFlag != 0) { this.instructionPointer += 2; } else { this.instructionPointer += 1; } }, }; this.perform_operation = (opcode, arg) => { switch (opcode) { case 0: this.instructions.end(arg); break; case 1: this.instructions.store_lit(arg); break; case 2: this.instructions.store_addr(arg); break; case 3: this.instructions.load_lit(arg); break; case 4: this.instructions.load_addr(arg); break; case 5: this.instructions.add_lit(arg); break; case 6: this.instructions.add_addr(arg); break; case 7: this.instructions.sub_lit(arg); break; case 8: this.instructions.sub_addr(arg); break; case 9: this.instructions.hop_lit(arg); break; case 10: this.instructions.hop_addr(arg); break; case 11: this.instructions.jump_lit(arg); break; case 12: this.instructions.jump_addr(arg); break; case 13: this.instructions.carry_clear(arg); break; case 14: this.instructions.carry_hop(arg); break; default: console.error( `Invalid opcode: ${opcode} with argument ${arg}` ); } } this.run_program = () => { const initialMemory = JSON.parse(JSON.stringify(this.memory)); // Hack to make a copy-by-value -- https://stackoverflow.com/questions/18829099/copy-a-variables-value-into-another console.log(); console.log("————————————————————————————————————————"); let time = new Date(); console.log( `Running at ${time.toLocaleTimeString('en-US')}` ); console.log("————————————————————————————————————————"); log_debug_state(); this.running = true; for (let i = 1; i < 16; i++) { if ( this.running && (this.instructionPointer < this.memory.length) ) { let op_arg_tuple = this.memory[this.instructionPointer]; console.group("Proccessing instruction"); console.log( op_arg_tuple ); // console.log( `processing opcode ${op_arg_tuple[0]} with arg ${op_arg_tuple[1]}` ); this.perform_operation(op_arg_tuple[0], op_arg_tuple[1]); log_debug_state(); console.groupEnd("Processing instruction"); } } return { memoryAtStart: initialMemory, memoryAtEnd: this.memory } } log_debug_state = () => { console.log(); console.group('CPU state'); console.log( `Acc: ${this.acc} IP: ${this.instructionPointer} CF: ${this.carryFlag}  ${this.running ? "running" : "halted" }` ); console.log(); console.groupEnd('CPU state'); }; }; log_table_with_title = (memory, tableTitle) => { console.log(); console.group(tableTitle); console.table(memory); console.groupEnd(tableTitle); }; // TESTS let halt_and_catch_fire = [ [0, 0], [1, 0], ]; let test_lda_sto = [ [3, 8], // LDA lit [1, 5], // STO lit [4, 5], // LDA addr [2, 6], // STO addr [0, 0], // END ]; let test_add_sub_nocarry = [ [5, 6], // ADD lit ... acc = 6 [7, 1], // SUB lit ... acc = 5 [1, 8], // STO lit ... mem[8] = 5 [6, 8], // ADD addr ... acc = 10 [8, 8], // SUB addr ... acc = 5 [0, 0], // END ] let test_add_sub = [ [5, 26], // ADD lit [0, 0], // END ] let test_hop = [ [5, 8], // ADD lit ... acc = 8 [9, 8], // HOP lit ... hop over next op if acc = 8 [0, 0], // END ... (hopped over) [7, 8], // SUB lit ... acc = 0 [0, 0] ] let test_jmp = [ [11, 4], // 0 [0, 0], // 1 ... END ... JMP'd over [0, 0], // 2 [0, 0], // 3 [5, 8], // 4 ... ADD lit ... acc = 8 [0, 0], // 5 ... END ] let test_chp = [ [5, 8], // ADD lit ... acc = 8 [14, 0], // CHP ... shouldn't hop (CF = 0) [5, 1], // ADD lit ... acc = 9 [5, 8], // ADD lit ... acc = 1 and CF = 1 [14, 0], // CHP ... hop! (CF = 1) [0, 0], // END [7, 1], // SUB lit ... acc = 0 [13, 0], // CFC ... CF = 0 [0, 0], // END ] //let comp = new CPU(test_chp); let comp = new CPU(test_lda_sto); let memory_snapshots = comp.run_program(); log_table_with_title(memory_snapshots.memoryAtEnd, 'Memory after running'); log_table_with_title(memory_snapshots.memoryAtStart, 'Memory before running'); // TODO: TEST HOP_addr