UNPKG

@asm80/core

Version:

Core ASM80 compiler / assembler

280 lines (239 loc) 8.12 kB
import { toHex2, toHex4, toHex6 } from "./utils.js"; /** * Generate a single S-Record line (S1 format, 16-bit addresses) * @param {number} addr - Starting address for this line * @param {number[]} buffer - Array of data bytes * @returns {string} S-Record line */ const srecLine = (addr, buffer) => { let s = "S1"; let len = buffer.length; let checksum = 0; s += toHex2(len + 3); // Record length (data + address + checksum) s += toHex4(addr); // 16-bit address // Calculate checksum from length and address bytes checksum = len + 3 + Math.floor(addr / 256) + Math.floor(addr % 256); // Add data bytes and update checksum for (let i = 0; i < buffer.length; i++) { s += toHex2(buffer[i]); checksum += buffer[i]; } // Add one's complement checksum s += toHex2(256 - (checksum % 256)); return s; }; /** * Generate S-Record format from data array (16-bit addresses) * @param {number} addr - Starting address * @param {number[]} dta - Data bytes * @returns {string} S-Record formatted string */ const makeSrec = (addr, dta) => { let inter = 0; let buffer = []; let ilen = 16; let out = ""; for (let i = 0; i < dta.length; i++) { buffer.push(dta[i]); if (++inter === ilen) { // Flush buffer when line is full out += srecLine(addr, buffer) + "\n"; buffer = []; inter = 0; addr += ilen; } } // Flush remaining bytes if (buffer.length) { out += srecLine(addr, buffer) + "\n"; } return out; }; /** * Generate Motorola S-Record format output (S1/S9 format for 16-bit addresses) * @param {Object} result - Compilation result containing dump array * @param {string} [segment] - Optional segment filter (e.g., "CSEG", "DSEG") * @returns {string} Complete S-Record file content with S9 termination record */ export const isrec = (result, segment) => { let V=result.dump; let op; let addr = null; let len = 0; let segments = false; let dta = []; let out = ""; for (let i = 0, j = V.length; i < j; i++) { op = V[i]; // Process pragma directives if (op.opcode === ".PRAGMA") { if (op.params.length == 1 && op.params[0].toUpperCase() == "SEGMENT") { segments = true; } } // Skip conditional assembly blocks if (op.ifskip) continue; // Filter by segment if specified if (typeof op.segment !== "undefined" && typeof segment !== "undefined" && op.segment != segment) continue; if (segments) { if (!segment) segment = "CSEG"; // Default to code segment if (op.segment != segment) continue; } let opaddr = op.addr; // Adjust for phase directive if (op.phase) opaddr -= op.phase; // Set initial address if (opaddr !== undefined && len === 0) { addr = opaddr; } // Check for address discontinuity if (opaddr != addr + len) { if (len) { // Flush current data block out += makeSrec(addr, dta); } addr = opaddr; len = 0; dta = []; } // Add instruction bytes to data array if (op.lens) { for (let n = 0; n < op.lens.length; n++) { dta.push(op.lens[n]); } len += op.lens.length; continue; } } // Flush final data block if (dta.length) { out += makeSrec(addr, dta); } // Add S9 termination record with entry point let ent = result.opts?result.opts.ENT: result.entry.address || 0; let checksum = 3 + Math.floor(ent / 256) + Math.floor(ent % 256); out += "S903" + toHex4(ent) + toHex2(255 - (checksum % 256)); return out; }; /** * Generate a single S-Record line (S2 format, 24-bit addresses) * @param {number} addr - Starting address for this line * @param {number[]} buffer - Array of data bytes * @returns {string} S-Record line with 24-bit address */ const srecLine28 = (addr, buffer) => { let s = "S2"; let len = buffer.length; let checksum = 0; s += toHex2(len + 4); // Record length (data + 3-byte address + checksum) s += toHex6(addr); // 24-bit address // Calculate checksum from length and all address bytes checksum = len + 4 + Math.floor(addr / 65536) + // High byte (Math.floor(addr / 256) % 256) + // Middle byte Math.floor(addr % 256); // Low byte // Add data bytes and update checksum for (let i = 0; i < buffer.length; i++) { s += toHex2(buffer[i]); checksum += buffer[i]; } // Add one's complement checksum s += toHex2(255 - (checksum % 256)); return s; }; /** * Generate S-Record format from data array (24-bit addresses) * @param {number} addr - Starting address * @param {number[]} dta - Data bytes * @returns {string} S-Record formatted string with 24-bit addresses */ const makeSrec28 = (addr, dta) => { let inter = 0; let buffer = []; let ilen = 16; let out = ""; for (let i = 0; i < dta.length; i++) { buffer.push(dta[i]); if (++inter === ilen) { // Flush buffer when line is full out += srecLine28(addr, buffer) + "\n"; buffer = []; inter = 0; addr += ilen; } } // Flush remaining bytes if (buffer.length) { out += srecLine28(addr, buffer) + "\n"; } return out; }; /** * Generate Motorola S-Record format output (S2/S8 format for 24-bit addresses) * @param {Object} result - Compilation result containing dump array * @param {string} [segment] - Optional segment filter (e.g., "CSEG", "DSEG") * @returns {string} Complete S-Record file content with S8 termination record */ export const isrec28 = (result, segment) => { let V=result.dump; let ln; let op; let addr = null; let len = 0; let segments = false; let dta = []; let out = ""; for (let i = 0, j = V.length; i < j; i++) { op = V[i]; // Process pragma directives if (op.opcode === ".PRAGMA") { if (op.params.length == 1 && op.params[0].toUpperCase() == "SEGMENT") { segments = true; } } // Skip conditional assembly blocks if (op.ifskip) continue; // Filter by segment if specified if (typeof op.segment !== "undefined" && typeof segment !== "undefined" && op.segment != segment) continue; if (segments) { if (!segment) segment = "CSEG"; // Default to code segment if (op.segment != segment) continue; } let opaddr = op.addr; // Adjust for phase directive if (op.phase) opaddr -= op.phase; // Set initial address if (opaddr !== undefined && len === 0) { addr = opaddr; } // Check for address discontinuity if (opaddr != addr + len) { if (len) { // Flush current data block out += makeSrec28(addr, dta); } addr = opaddr; len = 0; dta = []; } // Add instruction bytes to data array if (op.lens) { for (let n = 0; n < op.lens.length; n++) { dta.push(op.lens[n]); } len += op.lens.length; continue; } } // Flush final data block if (dta.length) { out += makeSrec28(addr, dta); } // Add S8 termination record with 24-bit entry point let ent = result.opts?result.opts.ENT: result.entry.address || 0; let checksum = 3 + Math.floor(ent / 256) + Math.floor(ent % 256); out += "S804" + toHex6(ent) + toHex2(255 - (checksum % 256)) + "\n"; return out; };