#!/usr/bin/env node const fs = require('fs'); const DBG = require('./dbg.js'); const Opter = require('./opter/opter.js'); const { num2hex, bool2bit } = require('./conversions.js'); const CFG = require('./machine.config.js'); const CPU = require('./cpu.js'); const io = require('./io.js'); /** SETUP **/ const dbg = new DBG('nitpick'); let cpu = new CPU(CFG.initialIP, CFG.defaultCycleLimit); main(); async function main() { const opter = new Opter(); opter.addOption('-i', '--in', false, true, 1); const opts = opter.parse(process.argv); let input = null; if ('in' in opts) { // Read from file input = fs.readFileSync(opts.in[0], 'utf8'); } else { // Read from stdin input = await readPipedStdin(); } let code = null; let sourceAnnotations = null; try { const parsedInput = JSON.parse(input); sourceAnnotations = parsedInput.sourceAnnotations; code = new Uint8Array(parsedInput.machineCode); } catch (error) { if (error.name === 'SyntaxError') { code = new Uint8Array(input.split(' ')); } } cpu.loadMemory(code); if (sourceAnnotations !== null) { cpu.loadSourceAnnotations(sourceAnnotations); } cpu.onCycleEnd(tick); cpu.onCycleEnd(logCPUState); cpu.start(); io.getKeypadInput(cpu); cpu.step(); } async function tick() { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) await sleep(100); cpu.step(); if (!cpu.running) { console.log('Halted'); process.exit(); } } function logCPUState() { let lineInfo = null; if (cpu.dbg.sourceAnnotations) { lineInfo = cpu.dbg.sourceAnnotations[cpu.dbg.previousIP]; } console.group(`Step ${cpu.dbg.cycleCounter}`); console.log(); io.showDisplay(cpu.memory, true); // FIXME - display - allow printing hex as well as pretty-printing console.log(); if (lineInfo) { console.log(`Line ${lineInfo.lineNumber}: ${lineInfo.source}`); console.log(); } console.log('Mnemonic:', cpu.dbg.currentMnemonic); console.log(`Machine: $${num2hex(cpu.instruction.opcode)} $${num2hex(cpu.instruction.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: ${io.readKeyMem(cpu.memory)} ${cpu.running ? "running" : "halted" }`); console.log(); console.groupEnd(); }; async function readPipedStdin() { // https://wellingguzman.com/notes/node-pipe-input return new Promise(function (resolve, reject) { const stdin = process.stdin; stdin.setEncoding('utf8'); let data = ''; stdin.on('data', function (chunk) { data += chunk; }); stdin.on('end', function () { resolve(data); }); stdin.on('error', reject); }); }