// Syntax: // ADD $01 ; comments follow a `;` // ADD $FF ; this is direct addressing // ADD ($CC) ; this is indirect addressing // END ; END, CFC, and CHP don't require arguments // ; (a default value of 0 will be used as their operand) const mnemonicsWithOptionalArgs = ['end', 'cfc', 'chp']; 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 }, cfc: { direct: 13, indirect: 13 }, chp: { direct: 14, indirect: 14 }, }; function decodeMultipleInstructions(str) { let lines = str.split(/\n/); // returns an array of lines let output = []; lines.forEach( (l) => { let decoded = decodeInstruction(l); if (decoded) { output.push(decoded.op); output.push(decoded.arg); } }); return new Uint8Array(output); } /** * @param {string} line - A line of assembly code * @returns {(object|false)} Either {op: machineOp, arg: 0}, or false if the line was blank */ function decodeInstruction(line) { line = stripWhitespaceFromEnds(stripComments(line)); let op_arg_array = line.split(" "); // split line into an array of [op, arg] let opName = op_arg_array[0].toLowerCase(); let addressingMode = 'direct'; // Must be "direct" or "indirect" // Handle blank lines and mnemonics without arguments (eg END) if (op_arg_array.length < 2) { // No argument // handle blank lines, or lines that just contain a comment: if (line.length === 0) { return false; } // handle mnemonics that aren't paired with an argument: if (mnemonicsWithOptionalArgs.indexOf(opName) < 0) { console.error(`Missing opcode: ${line}`); throw new Error("Missing opcode"); } let machineOp = mnemonics2opcodes[opName][addressingMode]; return { op: machineOp, arg: 0 }; } // Handle mnemonics with arguments (eg ADD $FF) let arg_str = op_arg_array[1]; if (arg_str.startsWith("(")) { addressingMode = "indirect"; arg_str = arg_str.replace("(", ""); arg_str = arg_str.replace(")", ""); } if (arg_str.startsWith("$")) { arg_str = arg_str.replace("$", ""); arg_num = hex2num(arg_str); } else { arg_num = parseInt(arg_str); } let machineOp = mnemonics2opcodes[opName][addressingMode]; return { op: machineOp, arg: arg_num }; } function stripComments(line) { return line.replace(/;.+/,""); } function stripWhitespaceFromEnds(line) { line = line.replace(/^\s+/,""); line = line.replace(/\s+$/,""); return line; } function hex2num(hex) { return parseInt(hex, 16) }; // RUN IT exports.assemble = (str) => { return decodeMultipleInstructions(str); }