All files / asm80-core/utils ihex.js

100% Statements 146/146
100% Branches 35/35
100% Functions 3/3
100% Lines 146/146

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 1471x 1x 1x 1x 1x 1x 1x 1x 1x 155x 155x 155x 155x 155x 155x 155x 155x 155x 155x 155x 155x 2445x 2445x 2445x 155x 155x 155x 155x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8x 8x 8x 8x 8x 8x 8x 2445x 2445x 148x 148x 148x 148x 148x 148x 2445x 8x 8x 8x 7x 7x 8x 8x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1875x 1875x 1875x 1875x 5x 5x 2x 2x 2x 5x 5x 2x 2x 5x 1875x 1875x 1875x 1871x 1871x 1875x 1848x 1875x 23x 23x 23x 1846x 1846x 1846x 1875x 1846x 1846x 1875x 85x 85x 1846x 1846x 1875x 8x 6x 6x 6x 8x 8x 8x 8x 1846x 1846x 1875x 1196x 2445x 2445x 1196x 1196x 1196x 1875x 4x 4x 4x 2x 2x 4x 4x 4x 4x 1x  
import { toHex2, toHex4 } from "./utils.js";
 
/**
 * Generate a single Intel HEX record line
 * @param {number} addr - Starting address for this line
 * @param {number[]} buffer - Array of data bytes
 * @returns {string} Intel HEX record line
 */
const hexLine =  (addr, buffer) => {
    let s = ":";
    let len = buffer.length;
    let checksum = 0;
    s += toHex2(len);           // Record length
    s += toHex4(addr);          // Address
    s += "00";                  // Record type (00 = data)
    
    // Calculate checksum from length and address bytes
    checksum = len + 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 two's complement checksum
    s += toHex2(256 - (checksum % 256));
    return s;
  };
 
/**
 * Generate Intel HEX format from data array
 * @param {number} addr - Starting address
 * @param {number[]} dta - Data bytes
 * @param {number} linelen - Bytes per line (default 16)
 * @returns {string} Intel HEX formatted string
 */
const makeHex =  (addr, dta, linelen) => {
    let inter = 0;
    let buffer = [];
    let ilen = 16;
    if (linelen > 1) ilen = linelen;
    let out = "";
    
    for (let i = 0; i < dta.length; i++) {
      buffer.push(dta[i]);
      if (++inter === ilen) {
        // Flush buffer when line is full
        out += hexLine(addr, buffer) + "\n";
        buffer = [];
        inter = 0;
        addr += ilen;
      }
    }
    
    // Flush remaining bytes
    if (buffer.length) {
      out += hexLine(addr, buffer) + "\n";
    }
 
    return out;
  };
 
/**
 * Generate Intel HEX format output from compilation result
 * @param {Object} result - Compilation result containing dump array
 * @param {string} [segment] - Optional segment filter (e.g., "CSEG", "DSEG")
 * @returns {string} Complete Intel HEX file content with EOF record
 */
export const ihex =  (result, segment) => {
    let V = result.dump;
    let ln;
    let op;
    let addr = null;
    let len = 0;
    let dta = [];
    let out = "";
    let segments = false;
    let ilen = 16; // Default line length
    
    for (let i = 0, j = V.length; i < j; i++) {
      op = V[i];
      
      // Process pragma directives
      if (op.opcode === ".PRAGMA") {
        // Check for HEXLEN pragma to set line length
        if (op.params.length == 2 && op.params[0].toUpperCase() == "HEXLEN") {
          ilen = parseInt(op.params[1]);
          if (ilen < 1 || ilen > 64) ilen = 16;
        }
        // Check for SEGMENT pragma to enable segment filtering
        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 += makeHex(addr, dta, ilen);
        }
        addr = opaddr;
        len = 0;
        dta = [];
      }
      
      // Add instruction bytes to data array
      if (op.lens) {
        for (var 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 += makeHex(addr, dta, ilen);
    }
    
    // Add EOF record
    out += ":00000001FF";
    return out;
  };