From 7cfad439f8ed83e914be930a0193dd7cc204a811 Mon Sep 17 00:00:00 2001 From: n loewen Date: Sat, 26 Aug 2023 12:57:27 +0100 Subject: [PATCH] run-cpu / cpu - Change command-line interface to use more standard flags (--debug --step --pretty) --- cpu.js | 52 ++++++++++++++++++++++++++------------------ run-cpu.js | 63 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 75 insertions(+), 40 deletions(-) diff --git a/cpu.js b/cpu.js index ee9e414..56b3370 100644 --- a/cpu.js +++ b/cpu.js @@ -322,8 +322,10 @@ function startCPU(code) { /** * Execute just the next instruction in memory + * @param {Object} debugInfo + * @param {Boolean} [debug] - Print machine status and the line of code being executed **/ -async function stepCPU(debugInfo, debug = false) { +async function stepCPU(debugInfo, debug = false, prettyPrintDisplay = false) { if (CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops if (CPU.cycleCounter > CYCLE_LIMIT) { console.warn('HALTING - reached cycle limit'); @@ -351,33 +353,41 @@ async function stepCPU(debugInfo, debug = false) { CPU.cycleCounter += 1; } } - logCPUState(debugInfo, debug); + logCPUState(debugInfo, debug, prettyPrintDisplay); } /** - * @param {Uint8Array} code - Machine code to run - * @param {any} debugInfo TODO type - * @param {Boolean} [debug] - Enable/disable debugging printouts - * @param {Number} [clockSpeed] - CPU clock speed in milliseconds + * @param {Uint8Array} code - Machine code to run + * @param {Object} debugInfo TODO type + * @param {Boolean} [debug] - Enable/disable debugging printouts + * @param {Boolean} [singleStep] + * @param {Boolean} [prettyPrint] - Print display with black and white emoji, instead of in hex + * @param {Number} [clockSpeed] - CPU clock speed in milliseconds **/ -exports.runProgram = (code, debugInfo, debug = false, clockSpeed = 100) => { - startCPU(code); - // Animate the output by pausing between steps - const loop = setInterval(async () => { - stepCPU(debugInfo, debug); - if (!CPU.running) clearInterval(loop); - }, clockSpeed); -} +exports.runProgram = + (code, debugInfo, debug=false, singleStep=false, prettyPrint=false, clockSpeed=100) => { + if (singleStep) { + this.singleStepProgram(code, debugInfo, debug, prettyPrint); + } else { + startCPU(code); + // Animate the output by pausing between steps + const loop = setInterval(async () => { + stepCPU(debugInfo, debug, prettyPrint); + if (!CPU.running) clearInterval(loop); + }, clockSpeed); + } + }; /** * @param {Uint8Array} code - Machine code to run * @param {any} debugInfo - TODO * @param {Boolean} [debug] - Enable/disable debugging printouts + * @param {Boolean} [prettyPrintDisplay] - Print display using black and white emoji **/ -exports.singleStepProgram = (code, debugInfo, debug = false) => { +exports.singleStepProgram = (code, debugInfo, debug = false, prettyPrintDisplay = false) => { startCPU(code); while (CPU.running) { - stepCPU(debugInfo, debug); + stepCPU(debugInfo, debug, prettyPrintDisplay); // FIXME: this prevents exiting with Ctrl+C: let key = readlineSync.keyIn('S to step, Q to quit > ', { limit: ['s', 'S', 'q', 'Q'], @@ -393,19 +403,19 @@ exports.singleStepProgram = (code, debugInfo, debug = false) => { /** * @param {Boolean} [debug] - Enable/disable debugging printouts **/ -function logCPUState(debugInfo, debug = false) { - console.group(`Step ${CPU.cycleCounter}`); +function logCPUState(debugInfo, debug = false, prettyPrintDisplay = false) { + console.group(`Line ${debugInfo[CPU.previousIP].lineNumber} - Step ${CPU.cycleCounter}`); console.log(); if (!debug) console.clear(); - if (debug) { + if (debug && !prettyPrintDisplay) { display.printDisplay(CPU.memory); } else { display.prettyPrintDisplay(CPU.memory); } console.log(); console.log(`Line ${debugInfo[CPU.previousIP].lineNumber}: ${debugInfo[CPU.previousIP].source}`); - console.log('Mnemonic:', CPU.currentInstruction.mnemonic); - console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`); + console.log(' Mnemonic:', CPU.currentInstruction.mnemonic); + console.log(` Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`); console.log(); console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} ONZC: ${num2bin_4bit(CPU.FLAGS)}`); console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])}  ${CPU.running ? "running" : "halted" }`); diff --git a/run-cpu.js b/run-cpu.js index eb07704..a9cd7d1 100755 --- a/run-cpu.js +++ b/run-cpu.js @@ -1,30 +1,55 @@ #!/usr/bin/env node -// Run: `./run-cpu.js run assembly.asm` -// Debug: `./run-cpu.js debug assembly.asm` -// Run with single-stepping: `./run-cpu.js step assembly.asm` -// Debug with single-stepping: `./run-cpu.js stepdebug assembly.asm` +// Usage: ./run-cpu.js -f code.asm [--debug] [--step] [--pretty] const fs = require('fs'); const computer = require('./cpu.js'); const assembler = require('./assembler.js'); -const { logRunningHeader, logMemory } = require('./logging.js'); +const { logRunningHeader } = require('./logging.js'); -const mode = process.argv[2]; -// console.log(`Reading ${filename}`); -const filename = process.argv[3]; +// Load file... + +let filename = null; +process.argv.forEach((arg, index) => { + if (arg === '-f' && process.argv.length > (index -1)) { + filename = process.argv[index + 1]; + } +}); + +if (!filename) { + console.error('Filename required'); + console.error('eg: run-cpu.js -f code.asm'); + process.exit() +} + const inputFile_str = fs.readFileSync(filename, 'utf8'); + +// Check optional arguments... + +let debug = false; +let singleStep = false; +let prettyPrint = false; +process.argv.forEach((arg) => { if (arg === '--debug') { debug = true } }); +process.argv.forEach((arg) => { if (arg === '--step') { singleStep = true } }); +process.argv.forEach((arg) => { if (arg === '--pretty') { prettyPrint = true } }); + +let speed = null; +process.argv.forEach((arg, index) => { + if (arg === '--speed' && process.argv.length > (index -1)) { + speed = parseInt(process.argv[index + 1]); + } +}); + + let assemblerOutput = assembler.assemble(inputFile_str); -if (mode === "debug") { - logRunningHeader(); - computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); -} else if (mode === "stepdebug") { - logRunningHeader(); - computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); -} else if (mode === "step") { - computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false); -} else { - computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false, 200); -} \ No newline at end of file +logRunningHeader(); +computer.runProgram( + assemblerOutput.machineCode, + assemblerOutput.debugInfo, + debug, + singleStep, + prettyPrint, + speed +); \ No newline at end of file