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

52
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 {Number} [clockSpeed] - CPU clock speed in milliseconds * @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) => { exports.runProgram =
startCPU(code); (code, debugInfo, debug=false, singleStep=false, prettyPrint=false, clockSpeed=100) => {
// Animate the output by pausing between steps if (singleStep) {
const loop = setInterval(async () => { this.singleStepProgram(code, debugInfo, debug, prettyPrint);
stepCPU(debugInfo, debug); } else {
if (!CPU.running) clearInterval(loop); startCPU(code);
}, clockSpeed); // 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 {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,19 +403,19 @@ 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);
} }
console.log(); console.log();
console.log(`Line ${debugInfo[CPU.previousIP].lineNumber}: ${debugInfo[CPU.previousIP].source}`); console.log(`Line ${debugInfo[CPU.previousIP].lineNumber}: ${debugInfo[CPU.previousIP].source}`);
console.log('Mnemonic:', CPU.currentInstruction.mnemonic); console.log(' Mnemonic:', CPU.currentInstruction.mnemonic);
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`); console.log(` Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
console.log(); console.log();
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} ONZC: ${num2bin_4bit(CPU.FLAGS)}`); 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" }`); console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])}  ${CPU.running ? "running" : "halted" }`);

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');
// 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); let assemblerOutput = assembler.assemble(inputFile_str);
if (mode === "debug") { logRunningHeader();
logRunningHeader(); computer.runProgram(
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); assemblerOutput.machineCode,
} else if (mode === "stepdebug") { assemblerOutput.debugInfo,
logRunningHeader(); debug,
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, true); singleStep,
} else if (mode === "step") { prettyPrint,
computer.singleStepProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false); speed
} else { );
computer.runProgram(assemblerOutput.machineCode, assemblerOutput.debugInfo, false, 200);
}