From b173d46cb6855ce074c911ad70725e6a68cf274b Mon Sep 17 00:00:00 2001 From: n loewen Date: Mon, 28 Aug 2023 16:25:43 -0400 Subject: [PATCH] cpu - Change structure of CPU object to group State and Debug info --- src/cpu.js | 233 ++++++++++++++++++++++++++++------------------------- 1 file changed, 125 insertions(+), 108 deletions(-) diff --git a/src/cpu.js b/src/cpu.js index fa1d567..d6861f2 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -16,39 +16,56 @@ const display = require('./display.js'); // STATE const CPU = { - // Core state - running: false, - IP: INITIAL_IP_ADDRESS, - FLAGS: {'C': false, 'Z': false, 'N': false, 'O': false}, - FLAGNUMS2NAMES: {0: 'C', 1: 'Z', 2: 'N', 3: 'O'}, - Acc: 0, - memory: null, - // Functions that update core state + /** Core state **/ + state: { + running: false, + IP: INITIAL_IP_ADDRESS, + flags: {'C': false, 'Z': false, 'N': false, 'O': false}, + FLAGNUMS2NAMES: {0: 'C', 1: 'Z', 2: 'N', 3: 'O'}, + acc: 0, + memory: null, + }, + + /** Debug info **/ + debug: { + previousIP: 0, + currentInstruction: { + opcode: null, + operand: null, + mnemonic: null, + }, + cycleCounter: 0, + }, + + /** Functions that update state **/ + /** @param {Uint8Array} data */ - loadMemory: (data) => { - CPU.memory = new Uint8Array(256); - CPU.memory.set(data, 0); + loadMemory(data) { + this.state.memory = new Uint8Array(256); + this.state.memory.set(data, 0); }, - incrementIP: (offset) => { - CPU.previousIP = CPU.IP; - CPU.IP = CPU.IP + offset; - }, - setIP: (address) => { - CPU.previousIP = CPU.IP; - CPU.IP = address; - }, - updateFlagZero: () => { CPU.FLAGS.Z = CPU.Acc === 0; }, - updateFlagNegative: () => { CPU.Acc & 128 ? CPU.FLAGS.N = true : CPU.FLAGS.N = false }, - // Debug info - previousIP: 0, - currentInstruction: { - opcode: null, - operand: null, - mnemonic: null, + incrementIP(offset) { + this.state.previousIP = this.state.IP; + this.state.IP = this.state.IP + offset; }, - cycleCounter: 0, + + setIP(address) { + this.state.previousIP = this.state.IP; + this.state.IP = address; + }, + + updateFlagZero() { this.state.flags.Z = this.state.acc === 0; }, + updateFlagNegative() { this.state.acc & 128 ? this.state.flags.N = true : this.state.flags.N = false }, + + + /** Hooks **/ + + onTickHooks: [], + + /** @param {function} fn **/ + onTick(fn) { this.onTickHooks.push(fn) }, } @@ -56,154 +73,154 @@ const CPU = { const Instructions = { end: () => { - CPU.currentInstruction.mnemonic = 'END'; - CPU.running = false; + CPU.debug.currentInstruction.mnemonic = 'END'; + CPU.state.running = false; CPU.incrementIP(2); }, store_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'STO lit'; - CPU.memory[lit] = CPU.Acc; + CPU.debug.currentInstruction.mnemonic = 'STO lit'; + CPU.state.memory[lit] = CPU.state.acc; CPU.incrementIP(2); }, store_addr: (addr) => { - CPU.currentInstruction.mnemonic = `STO addr; @addr: ${num2hex(CPU.memory[addr])}`; - CPU.memory[CPU.memory[addr]] = CPU.Acc; + CPU.debug.currentInstruction.mnemonic = `STO addr; @addr: ${num2hex(CPU.state.memory[addr])}`; + CPU.state.memory[CPU.state.memory[addr]] = CPU.state.acc; CPU.incrementIP(2); }, load_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'LDA lit'; - CPU.Acc = lit; + CPU.debug.currentInstruction.mnemonic = 'LDA lit'; + CPU.state.acc = lit; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, load_addr: (addr) => { - CPU.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.memory[addr])}`; - CPU.Acc = CPU.memory[addr]; + CPU.debug.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.state.memory[addr])}`; + CPU.state.acc = CPU.state.memory[addr]; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, add_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'ADD lit'; + CPU.debug.currentInstruction.mnemonic = 'ADD lit'; // Calculate sum - let sum = CPU.Acc + lit; + let sum = CPU.state.acc + lit; if (sum > 255) { - CPU.FLAGS.C = true; + CPU.state.flags.C = true; sum = (sum % 255) - 1; } else { - CPU.FLAGS.C = false; + CPU.state.flags.C = false; } // Calculate overflow flag status let bitSixCarry = 0; - if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); + if ((CPU.state.acc & 64) && (lit & 64)) { bitSixCarry = 1; } + // let overflow = bitSixCarry ^ (CPU.state.flags & 8); // FIXME FIXME FIXME // I'm on a plane and can't remember how this works let overflow = 0; if (overflow) { - CPU.FLAGS.O = true; + CPU.state.flags.O = true; } else { - CPU.FLAGS.O = false; + CPU.state.flags.O = false; } - CPU.Acc = sum; + CPU.state.acc = sum; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, add_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'ADD addr'; + CPU.debug.currentInstruction.mnemonic = 'ADD addr'; // Calculate sum - let sum = CPU.Acc + CPU.memory[addr]; + let sum = CPU.state.acc + CPU.state.memory[addr]; if (sum > 255) { - CPU.FLAGS.C = true; + CPU.state.flags.C = true; sum = (sum % 255) - 1; } else { - CPU.FLAGS.C = false; + CPU.state.flags.C = false; } // Calculate overflow flag status let bitSixCarry = 0; - if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); + if ((CPU.state.acc & 64) && (addr & 64)) { bitSixCarry = 1; } + // let overflow = bitSixCarry ^ (CPU.state.flags & 8); // FIXME FIXME FIXME // I'm on a plane and can't remember how this works let overflow = 0; if (overflow) { - CPU.FLAGS.O = true; + CPU.state.flags.O = true; } else { - CPU.FLAGS.O = false; + CPU.state.flags.O = false; } - CPU.Acc = sum; + CPU.state.acc = sum; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, sub_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'SUB lit'; + CPU.debug.currentInstruction.mnemonic = 'SUB lit'; // Calculate sum - let sum = CPU.Acc - lit; + let sum = CPU.state.acc - lit; if (sum < 0) { - CPU.FLAGS.C = true; + CPU.state.flags.C = true; sum = sum + 256; } else { - CPU.FLAGS.C = false; + CPU.state.flags.C = false; } // Calculate overflow flag status let bitSixCarry = 0; - if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); + if ((CPU.state.acc & 64) && (lit & 64)) { bitSixCarry = 1; } + // let overflow = bitSixCarry ^ (CPU.state.flags & 8); // FIXME FIXME FIXME // I'm on a plane and can't remember how this works let overflow = 0; if (overflow) { - CPU.FLAGS.O = true; + CPU.state.flags.O = true; } else { - CPU.FLAGS.O = false; + CPU.state.flags.O = false; } - CPU.Acc = sum; + CPU.state.acc = sum; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, sub_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'SUB addr'; + CPU.debug.currentInstruction.mnemonic = 'SUB addr'; // Calculate sum - let sum = CPU.Acc - CPU.memory[addr]; + let sum = CPU.state.acc - CPU.state.memory[addr]; if (sum < 0) { - CPU.FLAGS.C = true; + CPU.state.flags.C = true; sum = sum + 256; } else { - CPU.FLAGS.C = false; + CPU.state.flags.C = false; } // Calculate overflow flag status let bitSixCarry = 0; - if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); + if ((CPU.state.acc & 64) && (addr & 64)) { bitSixCarry = 1; } + // let overflow = bitSixCarry ^ (CPU.state.flags & 8); // FIXME FIXME FIXME // I'm on a plane and can't remember how this works let overflow = 0; if (overflow) { - CPU.FLAGS.O = true; + CPU.state.flags.O = true; } else { - CPU.FLAGS.O = false; + CPU.state.flags.O = false; } - CPU.Acc = sum; + CPU.state.acc = sum; CPU.updateFlagNegative(); CPU.updateFlagZero(); CPU.incrementIP(2); }, hop_lit: (lit) => { - CPU.currentInstruction.mnemonic = `HOP lit; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`; - if (CPU.Acc === lit) { + CPU.debug.currentInstruction.mnemonic = `HOP lit; IP+2: ${CPU.state.memory[CPU.state.IP+2]}, IP+3: ${CPU.state.memory[CPU.state.IP+3]}`; + if (CPU.state.acc === lit) { CPU.incrementIP(4); } else { CPU.incrementIP(2); @@ -211,8 +228,8 @@ const Instructions = { }, hop_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'HOP addr'; - if (CPU.Acc === CPU.memory[addr]) { + CPU.debug.currentInstruction.mnemonic = 'HOP addr'; + if (CPU.state.acc === CPU.state.memory[addr]) { CPU.incrementIP(4); } else { CPU.incrementIP(2); @@ -220,13 +237,13 @@ const Instructions = { }, jump_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'JMP lit'; + CPU.debug.currentInstruction.mnemonic = 'JMP lit'; CPU.setIP(lit); }, jump_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'JMP addr'; - CPU.setIP(CPU.memory[addr]); + CPU.debug.currentInstruction.mnemonic = 'JMP addr'; + CPU.setIP(CPU.state.memory[addr]); }, flag_toggle: (flagNum) => { @@ -235,8 +252,8 @@ const Instructions = { process.exit(); } const flagName = CPU.FLAGNUMS2NAMES[flagNum]; - CPU.currentInstruction.mnemonic = `FTG ${flagName}`; - CPU.FLAGS[flagName] = !CPU.FLAGS[flagName]; + CPU.debug.currentInstruction.mnemonic = `FTG ${flagName}`; + CPU.state.flags[flagName] = !CPU.state.flags[flagName]; CPU.incrementIP(2); }, @@ -246,8 +263,8 @@ const Instructions = { process.exit(); } const flagName = CPU.FLAGNUMS2NAMES[flagNum]; - CPU.currentInstruction.mnemonic = `FHP ${flagName}; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`; - if (CPU.FLAGS[CPU.FLAGNUMS2NAMES[flagNum]]) { + CPU.debug.currentInstruction.mnemonic = `FHP ${flagName}; IP+2: ${CPU.state.memory[CPU.state.IP+2]}, IP+3: ${CPU.state.memory[CPU.state.IP+3]}`; + if (CPU.state.flags[CPU.FLAGNUMS2NAMES[flagNum]]) { CPU.incrementIP(4); } else { CPU.incrementIP(2); @@ -255,7 +272,7 @@ const Instructions = { }, no_op: () => { - CPU.currentInstruction.mnemonic = `NOP`; + CPU.debug.currentInstruction.mnemonic = `NOP`; CPU.incrementIP(2); }, } @@ -280,13 +297,13 @@ const opcodes2mnemonics = { }; /** - * Load code into memory and set CPU state to "running" + * Load code into memory and set CPU state to "state.running" * @param {Uint8Array} code - Machine code to load **/ function startCPU(code) { CPU.loadMemory(code); - CPU.cycleCounter = 0; - CPU.running = true; + CPU.debug.cycleCounter = 0; + CPU.state.running = true; // FIXME: This conflicts with single-stepping // (you can single-step, but keys aren't passed @@ -309,7 +326,7 @@ function startCPU(code) { if (key.sequence === '\x03') process.exit(); let name = key.name.toUpperCase(); if (name in KEY_MAP) { - CPU.memory[KEYPAD_ADDR] = KEY_MAP[name]; + CPU.state.memory[KEYPAD_ADDR] = KEY_MAP[name]; } }); } @@ -320,14 +337,14 @@ function startCPU(code) { * @param {Boolean} [debug] - Print machine status and the line of code being executed **/ async function stepCPU(debugInfo, debug = false, prettyPrintDisplay = false) { - if (CPU.IP >= CPU.memory.length) { + if (CPU.state.IP >= CPU.state.memory.length) { console.error('HALTING - IP greater than memory size'); - CPU.running = false; + CPU.state.running = false; process.exit(); } else { - CPU.currentInstruction.opcode = CPU.memory[CPU.IP]; - CPU.currentInstruction.operand = CPU.memory[CPU.IP+1]; - let executeInstruction = opcodes2mnemonics[CPU.currentInstruction.opcode]; + CPU.debug.currentInstruction.opcode = CPU.state.memory[CPU.state.IP]; + CPU.debug.currentInstruction.operand = CPU.state.memory[CPU.state.IP+1]; + let executeInstruction = opcodes2mnemonics[CPU.debug.currentInstruction.opcode]; if (typeof executeInstruction === 'undefined') { let info = debugInfo[CPU.previousIP]; console.error(); @@ -336,17 +353,17 @@ async function stepCPU(debugInfo, debug = false, prettyPrintDisplay = false) { console.error(` from line ${info.lineNumber}: ${info.source}`); process.exit(); } - executeInstruction(CPU.currentInstruction.operand); - CPU.cycleCounter += 1; + executeInstruction(CPU.debug.currentInstruction.operand); + CPU.debug.cycleCounter += 1; } logCPUState(debugInfo, debug, prettyPrintDisplay); if (DEFAULT_CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops - if (CPU.cycleCounter >= DEFAULT_CYCLE_LIMIT) { + if (CPU.debug.cycleCounter >= DEFAULT_CYCLE_LIMIT) { console.warn(' HALTING - reached cycle limit'); - CPU.running = false; + CPU.state.running = false; } } - if (!CPU.running) process.exit(); + if (!CPU.state.running) process.exit(); } /** @@ -366,7 +383,7 @@ exports.runProgram = // Animate the output by pausing between steps const loop = setInterval(async () => { stepCPU(debugInfo, debug, prettyPrint); - if (!CPU.running) { + if (!CPU.state.running) { logCPUState(debugInfo, debug, prettyPrint); console.log('Halted'); process.exit(); @@ -383,7 +400,7 @@ exports.runProgram = **/ exports.singleStepProgram = (code, debugInfo, debug = false, prettyPrintDisplay = false) => { startCPU(code); - while (CPU.running) { + while (CPU.state.running) { stepCPU(debugInfo, debug, prettyPrintDisplay); // FIXME: this prevents exiting with Ctrl+C: let key = readlineSync.keyIn('S to step, Q to quit > ', { @@ -402,20 +419,20 @@ exports.singleStepProgram = (code, debugInfo, debug = false, prettyPrintDisplay **/ function logCPUState(debugInfo, debug = false, prettyPrintDisplay = false) { debugInfo = debugInfo[CPU.previousIP] !== 'undefined' ? debugInfo[CPU.previousIP] : false; - console.group(`Step ${CPU.cycleCounter}`); + console.group(`Step ${CPU.debug.cycleCounter}`); console.log(); if (!debug) console.clear(); - display.show(CPU.memory, prettyPrintDisplay); + display.show(CPU.state.memory, prettyPrintDisplay); console.log(); if (debugInfo) { console.log(`Line ${debugInfo.lineNumber}: ${debugInfo.source}`); console.log(); } - console.log('Mnemonic:', CPU.currentInstruction.mnemonic); - console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`); + console.log('Mnemonic:', CPU.debug.currentInstruction.mnemonic); + console.log(`Machine: $${num2hex(CPU.debug.currentInstruction.opcode)} $${num2hex(CPU.debug.currentInstruction.operand)}`); console.log(); - console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} ONZC ${bool2bit(CPU.FLAGS.O)}${bool2bit(CPU.FLAGS.N)}${bool2bit(CPU.FLAGS.Z)}${bool2bit(CPU.FLAGS.C)}`); - console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])}  ${CPU.running ? "running" : "halted" }`); + console.log(`IP: $${num2hex(CPU.state.IP)} Acc: $${num2hex(CPU.state.acc)} ONZC ${bool2bit(CPU.state.flags.O)}${bool2bit(CPU.state.flags.N)}${bool2bit(CPU.state.flags.Z)}${bool2bit(CPU.state.flags.C)}`); + console.log(`KEY: $${num2hex(CPU.state.memory[KEYPAD_ADDR])}  ${CPU.state.running ? "state.running" : "halted" }`); console.log(); console.log(); console.groupEnd();