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
* @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" }`);

View File

@ -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);
}
logRunningHeader();
computer.runProgram(
assemblerOutput.machineCode,
assemblerOutput.debugInfo,
debug,
singleStep,
prettyPrint,
speed
);