ihex.c 6.56 KB
/*
 * LPC 2000 Loader, http://www.pjrc.com/arm/lpc2k_pgm
 * Copyright (c) 2004, PJRC.COM, LLC, <paul@pjrc.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/* If this code fails to build, please provide at least the following
 * information when requesting (free) technical support.
 *
 * 1: Complete copy of all messages during the build.
 * 2: Output of "gtk-config --version"
 * 3: Output of "gtk-config --libs"
 * 4: Output of "gtk-config --cflags"
 * 5: Output of "uname -a"
 * 6: Version of GTK installed... eg, type: ls -l /lib/libgtk*
 * 7: Other info... which linux distribution, version, other software
 */

#include <stdio.h>
#include <string.h>


// the maximum flash image size we can support
// chips with larger memory may be used, but only this
// much intel-hex data can be loaded into memory!
#define MAX_MEMORY_SIZE 0x80000


#include "ihex.h"


static unsigned char firmware_image[MAX_MEMORY_SIZE];
static unsigned char firmware_mask[MAX_MEMORY_SIZE];
static int end_record_seen = 0;
static int byte_count;
static unsigned int extended_addr = 0;


static int parse_hex_line(char *line);


/****************************************************************/
/*                              */
/*            Read Intel Hex File           */
/*                              */
/****************************************************************/



int read_intel_hex(const char *filename)
{
    FILE *fp;
    int i, lineno = 0;
    char buf[1024];

    byte_count = 0;
    end_record_seen = 0;

    for (i = 0; i < MAX_MEMORY_SIZE; i++) {
        firmware_image[i] = 0xFF;
        firmware_mask[i] = 0;
    }

    extended_addr = 0;

    fp = fopen(filename, "r");

    if (fp == NULL) {
        printf("Unable to read file %s\n", filename);
        return -1;
    }

    while (!feof(fp)) {
        *buf = '\0';
        fgets(buf, sizeof(buf), fp);
        lineno++;

        if (*buf) {
            if (parse_hex_line(buf) == 0) {
                printf("Warning, parse error line %d\n", lineno);
                fclose(fp);
                return -2;
            }
        }

        if (end_record_seen) {
            break;
        }

        if (feof(stdin)) {
            break;
        }
    }

    fclose(fp);
    return byte_count;
}


/* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */

/* parses a line of intel hex code, stores the data in bytes[] */
/* and the beginning address in addr, and returns a 1 if the */
/* line was valid, or a 0 if an error occured.  The variable */
/* num gets the number of bytes that were stored into bytes[] */


int
parse_hex_line(char *line)
{
    unsigned int addr, code, num;
    unsigned int sum, len, cksum, i;
    char *ptr;

    num = 0;

    if (line[0] != ':') {
        return 0;
    }

    if (strlen(line) < 11) {
        return 0;
    }

    ptr = line + 1;

    if (!sscanf(ptr, "%02x", &len)) {
        return 0;
    }

    ptr += 2;

    if (strlen(line) < (11 + (len * 2))) {
        return 0;
    }

    if (!sscanf(ptr, "%04x", &addr)) {
        return 0;
    }

    ptr += 4;

    /* printf("Line: length=%d Addr=%d\n", len, addr); */
    if (!sscanf(ptr, "%02x", &code)) {
        return 0;
    }

    if (addr + extended_addr + len >= MAX_MEMORY_SIZE) {
        return 0;
    }

    ptr += 2;
    sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);

    if (code != 0) {
        if (code == 1) {
            end_record_seen = 1;
            return 1;
        }

        if (code == 2 && len == 2) {
            if (!sscanf(ptr, "%04x", &i)) {
                return 1;
            }

            ptr += 4;
            sum += ((i >> 8) & 255) + (i & 255);

            if (!sscanf(ptr, "%02x", &cksum)) {
                return 1;
            }

            if (((sum & 255) + (cksum & 255)) & 255) {
                return 1;
            }

            extended_addr = i << 4;
            //printf("ext addr = %05X\n", extended_addr);
        }

        if (code == 4 && len == 2) {
            if (!sscanf(ptr, "%04x", &i)) {
                return 1;
            }

            ptr += 4;
            sum += ((i >> 8) & 255) + (i & 255);

            if (!sscanf(ptr, "%02x", &cksum)) {
                return 1;
            }

            if (((sum & 255) + (cksum & 255)) & 255) {
                return 1;
            }

            extended_addr = i << 16;
            //printf("ext addr = %08X\n", extended_addr);
        }

        return 1;   // non-data line
    }

    byte_count += len;

    while (num != len) {
        if (sscanf(ptr, "%02x", &i) != 1) {
            return 0;
        }

        i &= 255;
        firmware_image[addr + extended_addr + num] = i;
        firmware_mask[addr + extended_addr + num] = 1;
        ptr += 2;
        sum += i;
        (num)++;

        if (num >= 256) {
            return 0;
        }
    }

    if (!sscanf(ptr, "%02x", &cksum)) {
        return 0;
    }

    if (((sum & 255) + (cksum & 255)) & 255) {
        return 0;    /* checksum error */
    }

    return 1;
}


int bytes_within_range(int begin, int end)
{
    int i;

    if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
        end < 0 || end >= MAX_MEMORY_SIZE) {
        return 0;
    }

    for (i = begin; i <= end; i++) {
        if (firmware_mask[i]) {
            return 1;
        }
    }

    return 0;
}

void get_ihex_data(int addr, int len, unsigned char *bytes)
{
    int i;

    if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
        for (i = 0; i < len; i++) {
            bytes[i] = 255;
        }

        return;
    }

    for (i = 0; i < len; i++) {
        if (firmware_mask[addr]) {
            bytes[i] = firmware_image[addr];
        }
        else {
            bytes[i] = 255;
        }

        addr++;
    }
}

void put_ihex_data(int addr, int len, const unsigned char *bytes)
{
    int i;

    if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
        return;
    }

    for (i = 0; i < len; i++) {
        firmware_image[addr] = bytes[i];
        firmware_mask[addr] = 1;
        addr++;
    }
}