// NOTES: // // - instructions are two bytes long: // one byte for the opcode, one for the argument // STATE const CPU = { running: false, IP: 0, CF: 0, Acc: 0, memory: null, loadMemory: (data) => { // data: Uint8Array // TODO: check length of data CPU.memory = data; }, } // FUNCTIONS THAT MODIFY STATE const Instructions = { end: () => { console.log('END'); CPU.running = false; }, store_lit: (lit) => { console.log('STO lit#'); CPU.memory[lit] = CPU.Acc; logTableTitled(CPU.memory, 'Current memory'); CPU.IP = CPU.IP += 2; }, store_addr: (addr) => { console.log('STO addr'); CPU.memory[CPU.memory[addr]] = CPU.Acc; logTableTitled(CPU.memory, 'Memory'); CPU.IP = CPU.IP += 2; }, load_lit: (lit) => { console.log('LDA lit#'); CPU.Acc = lit; CPU.IP = CPU.IP += 2; }, load_addr: (addr) => { console.log('LDA addr'); console.log('mem at addr: ', CPU.memory[addr]); CPU.Acc = CPU.memory[addr]; CPU.IP = CPU.IP += 2; }, add_lit: (lit) => { console.log("ADD lit"); let sum = CPU.Acc + lit; if (sum > 15) { CPU.CF = 1; CPU.Acc = (sum % 15) - 1; } else { CPU.CF = 0; CPU.Acc = sum; } CPU.IP = CPU.IP += 2; }, add_addr: (addr) => { console.log("ADD addr"); let sum = CPU.Acc + CPU.memory[addr]; if (sum > 15) { CPU.CF = 1; CPU.Acc = (sum % 15) - 1; } else { CPU.CF = 0; CPU.Acc = sum; } CPU.IP = CPU.IP += 2; }, sub_lit: (lit) => { // TODO: carry flag console.log("SUB lit"); CPU.Acc = CPU.Acc - lit; CPU.IP = CPU.IP += 2; }, sub_addr: (addr) => { // TODO: carry flag console.log("SUB addr"); CPU.Acc = CPU.Acc - CPU.memory[addr]; CPU.IP = CPU.IP += 2; }, hop_lit: (lit) => { console.log("HOP lit"); console.log(` ↳ Memory at IP+2 and +3: ${CPU.memory[CPU.IP+2]}, ${CPU.memory[CPU.IP+3]}`); if (CPU.Acc === lit) { CPU.IP += 4; } else { CPU.IP += 2; } }, hop_addr: (addr) => { console.log("HOP addr"); if (CPU.Acc === CPU.memory[addr]) { CPU.IP += 4; } else { CPU.IP += 2; } }, jump_lit: (lit) => { console.log("JMP lit"); CPU.IP = lit; }, jump_addr: (addr) => { console.log("JMP addr"); CPU.IP = CPU.memory[addr]; }, carry_clear: () => { console.log("CFC"); CPU.CF = 0; CPU.IP += 2; }, carry_hop: () => { console.log("CHP"); console.log(` ↳ Memory at IP+2 and +3: ${CPU.memory[CPU.IP+2]}, ${CPU.memory[CPU.IP+3]}`); console.table(CPU.memory); if (CPU.CF != 0) { CPU.IP += 4; } else { CPU.IP += 2; } }, } const opcodes2mnemonics = { 0: (arg) => Instructions.end(arg), 1: (arg) => Instructions.store_lit(arg), 2: (arg) => Instructions.store_addr(arg), 3: (arg) => Instructions.load_lit(arg), 4: (arg) => Instructions.load_addr(arg), 5: (arg) => Instructions.add_lit(arg), 6: (arg) => Instructions.add_addr(arg), 7: (arg) => Instructions.sub_lit(arg), 8: (arg) => Instructions.sub_addr(arg), 9: (arg) => Instructions.hop_lit(arg), 10: (arg) => Instructions.hop_addr(arg), 11: (arg) => Instructions.jump_lit(arg), 12: (arg) => Instructions.jump_addr(arg), 13: (arg) => Instructions.carry_clear(arg), 14: (arg) => Instructions.carry_hop(arg), }; function stepCPU() { console.group("Step CPU"); let opcode = CPU.memory[CPU.IP]; let argument = CPU.memory[CPU.IP+1]; console.log(`OP: ${opcode} ARG: ${argument}`); let instruction = opcodes2mnemonics[opcode]; instruction(argument); logCPUState(); console.groupEnd("Step CPU"); } function runCPU() { const initialMemory = JSON.parse(JSON.stringify(CPU.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("————————————————————————————————————————"); logCPUState(); CPU.running = true; for (let i = 0; i < 255; i++) { // FIXME: temporary limit as a lazy way to halt infinite loops if (!CPU.running) break; if (CPU.IP >= CPU.memory.length) break; stepCPU(); }; } // FUNCTIONS THAT PULL INFO FROM STATE TO DISPLAY function logCPUState() { console.log(); console.group('CPU state'); console.log( `Acc: ${CPU.Acc} IP: ${CPU.IP} CF: ${CPU.CF}  ${CPU.running ? "running" : "halted" }` ); console.log(); console.groupEnd('CPU state'); }; // FUNCTIONS FOR DISPLAYING DATA function num2hex(num) { return num.toString(16) }; function hex2num(hex) { return parseInt(hex, 16) }; logTableTitled = (memory, tableTitle) => { console.log(); console.group(tableTitle); console.table(memory); console.groupEnd(tableTitle); }; // RUN IT ! const test_lda_sto = new Uint8Array([ 3, 17, // LDA lit ... Acc = 17 1, 14, // STO lit ... @14 = Acc = 17 3, 16, // LDA lit ... Acc = 16 1, 15, // STO lit ... @15 = Acc = 16 2, 15, // STO addr ... @[@15] = Acc = 16 ... mem[mem[addr]] <- Acc 4, 0, // LDA addr ... Acc = @00 = 03 0, 0, // END 0, 0, // DATA ]); let test_add_sub_nocarry = new Uint8Array([ 5, 6, // ADD lit ... acc = 6 7, 1, // SUB lit ... acc = 5 1, 15, // STO lit ... mem[15] = 5 6, 15, // ADD addr ... acc = 10 8, 15, // SUB addr ... acc = 5 0, 0, // END 0, 0, 0, 0, ]); let test_hop = new Uint8Array([ 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 = new Uint8Array([ 11, 8, // 0 ... JMP lit 0, 0, // 2 ... END ... JMP'd over 0, 0, // 4 0, 0, // 6 5, 8, // 8 ... ADD lit ... acc = 8 0, 0, // 10 ... END ]); const test_chp = new Uint8Array([ 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 ]); //CPU.loadMemory(test_lda_sto); //CPU.loadMemory(test_add_sub_nocarry); //CPU.loadMemory(test_hop); CPU.loadMemory(test_jmp); //CPU.loadMemory(test_chp); runCPU();