diff --git a/.gitmodules b/.gitmodules index 554e1cb..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "src/argparser"] - path = src/argparser - url = https://git.nloewen.com/n/argv-parser.git diff --git a/issues/issues.md b/issues/issues.md deleted file mode 100644 index 4a9e2dc..0000000 --- a/issues/issues.md +++ /dev/null @@ -1,46 +0,0 @@ -# Cardiograph issues - -## Open - -### #1 - Improve CLI interface - -I'm thinking of an interface like this... - - $ ./cpu.js -mc code.bin - $ ./cpu.js code.asm - $ ./cpu.js --debug code.asm - -Full list of flags I want: - - -d --debug - -s --singlestep - -p --prettydisplay - -mc --machinecode - -### #2 - Startup: Execute `JMP $FF` - -See [2023-08-24](../notes/2023-08-24--dev-notes.md#cpu-start-up) - -... say that there's a 1-byte ROM at $FF. - -- `00-19` - display (5x5) -- `1A ` - pointer to display memory -- `1B ` - keypad: value of latest key pressed -- `1C ` - reserved for future use (bank switching flag) -- `1D ` - initial IP -- `1D-FE` - free -- `FF ` - ROM (unwriteable) - pointer to initial IP - -- store `$1D` at `$FF` -- make CPU execute `JMP $FF` on startup -- make ROM unwriteable - -More step-by-step: - - - Change memory from a Uint8Array to a regular array, - and make every entry { number | { type: 'ROM', value: number }} - - Store ROM as an object in machine.config.js - - Load ROM data into memory at CPU startup (`startCPU(RAM, ROM)`) - - -## Closed \ No newline at end of file diff --git a/issues/todo.md b/issues/todo.md deleted file mode 100644 index e29a352..0000000 --- a/issues/todo.md +++ /dev/null @@ -1,55 +0,0 @@ -# To do — Summary - -This is a quick todo list. - -For extended commentary, see [issues](issues.md). - -## Open - -### Todo - -- Finish WIP on run-cli arg parsing -- Pass CYCLE_COUNT as a cli arg - -- (cpu) !! Fix overflow flag -- Add a flag for bank-switching to the ~zero-page -- Remove run-scripts and add the ability to run `./cpu.js` and `./assembler.js` directly -- cf. [#1](issues.md#1---improve-cli-interface) -- [fix] (cpu) Make single-stepping work with simulated keypad - -### Features - -- (cpu) allow arrow keys, too -- [fix] (cpu) Execute `JMP $FF` on startup / Implement ROM — see [#2](issues.md#2---startup-execute-jmp-ff) -- (assembler) Validate labels -- (assembler) Extract debugging to its own module -- (cpu) Consider adding a VIP-style keypad-based machine code monitor -- (cpu) Add a mode that prints the display as text, but still animates -- (cpu) Allow running pre-compiled machine code -- (cpu) DRY out addition and subtraction -- [Extended system (secret bonus operations)](../notes/2023-08-07--dev-notes.md) -- (research) Review CHIP-8 - - read about the spec / ISA - - read these, and add them to the bibliography: - - Steve Losh: https://stevelosh.com/blog/2016/12/chip8-input/ - - https://tonisagrista.com/blog/2021/chip8-spec/ - -### Documentation - -- Improve docs for flags register - -### Testing - -- Display (hex) numbers -- Greater than -- Minimal LOGO-ish interpreter for turtle graphics - - -## Closed - -- 2023-08-26 - [fix] (logging) - 'undefined operand' situation is caused by assembling to an initial IP of $1C, which is an odd number -- (assembler) Pass asm line thru to cpu to print when debugging - - -## Abandoned - -- (assembler) Return pure machine code when printing to stdout (and not in debug mode) \ No newline at end of file diff --git a/readme.md b/readme.md deleted file mode 100644 index 0dba40d..0000000 --- a/readme.md +++ /dev/null @@ -1,175 +0,0 @@ -# Cardiograph Mark I — simulator for an imaginary computer - -## Dependencies - -- Node.js - - readline-sync - -## Run - -### Assemble - -Hex output: -```./run-assembler run source_code.asm``` - -Binary output: -```./run-assembler runbin source_code.asm``` - -Verbose debugging output (hex): -```./run-assembler debug source_code.asm``` - -### Assemble and run - -With animated display of screen memory: -```./run-cpu run source_code.asm``` - -With verbose debugging output: -```./run-cpu debug source_code.asm``` - -With single stepping + pretty-printed display: -```./run-cpu step source_code.asm``` - -With single stepping + verbose debugging output: -```./run-cpu stepdebug source_code.asm``` - - -## Registers and Flags - -- `A` - accumulator -- `IP` - instruction pointer (aka program counter) -- `FLAGS` - flags: **O**verflow, **N**egative, **Z**ero, **C**arry - - in machine language, each flag is given a number: - - O = 3 - N = 2 - Z = 1 - C = 0 - - (bitwise, `0000 = ONZC`) - - -## Instruction set - -### Operations - -``` -Hex Mnem. Operand Effect - -00 END (ignored) Halt CPU -01 STO literal # mem[lit#] = A -02 STO address mem[mem[addr]] = A -03 LDA literal # A = lit# -04 LDA address A = addr -05 ADD literal # A = A + lit# -06 ADD address A = A + mem[addr] -07 SUB literal # A = A - lit# -08 SUB address A = A - mem[addr] -09 HOP literal # If A == lit#, skip next op (IP += 4) -0A HOP address If A == mem[addr], skip next instruction (IP += 4) -0B JMP literal # IP = lit# -0C JMP address IP = mem[addr] -0D FTG literal # Toggle flag, where flag number == lit# -0E FHP literal # Skip next op if flag is set, where flag number == lit# -0F NOP (ignored) None -``` - -- Instructions are two bytes long: - one byte for the opcode, one for the operand - - -### Effects on memory, flags, registers - -``` -op mem flags IP - -END +2 -NOP +2 - -STO w +2 -LDA r NZ +2 -ADD ONZC +2 -SUB ONZC +2 -HOP +2/+4 -JMP arg -FTG ONZC +2 -FHP ONZC +2/+4 - -STO r,w +2 -LDA r,r NZ +2 -ADD r ONZC +2 -SUB r ONZC +2 -HOP r +2/+4 -JMP r arg -FTG r ONZC +2 -FHP r ONZC +2/+4 -``` - - -## CPU start-up - -When starting up, the CPU executes a `JMP $FF`. - -Put differently: it starts executing instructions at the address contained in `$FF`. - -## Cardiograph memory map - -- `00-19` - display (5x5) -- `1A ` - pointer to display memory -- `1B ` - keypad: value of latest key pressed -- `1C ` - reserved for future use (bank switching flag) -- `1D ` - initial IP -- `1D-FE` - free -- `FF ` - ROM (unwriteable) pointer to initial IP (not yet implemented) - -## Peripherals - -### Keypad - -The value of the latest keypress on a hex keypad is stored at `$1B`. - -The keypad uses the same layout as the COSMAC VIP (and CHIP-8). The CPU simulator maps those keys onto a Qwerty set: - -``` -1 2 3 C 1 2 3 4 -4 5 6 D Q W E R -7 8 9 E A S D F -A 0 B F Z X C V - -hex pad simulator -``` - -The arrow keys are also mapped onto the hex keypad: - -``` - 5 ↑ - 7 8 9 ← ↓ → - -hex pad simulator -``` - -## Assembly language - - ADD $01 ; comments follow a `;` - - ADD $FF ; this is direct addressing - ADD ($CC) ; this is indirect addressing - - END ; END and NOP don't require operands - ; (the assembler will fill in a default value of 0) - - @subroutine ; create a label - ADD $01 ; (it must be on the line before the code it names) - ADD $02 - - JMP @subroutine ; use a label as operand - ; the label will be replaced with - ; the address of the label - - #foo $FF ; define a constant - ; (must be defined before it is referenced) - - ADD #foo ; use a constant as an operand - - LDA * ; `*` is a special label referencing the memory address - ; where the current line will be stored after assembly - -- Hexadecimal numbers are preceded by a `$` -- Whitespace is ignored \ No newline at end of file diff --git a/src/argparser b/src/argparser deleted file mode 160000 index 584d9dd..0000000 --- a/src/argparser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 584d9dd95f4b1b3c69065826cf96b3cda0cf9e16 diff --git a/src/assembler.js b/src/assembler.js deleted file mode 100644 index d30c41c..0000000 --- a/src/assembler.js +++ /dev/null @@ -1,422 +0,0 @@ -const { logMemory, num2hex } = require('./logging.js'); -const { - INITIAL_IP_ADDRESS, - DISPLAY_ADDR, - POINTER_TO_DISPLAY, -} = require('./machine.config.js'); - -// 1 = verbose -// 2 = what i'm currently focusing on -// 3 = always print -// 4 = silent -const DEBUG_LEVEL = 2; -let DEBUG; // Turn debugging on/off -- set by assemble() - -/** - * @param {string} assemblyCode - * @param {Boolean} [debug = false] - **/ -exports.assemble = (assemblyCode, debug = false) => { - DEBUG = debug; - return decodeInstructions(assemblyCode); -} - -// Configure pseudo-ops: -const ASM_IP_LABEL = '*'; -const ASM_CONSTANT_PREFIX = '#'; -const ASM_LABEL_PREFIX = '@'; - -const mnemonicsWithOptionalArgs = ['end', 'nop']; -const mnemonics2opcodes = { - end: { direct: 0, indirect: 0 }, - sto: { direct: 1, indirect: 2 }, - lda: { direct: 3, indirect: 4 }, - add: { direct: 5, indirect: 6 }, - sub: { direct: 7, indirect: 8 }, - hop: { direct: 9, indirect: 10 }, - jmp: { direct: 11, indirect: 12 }, - ftg: { direct: 13, indirect: 13 }, - fhp: { direct: 14, indirect: 14 }, - nop: { direct: 15, indirect: 15 }, -}; - - -/** - * @typedef {('code'|'comment'|'blank')} SourceLineType - **/ - -/** - * @typedef {Object} SourceLineInfo - * @property {number} number - line number - * @property {string} source - source text - * @property {string} sanitized - source text, with comments and whitespace removed - * @property {SourceLineType} type - line type - * @property {string} [operation] - For code: the first non-whitespace chunk - * @property {string} [argument] - For code: the second non-whitespace chunk, if there is one - * @property {string} [extraArgument] - For code: the third non-whitespace chunk, if there is one - **/ - -/** - * @param {string} source - * @returns {Array} - **/ -function preparseSourceCode(source) { - let lines = source.split(/\n/); // returns an array of lines - - const isLineBlank = (l) => { return stripWhitespaceFromEnds(l).length === 0 ? true : false }; - const isLineComment = (l) => { return stripWhitespaceFromEnds(l).startsWith(';') }; - - /** - * @param {string} l - * @returns {SourceLineType} - **/ - const getLineType = (l) => { - if (isLineBlank(l)) return 'blank'; - if (isLineComment(l)) return 'comment'; - return 'code'; - } - - return lines.map((line, index) => { - dbg(1, ` in: ${line}`); - let info = { - number: index + 1, - source: line, - sanitized: stripWhitespaceFromEnds(stripComments(line)), - type: getLineType(line), - }; - dbg(1, ` → ${info.number} - ${info.type}: ${info.sanitized}`); - dbg(1, ``); - - if (info.type === 'code') { - const op_arg_array = info.sanitized.split(/\s+/); // split line into an array of [op, arg, extra_arg] - if (op_arg_array[0] !== 'undefined') { - info.operation = op_arg_array[0]; - } - if (op_arg_array.length === 2) { - info.argument = op_arg_array[1]; - } - if (op_arg_array.length === 3) { - info.argument = op_arg_array[1]; - info.extraArgument = op_arg_array[2]; - } - // If there's too many arguments, throw an error - // NB. there's a special case: - // lines with the ASM_IP_LABEL can take an extra argument - let maxArgs = 2; - if (op_arg_array.length > 2 && op_arg_array[1].startsWith(ASM_IP_LABEL)) { - maxArgs = 3; - } - if (op_arg_array.length > maxArgs) { - console.error(); - console.error(`Error: Too many arguments`); - console.error(` at line ${info.number}`); - process.exit(); - } - } - return info; - }); -} - - - -/** - * @param {string} arg - * @returns {number} - **/ -function decodeNumericOp(arg) { - if (arg.startsWith("$")) return hex2num(arg.replace("$", "")); - return parseInt(arg); -} - - -/** - * @param {string} op - * @param {object} labels // TODO document better - * @param {number} IP - * @returns {Array} - array of labels - **/ -function handleLabelDefinition(op, IP, labels) { - let label = op.substring(1); // strip label prefix - - if (label in labels) { - labels[label].pointsToByte = IP; - } else { - labels[label] = { - pointsToByte: IP, - bytesToReplace: [], - }; - } - dbg(1, ` Label definition:`); - dbg(1, ` Points to byte: ${labels[label].pointsToByte}`); - dbg(1, ` Bytes to replace: ${labels[label].bytesToReplace}`); - dbg(1, ` IP: $${num2hex(IP)}, new code: none`); - dbgGroupEnd(1, 'Input line'); - return labels; -} - -/** - * @param {string} op - * @param {string} arg - * @param {number} IP - * @returns {Array} - array of constants - **/ -function handleConstantDefinitions(op, arg, IP, constants) { - let constantName = op.substring(1); // strip '>' - let constantValue = arg; - if (constantValue === ASM_IP_LABEL) { - constantValue = IP.toString(); - } - constants[constantName] = constantValue; - dbg(1, ''); - dbg(1, `Constants:`); - dbg(1, constants); - dbg(1, ''); - return constants; -} - - -/** - * Assemble source code. - * - * If the source doesn't explicitly set an address to assemble to, - * it will be assembled to the default intial value of the IP, - * as specified in `machine.config.js`. - * @param {string} source - Assembly source to decode - * @return {{ debugInfo: Object, machineCode: Uint8Array }}; - **/ -function decodeInstructions(source) { - dbg(1, 'Pre-parsing...'); - let lines = preparseSourceCode(source); - dbg(1, ''); - dbg(1, 'Done pre-parsing.'); - dbg(1, ''); - dbg(1, 'Assembling...'); - - // Figure out where to start assembly... - - /** @type {number} IP - Destination addr for the next line **/ - let IP; - - // Check if the source code explicitly sets an address to assemble at - // by including a `* [addr]` as the first (non-blank, non-comment) line - let idOfFirstLineWithCode = lines.findIndex((el) => el.type === 'code'); - if (lines[idOfFirstLineWithCode].operation.startsWith(ASM_IP_LABEL)) { - IP = parseInt(lines[idOfFirstLineWithCode].argument); - } else { - IP = INITIAL_IP_ADDRESS; - } - - // Initialize arrays to collect assembled code - - /** @type {Array} - Assembled source code, as an array of bytes **/ - let machineCode = new Array(IP).fill(0); - - let debugInfo = {}; - - // Initialize memory-mapped IO -- TODO this should probably be in the CPU, not here - machineCode[POINTER_TO_DISPLAY] = DISPLAY_ADDR; - - // Initialize arrays that collect code references that - // have to be revisited after our first pass through the source - let labels = {}; - let constants = {}; - - - // Decode line by line... - for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - // dbg(2, `line info:`); - // dbg(2, line); - if (line.type === 'code') { - const op = line.operation; - - if (typeof line.argument === 'undefined') { - // If this isn't a label definition, - // or one of the ops with optional arguments, - // then it's an error - if (!line.operation.startsWith('@')) { - if (mnemonicsWithOptionalArgs.indexOf(line.operation.toLowerCase()) < 0) { - console.error(''); - console.error(`Error: Missing operand ${line.source}`); - console.error(` at line ${line.number}`); - process.exit(); - } else { - // It *is* one of the special optional-arg ops - // So let's fill in the implicit operand with $00 - line.argument = '0'; - } - } - } - - - // *** Decode special operations *** - - // Opcodes - Handle label definitions - if (op.startsWith(ASM_LABEL_PREFIX)) { - labels = handleLabelDefinition(op, IP, labels); - continue; - } - - // Opcodes - Handle constant definitions - if (op.startsWith(ASM_CONSTANT_PREFIX)) { - constants = handleConstantDefinitions(op, line.argument, IP, constants); - continue; - } - - // Opcodes - Handle setting value of IP - if (op.startsWith(ASM_IP_LABEL)) { - IP = parseInt(line.argument); - continue; - } - - // *** Decode regular operations *** - - /** @type {number|null} decodedOp **/ - let decodedOp = null; - - /** @type {number|null} decodedArg **/ - let decodedArg = null; - - /** @typedef {'direct'|'indirect'} AddressingMode **/ - let addressingMode = 'direct'; - - // Now that it can't be a label or a constant, normalize the opcode - line.operation = line.operation.toLowerCase(); - - // Operands - Handle references to labels - if (line.argument.startsWith(ASM_LABEL_PREFIX)) { - let label = line.argument.substring(1); // strip label prefix - if (label in labels) { - dbg(1, `'${label}' already in labels object`); - labels[label].bytesToReplace.push(IP + 1); - } else { - dbg(1, `'${label}' NOT in labels object`); - labels[label] = { - bytesToReplace: [IP + 1], - }; - } - dbg(1, `Label reference:`); - dbg(1, ` Points to byte: ${labels[label].pointsToByte}`); - dbg(1, ` Bytes to replace: ${labels[label].bytesToReplace}`); - decodedArg = 0; // Return 0 for operand for now -- we'll replace it later - } - - // Operands - Handle references to the Instruction Pointer - if (line.argument === ASM_IP_LABEL) { - dbg(1, ` References current IP - ${IP}`); - if (typeof line.extraArgument === 'undefined') { - decodedArg = IP; - } else { - decodedArg = IP + decodeNumericOp(line.extraArgument); - } - } - - // Operands - Handle references to constants - if (line.argument.startsWith(ASM_CONSTANT_PREFIX)) { - dbg(1, `References '${line.argument}'`); - if (typeof constants[line.argument.substring(1)] === 'undefined') { - console.error(); - console.error(`Error: Undefined constant '${line.argument}'`); - console.error(` at line ${line.number}`); - process.exit(); - } - decodedArg = decodeNumericOp(constants[line.argument.substring(1)]); // substring(1) strips '>' - } - - // Operands - Handle references to constants in indirect mode - if (line.argument.startsWith(`(${ASM_CONSTANT_PREFIX}`)) { - addressingMode = "indirect"; - dbg(1, `(Indirectly) References '${line.argument}'`); - let constName = line.argument.replace(`(${ASM_CONSTANT_PREFIX}`, ""); - constName = constName.replace(")", ""); - decodedArg = decodeNumericOp(constants[constName]); - } - - // Operands - Handle indirect expressions - if (decodedArg === null && line.argument.startsWith("(")) { - addressingMode = "indirect"; - let indyTemp = line.argument.replace("(", "").replace(")", ""); - decodedArg = decodeNumericOp(indyTemp); - } - - // Decode regular opcodes - if (decodedOp === null) { - decodedOp = mnemonics2opcodes[line.operation][addressingMode]; - } - - // Decode regular operands - if (decodedArg === null) { - decodedArg = decodeNumericOp(line.argument); - } - - machineCode[IP] = decodedOp; - machineCode[IP + 1] = decodedArg; - - debugInfo[IP] = { - lineNumber: line.number, - source: line.source, - address: IP, - machine: [decodedOp, decodedArg] - }; - - - dbg(3, ``); - dbg(3, `Line ${line.number}: ${line.source}`); - if (line.argument) { - dbg(3, ` Asm operation: ${line.operation.toUpperCase()} ${line.argument}`); - } else if (line.operation) { - dbg(3, ` Asm operation: ${line.operation.toUpperCase()}`); - } - - dbg(3, ` Machine code: $${num2hex(decodedOp)} $${num2hex(decodedArg)}`); - dbg(3, ` IP: $${num2hex(IP)}`); - IP += 2; - }; - } - - dbg(1, ''); - dbgGroup(1, 'Memory before filling in label constants'); - dbgExec(1, () => logMemory(new Uint8Array(machineCode))); - dbgGroupEnd(1); - - // Backfill label references - for (let k of Object.keys(labels)) { - dbgGroup(1, `${ASM_LABEL_PREFIX}${k}`); - let label = labels[k]; - dbg(1, `Points to byte: ${label.pointsToByte}`); - dbg(1, `Bytes to replace: ${label.bytesToReplace}`); - dbgGroupEnd(1); - for (let j = 0; j < label.bytesToReplace.length; j++) { - machineCode[label.bytesToReplace[j]] = label.pointsToByte; - } - } - - return { 'debugInfo': debugInfo, 'machineCode': new Uint8Array(machineCode) }; -} - - -/** - * @param {string} line - * @returns {string} - **/ -function stripComments(line) { - return line.replace(/;.+/,""); -} - -/** - * @param {string} line - * @returns {string} - **/ -function stripWhitespaceFromEnds(line) { - line = line.replace(/^\s+/,""); - line = line.replace(/\s+$/,""); - return line; -} - -function hex2num(hex) { return parseInt(hex, 16) }; - -// Debug helpers -const dbg = (lvl, s) => { if (DEBUG && (lvl >= DEBUG_LEVEL)) console.log(s) }; -const dbgGroup = (lvl, s) => { if (DEBUG && (lvl >= DEBUG_LEVEL)) console.group(s) }; -const dbgGroupEnd = (lvl, s) => { if (DEBUG && (lvl >= DEBUG_LEVEL)) console.groupEnd() }; -const dbgExec = (lvl, func) => { if (DEBUG && (lvl >= DEBUG_LEVEL)) func(); } \ No newline at end of file diff --git a/src/cpu.js b/src/cpu.js deleted file mode 100644 index fa1d567..0000000 --- a/src/cpu.js +++ /dev/null @@ -1,422 +0,0 @@ -const readline = require('readline'); -const readlineSync = require('readline-sync'); - -const { - INITIAL_IP_ADDRESS, - DEFAULT_CYCLE_LIMIT, - KEYPAD_ADDR, - KEY_MAP, -} = require('./machine.config'); - -const { - num2hex, - bool2bit, -} = require('./logging.js'); -const display = require('./display.js'); - -// STATE -const CPU = { - // Core state - running: false, - IP: INITIAL_IP_ADDRESS, - FLAGS: {'C': false, 'Z': false, 'N': false, 'O': false}, - FLAGNUMS2NAMES: {0: 'C', 1: 'Z', 2: 'N', 3: 'O'}, - Acc: 0, - memory: null, - - // Functions that update core state - /** @param {Uint8Array} data */ - loadMemory: (data) => { - CPU.memory = new Uint8Array(256); - CPU.memory.set(data, 0); - }, - incrementIP: (offset) => { - CPU.previousIP = CPU.IP; - CPU.IP = CPU.IP + offset; - }, - setIP: (address) => { - CPU.previousIP = CPU.IP; - CPU.IP = address; - }, - updateFlagZero: () => { CPU.FLAGS.Z = CPU.Acc === 0; }, - updateFlagNegative: () => { CPU.Acc & 128 ? CPU.FLAGS.N = true : CPU.FLAGS.N = false }, - - // Debug info - previousIP: 0, - currentInstruction: { - opcode: null, - operand: null, - mnemonic: null, - }, - cycleCounter: 0, -} - - -// FUNCTIONS THAT MODIFY STATE - -const Instructions = { - end: () => { - CPU.currentInstruction.mnemonic = 'END'; - CPU.running = false; - CPU.incrementIP(2); - }, - - store_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'STO lit'; - CPU.memory[lit] = CPU.Acc; - CPU.incrementIP(2); - }, - - store_addr: (addr) => { - CPU.currentInstruction.mnemonic = `STO addr; @addr: ${num2hex(CPU.memory[addr])}`; - CPU.memory[CPU.memory[addr]] = CPU.Acc; - CPU.incrementIP(2); - }, - - load_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'LDA lit'; - CPU.Acc = lit; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - load_addr: (addr) => { - CPU.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.memory[addr])}`; - CPU.Acc = CPU.memory[addr]; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - add_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'ADD lit'; - // Calculate sum - let sum = CPU.Acc + lit; - if (sum > 255) { - CPU.FLAGS.C = true; - sum = (sum % 255) - 1; - } else { - CPU.FLAGS.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); - // FIXME FIXME FIXME - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - CPU.FLAGS.O = true; - } else { - CPU.FLAGS.O = false; - } - CPU.Acc = sum; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - add_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'ADD addr'; - // Calculate sum - let sum = CPU.Acc + CPU.memory[addr]; - if (sum > 255) { - CPU.FLAGS.C = true; - sum = (sum % 255) - 1; - } else { - CPU.FLAGS.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); - // FIXME FIXME FIXME - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - CPU.FLAGS.O = true; - } else { - CPU.FLAGS.O = false; - } - CPU.Acc = sum; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - sub_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'SUB lit'; - // Calculate sum - let sum = CPU.Acc - lit; - if (sum < 0) { - CPU.FLAGS.C = true; - sum = sum + 256; - } else { - CPU.FLAGS.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); - // FIXME FIXME FIXME - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - CPU.FLAGS.O = true; - } else { - CPU.FLAGS.O = false; - } - CPU.Acc = sum; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - sub_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'SUB addr'; - // Calculate sum - let sum = CPU.Acc - CPU.memory[addr]; - if (sum < 0) { - CPU.FLAGS.C = true; - sum = sum + 256; - } else { - CPU.FLAGS.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (CPU.FLAGS & 8); - // FIXME FIXME FIXME - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - CPU.FLAGS.O = true; - } else { - CPU.FLAGS.O = false; - } - CPU.Acc = sum; - CPU.updateFlagNegative(); - CPU.updateFlagZero(); - CPU.incrementIP(2); - }, - - hop_lit: (lit) => { - CPU.currentInstruction.mnemonic = `HOP lit; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`; - if (CPU.Acc === lit) { - CPU.incrementIP(4); - } else { - CPU.incrementIP(2); - } - }, - - hop_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'HOP addr'; - if (CPU.Acc === CPU.memory[addr]) { - CPU.incrementIP(4); - } else { - CPU.incrementIP(2); - } - }, - - jump_lit: (lit) => { - CPU.currentInstruction.mnemonic = 'JMP lit'; - CPU.setIP(lit); - }, - - jump_addr: (addr) => { - CPU.currentInstruction.mnemonic = 'JMP addr'; - CPU.setIP(CPU.memory[addr]); - }, - - flag_toggle: (flagNum) => { - if (flagNum === null) { - console.error('Invalid flag number'); - process.exit(); - } - const flagName = CPU.FLAGNUMS2NAMES[flagNum]; - CPU.currentInstruction.mnemonic = `FTG ${flagName}`; - CPU.FLAGS[flagName] = !CPU.FLAGS[flagName]; - CPU.incrementIP(2); - }, - - flag_hop: (flagNum) => { - if (flagNum === null) { - console.error('Invalid flag number'); - process.exit(); - } - const flagName = CPU.FLAGNUMS2NAMES[flagNum]; - CPU.currentInstruction.mnemonic = `FHP ${flagName}; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`; - if (CPU.FLAGS[CPU.FLAGNUMS2NAMES[flagNum]]) { - CPU.incrementIP(4); - } else { - CPU.incrementIP(2); - } - }, - - no_op: () => { - CPU.currentInstruction.mnemonic = `NOP`; - CPU.incrementIP(2); - }, -} - -const opcodes2mnemonics = { - 0: (operand) => Instructions.end(), - 1: (operand) => Instructions.store_lit(operand), - 2: (operand) => Instructions.store_addr(operand), - 3: (operand) => Instructions.load_lit(operand), - 4: (operand) => Instructions.load_addr(operand), - 5: (operand) => Instructions.add_lit(operand), - 6: (operand) => Instructions.add_addr(operand), - 7: (operand) => Instructions.sub_lit(operand), - 8: (operand) => Instructions.sub_addr(operand), - 9: (operand) => Instructions.hop_lit(operand), - 10: (operand) => Instructions.hop_addr(operand), - 11: (operand) => Instructions.jump_lit(operand), - 12: (operand) => Instructions.jump_addr(operand), - 13: (operand) => Instructions.flag_toggle(operand), - 14: (operand) => Instructions.flag_hop(operand), - 15: (operand) => Instructions.no_op(), -}; - -/** - * Load code into memory and set CPU state to "running" - * @param {Uint8Array} code - Machine code to load - **/ -function startCPU(code) { - CPU.loadMemory(code); - CPU.cycleCounter = 0; - CPU.running = true; - - // FIXME: This conflicts with single-stepping - // (you can single-step, but keys aren't passed - // through to the Cardiograph) - // - // -> The fix is maybe to remove readlineSync, - // and instead stash the keypress into a buffer variable.* - // Then have the stepping function check that buffer, - // and then clear the buffer, each time it runs. - // - // * If it's in the set of keys that are relevant - // to single-stepping. - - // Start listening for keypresses... - readline.emitKeypressEvents(process.stdin); - if (process.stdin.setRawMode != null) { - process.stdin.setRawMode(true); - } - process.stdin.on('keypress', (str, key) => { // TODO: is it possible to turn this off again? - if (key.sequence === '\x03') process.exit(); - let name = key.name.toUpperCase(); - if (name in KEY_MAP) { - CPU.memory[KEYPAD_ADDR] = KEY_MAP[name]; - } - }); -} - -/** - * 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, prettyPrintDisplay = false) { - if (CPU.IP >= CPU.memory.length) { - console.error('HALTING - IP greater than memory size'); - CPU.running = false; - process.exit(); - } else { - CPU.currentInstruction.opcode = CPU.memory[CPU.IP]; - CPU.currentInstruction.operand = CPU.memory[CPU.IP+1]; - let executeInstruction = opcodes2mnemonics[CPU.currentInstruction.opcode]; - if (typeof executeInstruction === 'undefined') { - let info = debugInfo[CPU.previousIP]; - console.error(); - console.error(`Error: Invalid opcode`); - console.error(` Executing $${num2hex(info.machine[0])} $${num2hex(info.machine[1])}`); - console.error(` from line ${info.lineNumber}: ${info.source}`); - process.exit(); - } - executeInstruction(CPU.currentInstruction.operand); - CPU.cycleCounter += 1; - } - logCPUState(debugInfo, debug, prettyPrintDisplay); - if (DEFAULT_CYCLE_LIMIT) { // Temporary limit as a lazy way to halt infinite loops - if (CPU.cycleCounter >= DEFAULT_CYCLE_LIMIT) { - console.warn(' HALTING - reached cycle limit'); - CPU.running = false; - } - } - if (!CPU.running) process.exit(); -} - -/** - * @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, 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) { - logCPUState(debugInfo, debug, prettyPrint); - console.log('Halted'); - process.exit(); - } - }, 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, prettyPrintDisplay = false) => { - startCPU(code); - while (CPU.running) { - 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'], - }); - if (key.toLowerCase() === 'q') { process.exit(); } - console.log(); - } -} - - -// FUNCTIONS THAT PULL INFO FROM STATE TO DISPLAY - -/** - * @param {Boolean} [debug] - Enable/disable debugging printouts - **/ -function logCPUState(debugInfo, debug = false, prettyPrintDisplay = false) { - debugInfo = debugInfo[CPU.previousIP] !== 'undefined' ? debugInfo[CPU.previousIP] : false; - console.group(`Step ${CPU.cycleCounter}`); - console.log(); - if (!debug) console.clear(); - display.show(CPU.memory, prettyPrintDisplay); - console.log(); - if (debugInfo) { - console.log(`Line ${debugInfo.lineNumber}: ${debugInfo.source}`); - console.log(); - } - 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 ${bool2bit(CPU.FLAGS.O)}${bool2bit(CPU.FLAGS.N)}${bool2bit(CPU.FLAGS.Z)}${bool2bit(CPU.FLAGS.C)}`); - console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])}  ${CPU.running ? "running" : "halted" }`); - console.log(); - console.log(); - console.groupEnd(); -}; \ No newline at end of file diff --git a/src/display.js b/src/display.js deleted file mode 100644 index efa4c34..0000000 --- a/src/display.js +++ /dev/null @@ -1,22 +0,0 @@ -const { POINTER_TO_DISPLAY } = require('./machine.config'); -const { num2hex } = require('./logging.js'); - -/** - * Print the contents of display memory - * by default, each pixel is shown as a hex number - * @param {Uint8Array} mem - CPU memory - * @param {Boolean} pretty - Display pixels using black and white emoji circles - **/ -const printDisplay = (mem, pretty=false) => { - const disp = mem[POINTER_TO_DISPLAY]; - const num2pic = (n) => n > 0 ? '⚫' : '⚪'; - let fmt = (n) => num2hex(n); - if (pretty) fmt = (n) => num2pic(n); - for (let i = disp; i < disp + 25; i += 5) { - console.log(`${fmt(mem[i])} ${fmt(mem[i+1])} ${fmt(mem[i+2])} ${fmt(mem[i+3])} ${fmt(mem[i+4])}`); - } -} - -module.exports = { - "show": printDisplay, -} \ No newline at end of file diff --git a/src/jsconfig.json b/src/jsconfig.json deleted file mode 100644 index d0617e1..0000000 --- a/src/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "checkJs": true - }, - "exclude": ["node_modules", "**/node_modules/*"] -} \ No newline at end of file diff --git a/src/logging.js b/src/logging.js deleted file mode 100644 index 39ee03e..0000000 --- a/src/logging.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Display a table of memory locations. - * Call with [start] and [end] indices to display a range. - * @param {Uint8Array} mem - Memory to display - * @param {number} [start] - A start-index, in decimal - * @param {number} [end] - An end-index, in decimal - **/ -const logMemory = (mem, start=0, end=mem.length) => { - let top1 = `┌─────────┬────────┬─────────┐`; - let top2 = `│ addrs │ opcode │ operand │`; - let top3 = `├─────────┼────────┼─────────┤`; - let blnk = `│ │ │ │`; - let bot1 = `└─────────┴────────┴─────────┘`; - console.log(`${top1}\n${top2}\n${top3}`); - for (let i = start; i < mem.length; i +=2) { - let operand = mem[i+1]; - if (typeof operand === 'undefined') { - console.log(` ${num2hex(i)} ${num2hex(i+1)} │ ${num2hex(mem[i])} │ │`); - } else { - console.log(`│ ${num2hex(i)} ${num2hex(i+1)} │ ${num2hex(mem[i])} │ ${num2hex(operand)} │`); - } - - // Add a blank row every 4 lines: - let rowNum = i - start + 2; // Not actually the row number... - if ((rowNum % 8 === 0) - && (i < (mem.length - 2))) { - console.log(blnk); - } - } - console.log(bot1); -} - -const logRunningHeader = () => { - console.log(); - let time = new Date(); - console.log( `┌─────────────────────┐`); - console.log( `│ Running at ${time.toLocaleTimeString('en-GB')} │` ); - console.log( `└─────────────────────┘`); -} - -/** - * @param {number} num - * @returns {string} - */ -const num2hex = (num) => num.toString(16).toUpperCase().padStart(2, "0"); - -/** - * @param {string} hex - * @returns {number} - */ -const hex2num = (hex) => parseInt(hex, 16); - -/** - * Convert a number to binary, padded to 8 bits - * See here for an explanation: https://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript - * @param {number} num - * @returns {string} binary representation of the input - **/ -const num2bin = (num) => (num >>> 0).toString(2).padStart(8, "0"); - -/** - * Convert a number to binary, padded to 4 bits - * See here for an explanation: https://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript - * @param {number} num - * @returns {string} binary representation of the input - **/ -const num2bin_4bit = (num) => (num >>> 0).toString(2).padStart(4, "0"); - -/** - * @param {string} bin - * @returns {number} - */ -const bin2num = (bin) => parseInt(bin, 2) - -/** - * @param {Boolean} bool - * @returns {0|1} - **/ -const bool2bit = (bool) => bool ? 1 : 0; - - -module.exports = { - "logMemory": logMemory, - "logRunningHeader": logRunningHeader, - "num2hex": num2hex, - "hex2num": hex2num, - "num2bin": num2bin, - "num2bin_4bit": num2bin_4bit, - "bin2num": bin2num, - "bool2bit": bool2bit, -} \ No newline at end of file diff --git a/src/machine.config.js b/src/machine.config.js deleted file mode 100644 index 190ead1..0000000 --- a/src/machine.config.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - "INITIAL_IP_ADDRESS": 29, - - // Use these in CPU: - "DISPLAY_ADDR": 0, - "KEYPAD_ADDR": 27, - // Store the `DISPLAY_ADDR` at this address when assembling: - "POINTER_TO_DISPLAY": 26, - - "KEY_MAP": { - // Same layout as COSMAC VIP / CHIP-8 - // (This object maps qwerty keys to hex keys - // so that they are arranged in the same layout - // as the real keypad) - '1':'1', '2':'2', '3':'3', '4':'C', - 'Q':'4', 'W':'5', 'E':'6', 'R':'D', - 'A':'7', 'S':'8', 'D':'9', 'F':'E', - 'Z':'A', 'X':'0', 'C':'B', 'V':'F', - - // Include conventional arrow keys - 'UP': '5', - 'LEFT': '7', - 'DOWN': '8', - 'RIGHT': '9', - }, - - // max number of times to step the CPU, - // to stop endless loops - // 0 = infinite - "DEFAULT_CYCLE_LIMIT": 2048, -} \ No newline at end of file diff --git a/src/package-lock.json b/src/package-lock.json deleted file mode 100644 index f7b9e0d..0000000 --- a/src/package-lock.json +++ /dev/null @@ -1,1060 +0,0 @@ -{ - "name": "paper-computer", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "paper-computer", - "dependencies": { - "readline-sync": "^1.4.10" - }, - "devDependencies": { - "jsdoc": "^4.0.2", - "jsdoc-to-markdown": "^8.0.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jsdoc/salty": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", - "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v12.0.0" - } - }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, - "node_modules/ansi-escape-sequences": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", - "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", - "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/ansi-escape-sequences/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "dev": true, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/cache-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", - "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", - "dev": true, - "dependencies": { - "array-back": "^4.0.1", - "fs-then-native": "^2.0.0", - "mkdirp2": "^1.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cache-point/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/collect-all": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", - "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", - "dev": true, - "dependencies": { - "stream-connect": "^1.0.2", - "stream-via": "^1.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "dev": true, - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-args/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/command-line-args/node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/command-line-tool": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", - "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", - "dev": true, - "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "command-line-args": "^5.0.0", - "command-line-usage": "^4.1.0", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-tool/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/command-line-usage": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", - "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", - "dev": true, - "dependencies": { - "ansi-escape-sequences": "^4.0.0", - "array-back": "^2.0.0", - "table-layout": "^0.4.2", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/common-sequence": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", - "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/config-master": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", - "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", - "dev": true, - "dependencies": { - "walk-back": "^2.0.1" - } - }, - "node_modules/config-master/node_modules/walk-back": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", - "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/dmd": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.0.tgz", - "integrity": "sha512-uXWxLF1H7TkUAuoHK59/h/ts5cKavm2LnhrIgJWisip4BVzPoXavlwyoprFFn2CzcahKYgvkfaebS6oxzgflkg==", - "dev": true, - "dependencies": { - "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", - "handlebars": "^4.7.7", - "marked": "^4.2.3", - "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", - "reduce-unique": "^2.0.1", - "reduce-without": "^1.0.1", - "test-value": "^3.0.0", - "walk-back": "^5.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/file-set": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", - "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", - "dev": true, - "dependencies": { - "array-back": "^5.0.0", - "glob": "^7.1.6" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/file-set/node_modules/array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/find-replace/node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/fs-then-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", - "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc-api": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-8.0.0.tgz", - "integrity": "sha512-Rnhor0suB1Ds1abjmFkFfKeD+kSMRN9oHMTMZoJVUrmtCGDwXty+sWMA9sa4xbe4UyxuPjhC7tavZ40mDKK6QQ==", - "dev": true, - "dependencies": { - "array-back": "^6.2.2", - "cache-point": "^2.0.0", - "collect-all": "^1.0.4", - "file-set": "^4.0.2", - "fs-then-native": "^2.0.0", - "jsdoc": "^4.0.0", - "object-to-spawn-args": "^2.0.1", - "temp-path": "^1.0.0", - "walk-back": "^5.1.0" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/jsdoc-parse": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.0.tgz", - "integrity": "sha512-Afu1fQBEb7QHt6QWX/6eUWvYHJofB90Fjx7FuJYF7mnG9z5BkAIpms1wsnvYLytfmqpEENHs/fax9p8gvMj7dw==", - "dev": true, - "dependencies": { - "array-back": "^6.2.2", - "lodash.omit": "^4.5.0", - "lodash.pick": "^4.4.0", - "reduce-extract": "^1.0.0", - "sort-array": "^4.1.5", - "test-value": "^3.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jsdoc-to-markdown": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-8.0.0.tgz", - "integrity": "sha512-2FQvYkg491+FP6s15eFlgSSWs69CvQrpbABGYBtvAvGWy/lWo8IKKToarT283w59rQFrpcjHl3YdhHCa3l7gXg==", - "dev": true, - "dependencies": { - "array-back": "^6.2.2", - "command-line-tool": "^0.8.0", - "config-master": "^3.1.0", - "dmd": "^6.2.0", - "jsdoc-api": "^8.0.0", - "jsdoc-parse": "^6.2.0", - "walk-back": "^5.1.0" - }, - "bin": { - "jsdoc2md": "bin/cli.js" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, - "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true - }, - "node_modules/lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true - }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "dev": true - }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "dev": true - }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", - "dev": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", - "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/object-get": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", - "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", - "dev": true - }, - "node_modules/object-to-spawn-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", - "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/reduce-extract": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", - "integrity": "sha512-QF8vjWx3wnRSL5uFMyCjDeDc5EBMiryoT9tz94VvgjKfzecHAVnqmXAwQDcr7X4JmLc2cjkjFGCVzhMqDjgR9g==", - "dev": true, - "dependencies": { - "test-value": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reduce-extract/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/reduce-extract/node_modules/test-value": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", - "integrity": "sha512-wrsbRo7qP+2Je8x8DsK8ovCGyxe3sYfQwOraIY/09A2gFXU9DYKiTF14W4ki/01AEh56kMzAmlj9CaHGDDUBJA==", - "dev": true, - "dependencies": { - "array-back": "^1.0.2", - "typical": "^2.4.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reduce-flatten": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", - "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reduce-unique": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", - "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/reduce-without": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", - "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", - "dev": true, - "dependencies": { - "test-value": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reduce-without/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/reduce-without/node_modules/test-value": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", - "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", - "dev": true, - "dependencies": { - "array-back": "^1.0.3", - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", - "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/sort-array": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz", - "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", - "dev": true, - "dependencies": { - "array-back": "^5.0.0", - "typical": "^6.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sort-array/node_modules/array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/sort-array/node_modules/typical": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", - "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stream-connect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", - "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", - "dev": true, - "dependencies": { - "array-back": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stream-connect/node_modules/array-back": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", - "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", - "dev": true, - "dependencies": { - "typical": "^2.6.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/stream-via": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", - "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/table-layout": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", - "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", - "dev": true, - "dependencies": { - "array-back": "^2.0.0", - "deep-extend": "~0.6.0", - "lodash.padend": "^4.6.1", - "typical": "^2.6.1", - "wordwrapjs": "^3.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/temp-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", - "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", - "dev": true - }, - "node_modules/test-value": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", - "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", - "dev": true, - "dependencies": { - "array-back": "^2.0.0", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/test-value/node_modules/array-back": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", - "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", - "dev": true, - "dependencies": { - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/typical": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", - "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", - "dev": true - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, - "node_modules/walk-back": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", - "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", - "dev": true, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wordwrapjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", - "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", - "dev": true, - "dependencies": { - "reduce-flatten": "^1.0.1", - "typical": "^2.6.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/wordwrapjs/node_modules/reduce-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - } - } -} diff --git a/src/package.json b/src/package.json deleted file mode 100644 index fa555ee..0000000 --- a/src/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "paper-computer", - "scripts": { - "jsdoc": "./node_modules/.bin/jsdoc" - }, - "devDependencies": { - "jsdoc": "^4.0.2", - "jsdoc-to-markdown": "^8.0.0" - }, - "dependencies": { - "readline-sync": "^1.4.10" - } -} diff --git a/src/run-assembler.js b/src/run-assembler.js deleted file mode 100755 index 4a87a55..0000000 --- a/src/run-assembler.js +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env node - -// Run with hex output: `./run-assembler.js run assembly.asm` -// Run with binary output: `./run-assembler.js runbin assembly.asm` -// Debug: `./run-assembler.js debug assembly.asm` - -const fs = require('fs'); -const assembler = require('./assembler.js'); -const { logMemory, num2hex, num2bin } = require('./logging.js'); -const machineConfig = require('./machine.config.js'); - -const mode = process.argv[2]; -const filename = process.argv[3]; -const inputFile_str = fs.readFileSync(filename, 'utf8'); - -let assembler_output; - -if (mode === "debug") { - assembler_output = assembler.assemble(inputFile_str, true); - console.log(''); - console.group("Machine code output"); - logMemory(assembler_output.machineCode, machineConfig.INITIAL_IP_ADDRESS); - console.groupEnd(); -} else { - assembler_output = assembler.assemble(inputFile_str); - let output = ''; - if (mode === 'runbin') { // print binary output - assembler_output.machineCode.forEach((n) => output = `${output} ${num2bin(n)}`); - } else { // print hex output - assembler_output.machineCode.forEach((n) => output = `${output} ${num2hex(n)}`); - } - console.log(output); -} \ No newline at end of file diff --git a/src/run-cpu.js b/src/run-cpu.js deleted file mode 100755 index face7d4..0000000 --- a/src/run-cpu.js +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env node - -// 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 } = require('./logging.js'); - -// Load file... - -let filename; -try { - filename = getArgumentValue('-f', `Missing filename`); -} catch (error) { - console.error(error.message); - 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); -logRunningHeader(); -computer.runProgram( - assemblerOutput.machineCode, - assemblerOutput.debugInfo, - debug, - singleStep, - prettyPrint, - speed -); - - -// CLI args TODO -// - check if value is the name of another arg -// - usage info -// - catch nonexistant flags - -/** - * @param {string} flag - The command line flag, eg. '-f' - * @param {string} errorMessage - The error to throw if a value isn't found - * @returns {string} - **/ -function getArgumentValue(flag, errorMessage) { - let value = null; - process.argv.forEach((arg, index) => { - if (arg === flag && process.argv.length > (index -1)) { - value = process.argv[index + 1]; - } - }); - if (!value) throw new Error(errorMessage); - return value; -}