run-cpu / cpu - Change command-line interface to use more standard flags (--debug --step --pretty)

This commit is contained in:
n loewen 2023-08-26 12:57:27 +01:00
parent 4bb394f039
commit 7cfad439f8
2 changed files with 75 additions and 40 deletions

30
cpu.js
View File

@ -322,8 +322,10 @@ function startCPU(code) {
/** /**
* Execute just the next instruction in memory * 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 (CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops
if (CPU.cycleCounter > CYCLE_LIMIT) { if (CPU.cycleCounter > CYCLE_LIMIT) {
console.warn('HALTING - reached cycle limit'); console.warn('HALTING - reached cycle limit');
@ -351,33 +353,41 @@ async function stepCPU(debugInfo, debug = false) {
CPU.cycleCounter += 1; CPU.cycleCounter += 1;
} }
} }
logCPUState(debugInfo, debug); logCPUState(debugInfo, debug, prettyPrintDisplay);
} }
/** /**
* @param {Uint8Array} code - Machine code to run * @param {Uint8Array} code - Machine code to run
* @param {any} debugInfo TODO type * @param {Object} debugInfo TODO type
* @param {Boolean} [debug] - Enable/disable debugging printouts * @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 * @param {Number} [clockSpeed] - CPU clock speed in milliseconds
**/ **/
exports.runProgram = (code, debugInfo, debug = false, clockSpeed = 100) => { exports.runProgram =
(code, debugInfo, debug=false, singleStep=false, prettyPrint=false, clockSpeed=100) => {
if (singleStep) {
this.singleStepProgram(code, debugInfo, debug, prettyPrint);
} else {
startCPU(code); startCPU(code);
// Animate the output by pausing between steps // Animate the output by pausing between steps
const loop = setInterval(async () => { const loop = setInterval(async () => {
stepCPU(debugInfo, debug); stepCPU(debugInfo, debug, prettyPrint);
if (!CPU.running) clearInterval(loop); if (!CPU.running) clearInterval(loop);
}, clockSpeed); }, clockSpeed);
} }
};
/** /**
* @param {Uint8Array} code - Machine code to run * @param {Uint8Array} code - Machine code to run
* @param {any} debugInfo - TODO * @param {any} debugInfo - TODO
* @param {Boolean} [debug] - Enable/disable debugging printouts * @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); startCPU(code);
while (CPU.running) { while (CPU.running) {
stepCPU(debugInfo, debug); stepCPU(debugInfo, debug, prettyPrintDisplay);
// FIXME: this prevents exiting with Ctrl+C: // FIXME: this prevents exiting with Ctrl+C:
let key = readlineSync.keyIn('S to step, Q to quit > ', { let key = readlineSync.keyIn('S to step, Q to quit > ', {
limit: ['s', 'S', 'q', 'Q'], limit: ['s', 'S', 'q', 'Q'],
@ -393,11 +403,11 @@ exports.singleStepProgram = (code, debugInfo, debug = false) => {
/** /**
* @param {Boolean} [debug] - Enable/disable debugging printouts * @param {Boolean} [debug] - Enable/disable debugging printouts
**/ **/
function logCPUState(debugInfo, debug = false) { function logCPUState(debugInfo, debug = false, prettyPrintDisplay = false) {
console.group(`Step ${CPU.cycleCounter}`); console.group(`Line ${debugInfo[CPU.previousIP].lineNumber} - Step ${CPU.cycleCounter}`);
console.log(); console.log();
if (!debug) console.clear(); if (!debug) console.clear();
if (debug) { if (debug && !prettyPrintDisplay) {
display.printDisplay(CPU.memory); display.printDisplay(CPU.memory);
} else { } else {
display.prettyPrintDisplay(CPU.memory); display.prettyPrintDisplay(CPU.memory);

View File

@ -1,30 +1,55 @@
#!/usr/bin/env node #!/usr/bin/env node
// Run: `./run-cpu.js run assembly.asm` // Usage: ./run-cpu.js -f code.asm [--debug] [--step] [--pretty]
// 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`
const fs = require('fs'); const fs = require('fs');
const computer = require('./cpu.js'); const computer = require('./cpu.js');
const assembler = require('./assembler.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}`); // Load file...
const filename = process.argv[3];
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'); const inputFile_str = fs.readFileSync(filename, 'utf8');
let assemblerOutput = assembler.assemble(inputFile_str);
if (mode === "debug") { // Check optional arguments...
logRunningHeader();
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); let debug = false;
} else if (mode === "stepdebug") { let singleStep = false;
logRunningHeader(); let prettyPrint = false;
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); process.argv.forEach((arg) => { if (arg === '--debug') { debug = true } });
} else if (mode === "step") { process.argv.forEach((arg) => { if (arg === '--step') { singleStep = true } });
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false); process.argv.forEach((arg) => { if (arg === '--pretty') { prettyPrint = true } });
} else {
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false, 200); 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);
logRunningHeader();
computer.runProgram(
assemblerOutput.machineCode,
assemblerOutput.debugInfo,
debug,
singleStep,
prettyPrint,
speed
);