UNPKG

68kcounter

Version:
221 lines (220 loc) 8.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const timings_1 = require("../timings"); const syntax_1 = require("../syntax"); const nodes_1 = require("./nodes"); const evaluate_1 = __importDefault(require("./evaluate")); const sizes_1 = __importDefault(require("../sizes")); const totals_1 = require("../totals"); class Parser { constructor() { /** Variable/constants state */ this.vars = {}; /** Macro definitions */ this.macros = {}; /** Running total of parsed statement bytes sizes */ this.totalBytes = 0; /** The name of the macro currently being defined */ this.currentMacro = null; /** Start of the current repeating group */ this.reptStart = null; /** Statements in the current repeating group */ this.reptStatements = []; /** Are we currently in a BSS section */ this.bss = false; // Directive groups: this.assignments = [ syntax_1.Directives["="], syntax_1.Directives.EQU, syntax_1.Directives.FEQU, syntax_1.Directives.SET, ]; this.sections = [ syntax_1.Directives.SECTION, syntax_1.Directives.BSS, syntax_1.Directives.BSS_C, syntax_1.Directives.BSS_F, syntax_1.Directives.DATA, syntax_1.Directives.DATA_C, syntax_1.Directives.DATA_F, syntax_1.Directives.CODE, syntax_1.Directives.CODE_C, syntax_1.Directives.CODE_F, ]; } /** * Parse multiple lines of ASM code */ parse(input) { // Split input into lines const inputLines = input .replace(/\r\n/g, "\n") .replace(/\r/g, "\n") .split("\n"); // Parse individual statements: const statements = inputLines.map((l) => new nodes_1.StatementNode(l)); // Now do processing to add size/timing info and expand macros: // Reset state this.vars = {}; this.macros = {}; // Needs two passes to catch all references. let lines = []; for (let i = 0; i < 2; i++) { lines = this.processStatements(statements); } return lines; } /** * Add size and timing info to statements */ processStatements(statements) { this.totalBytes = 0; this.currentMacro = null; this.bss = false; this.reptStart = null; this.reptStatements = []; return statements.map(this.processStatement.bind(this)); } processStatement(statement) { let line = { statement }; // Currently defining a macro - store statements against this name rather than processing now if (this.currentMacro) { if (statement.opcode?.op.name === syntax_1.Directives.ENDM) { // End macro this.currentMacro = null; } else { // Add statement to macro this.macros[this.currentMacro].push(statement); } return line; } // Inside repeating section: if (this.reptStart) { if (statement.opcode?.op.name === syntax_1.Directives.ENDR) { // End of repeat line.macroLines = []; // Expand and process repeated statements const countOp = this.reptStart.statement.operands[0]?.text; const reptCount = evaluate_1.default(countOp) || 0; const statements = this.reptStatements; this.reptStart = null; this.reptStatements = []; // TODO: support REPTN for (let i = 0; i < reptCount; i++) { line.macroLines = [ ...line.macroLines, ...this.processStatements(statements), ]; } } else { // Add statement to repeated list this.reptStatements.push(statement); } } // Process types: else if (statement.isLabel()) { line = this.processLabel(statement); } else if (statement.isMacro()) { line = this.processMacro(statement); } else if (statement.isDirective()) { line = this.processDirective(statement); } else if (statement.isInstruction()) { line = this.processInstruction(statement); } // Calculate total of lines added by macro/rept: if (line.macroLines) { const macroTotals = totals_1.calculateTotals(line.macroLines); line.bytes = macroTotals.bytes; if (macroTotals.min[0]) { line.timing = macroTotals.isRange ? { values: [macroTotals.min, macroTotals.max], labels: ["Min", "Max"], } : { values: [macroTotals.min], labels: [], }; } } // Calculate byte size of this statement: else if (!line.bytes) { line.bytes = sizes_1.default(statement, this.vars); line.bss = this.bss; } this.totalBytes += line.bytes; return line; } processLabel(statement) { // Assign running total of bytes to labels names // This allows expressions to get byte count from ranges e.g. `dcb.b END-START` this.vars[statement.label.text] = this.totalBytes; return { statement, bytes: 0, bss: this.bss }; } processMacro(statement) { const line = { statement }; const macroName = statement.opcode.op.text; if (this.macros[macroName]) { const macroStatements = this.macros[macroName].map(({ text }) => { for (let i = 1; i <= statement.operands.length; i++) { const placeholder = "\\" + i; text = text.replace(placeholder, statement.operands[i - 1].text); } return new nodes_1.StatementNode(text); }); line.macroLines = this.processStatements(macroStatements); } return line; } processDirective(statement) { const line = { statement }; // Variable Assignment: if (statement.label && this.assignments.includes(statement.opcode.op.name) && statement.operands[0]) { const value = evaluate_1.default(statement.operands[0].text, this.vars); if (value) { this.vars[statement.label.text] = value; } } // Macro definition: else if (statement.opcode.op.name === syntax_1.Directives.MACRO) { const macroName = statement.label ? statement.label.text : statement.operands[0]?.text; if (macroName) { this.currentMacro = macroName.toUpperCase(); this.macros[this.currentMacro] = []; } } // Rept start: else if (statement.opcode.op.name === syntax_1.Directives.REPT) { this.reptStart = line; } // Section: else if (this.sections.includes(statement.opcode.op.name)) { // Is this a BSS section? this.bss = statement.opcode.op.name.includes("BSS") || statement.operands.some((o) => o.text.match(/^bss/i)); } return line; } processInstruction(statement) { const line = { statement, bss: this.bss, timing: timings_1.instructionTimings(statement, this.vars) || undefined, }; return line; } } exports.default = Parser;