summaryrefslogtreecommitdiffstats
path: root/tools/build/src/unhex.c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1995-05-11 17:39:37 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1995-05-11 17:39:37 +0000
commitac7d5ef06a6d6e8d84abbd1f0b82162725f98326 (patch)
tree9304cf759a73f2a1c6fd3191948f00e870af3787 /tools/build/src/unhex.c
downloadrtems-ac7d5ef06a6d6e8d84abbd1f0b82162725f98326.tar.bz2
Initial revision
Diffstat (limited to 'tools/build/src/unhex.c')
-rw-r--r--tools/build/src/unhex.c719
1 files changed, 719 insertions, 0 deletions
diff --git a/tools/build/src/unhex.c b/tools/build/src/unhex.c
new file mode 100644
index 0000000000..540095d6f4
--- /dev/null
+++ b/tools/build/src/unhex.c
@@ -0,0 +1,719 @@
+/*
+ * unhex
+ * convert a hex file to binary equivalent. If more than one file name
+ * is given, then the output will be logically concatenated together.
+ * stdin and stdout are defaults. Verbose will enable checksum output.
+ *
+ * Supported input formats are Intel hex, Motorola S records, and TI 'B'
+ * records.
+ *
+ * Intel hex input format is
+ * Byte
+ * 1 Colon :
+ * 2..3 Record length, eg: "20"
+ * 4..7 load address nibbles
+ * 8..9 record type: "00" (data) or "02" base addr
+ * 10..x data bytes in ascii-hex
+ * x+1..x+2 cksum (2's compl of (len+addr+data))
+ * x+3 \n -- newline
+ */
+
+char *USAGE = "\
+usage: unhex [-va] [ -o file ] [ file [file ... ] ]\n\
+ -v -- verbose\n\
+ -a base -- 1st byte of output corresponds to this address\n\
+ -l -- linear, just writes data out\n\
+ -o file -- output file; must not be input file\n\
+ -F k_bits -- \"holes\" in input will be filled with 0xFF's\n\
+ up to \"k_bits\" * 1024 bits\n\
+";
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define OK 0
+#define FAILURE (-1)
+#define Failed(x) ((x) == FAILURE)
+#define TRUE 1
+#define FALSE 0
+typedef char bool;
+#define STREQ(a,b) (strcmp(a,b) == 0)
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+
+/*
+ * Pick out designated bytes
+ */
+
+#define B0(x) ((x) & 0xff)
+#define B1(x) B0((x) >> 8)
+#define B2(x) B0((x) >> 16)
+#define B3(x) B0((x) >> 24)
+
+typedef struct buffer_rec {
+ u32 dl_destaddr;
+ u32 dl_jumpaddr;
+ int dl_count;
+ u8 dl_buf[512];
+} buffer_rec;
+
+/*
+ * vars controlled by command line options
+ */
+
+bool verbose = FALSE; /* be verbose */
+bool linear = FALSE; /* just write out linear data */
+char *outfilename = "-"; /* default output is stdout */
+u32 base = 0L; /* base address */
+u32 FFfill = 0L; /* how far to fill w 0xFF's */
+
+extern char *optarg; /* getopt(3) control vars */
+extern int optind;
+extern int errno;
+
+char *progname; /* for error() */
+
+void error(int errn, ...);
+#define ERR_ERRNO (1<<((sizeof(int) * 8) - 2)) /* hi bit; use 'errno' */
+#define ERR_FATAL (ERR_ERRNO / 2) /* error is fatal; no return */
+#define ERR_ABORT (ERR_ERRNO / 4) /* error is fatal; abort */
+#define ERR_MASK (ERR_ERRNO | ERR_FATAL | ERR_ABORT) /* all */
+
+#define stol(p) strtol(p, (char **) NULL, 0)
+
+int unhex(FILE *ifp, char *inm, FILE *ofp, char *onm);
+int convert_Intel_records(FILE *ifp, char *inm, FILE *ofp, char *onm);
+int convert_S_records(FILE *ifp, char *inm, FILE *ofp, char *onm);
+int convert_TI_records(FILE *ifp, char *inm, FILE *ofp, char *onm);
+void write_record(buffer_rec *tb, FILE *fp);
+int getnibble(char **p);
+int getbyte(char **p);
+long getNbytes(char **p, int n);
+void badformat(char *s, char *fname, char *msg);
+
+#define get1bytes(p) ((int) getbyte(p))
+#define get2bytes(p) ((int) getNbytes(p, 2))
+#define get3bytes(p) getNbytes(p, 3)
+#define get4bytes(p) getNbytes(p, 4)
+
+char *BADADDR = "Invalid record address";
+char *BADLEN = "Invalid record length";
+char *BADBASE = "Bad base or starting address";
+char *BADFMT = "Unrecognized record type";
+char *BADDATA = "Invalid data byte";
+char *BADCSUM = "Invalid checksum";
+char *MISCSUM = "Checksum mismatch";
+char *BADTYPE = "Unrecognized record type";
+char *MISTYPE = "Incompatible record types";
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ register int c;
+ bool showusage = FALSE; /* usage error? */
+ int rc = 0;
+ FILE *outfp, *infp;
+
+ /*
+ * figure out invocation leaf-name
+ */
+
+ if ((progname = strrchr(argv[0], '/')) == (char *) NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ argv[0] = progname; /* for getopt err reporting */
+
+ /*
+ * Check options and arguments.
+ */
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "F:a:o:vl")) != EOF)
+ switch (c)
+ {
+ case 'a': /* base address */
+ base = stol(optarg);
+ break;
+
+ case 'l': /* linear output */
+ linear = TRUE;
+ break;
+
+ case 'v': /* toggle verbose */
+ verbose = ! verbose;
+ break;
+
+ case 'o': /* output file */
+ outfilename = optarg;
+ break;
+
+ case 'F': /* 0xFF fill amount (bytes) */
+ FFfill = stol(optarg) * 1024L / 8L;
+ break;
+
+ case '?':
+ showusage = TRUE;
+ }
+
+ if (showusage)
+ {
+ (void) fprintf(stderr, "%s", USAGE);
+ exit(1);
+ }
+
+ if (linear && (base != 0))
+ {
+ error(0, "-l and -a may not be specified in combination");
+ exit(1);
+ }
+
+ if (STREQ(outfilename, "-"))
+ {
+ outfp = stdout;
+ outfilename = "stdout";
+ }
+ else
+ if ((outfp = fopen(outfilename, "w")) == (FILE *) NULL)
+ {
+ error(-1, "couldn't open '%s' for output", outfilename);
+ exit(1);
+ }
+
+ /*
+ * Now process the input files (or stdin, if none specified)
+ */
+
+ if (argv[optind] == (char *) NULL) /* just stdin */
+ exit(unhex(stdin, "stdin", outfp, outfilename));
+ else
+ for (; (optarg = argv[optind]); optind++)
+ {
+ if (STREQ(optarg, "-"))
+ rc += unhex(stdin, "stdin", outfp, outfilename);
+ else
+ {
+ if ((infp = fopen(optarg, "r")) == (FILE *) NULL)
+ {
+ error(-1, "couldn't open '%s' for input", optarg);
+ exit(1);
+ }
+ rc += unhex(infp, optarg, outfp, outfilename);
+ }
+ }
+
+ return(rc);
+}
+
+u16 filesum;
+
+int
+unhex(FILE *ifp,
+ char *inm,
+ FILE *ofp,
+ char *onm)
+{
+ int c;
+
+ filesum = 0;
+
+ /*
+ * Make sure holes will be filled with 0xFF's if requested. We
+ * do this the easy way by just filling the file with FF's before
+ * getting started. To do it more optimally would be quite a bit
+ * more difficult since the user can skip around as much as he/she
+ * likes in the input hex file addressing.
+ *
+ * We'll clean this up later (after this program has run) with
+ * 'stripffs'
+ */
+
+ if (FFfill)
+ {
+ (void) fseek(ofp, 0, 0);
+ for (c = FFfill; c > 0; c--)
+ (void) fputc(0xFF, ofp);
+ }
+
+ /*
+ * Read the first char from file and determine record types
+ */
+
+ if ((c = getc(ifp)) != EOF)
+ {
+ ungetc(c, ifp);
+ switch(c)
+ {
+ case 'S':
+ convert_S_records(ifp, inm, ofp, onm);
+ break;
+
+ case ':':
+ convert_Intel_records(ifp, inm, ofp, onm);
+ break;
+
+ case '9':
+ case 'B':
+ convert_TI_records(ifp, inm, ofp, onm);
+ break;
+
+ default:
+ {
+ char tmp[2];
+ tmp[0] = c; tmp[1] = 0;
+ badformat(tmp, inm, BADFMT);
+ }
+ }
+ }
+
+ if (verbose)
+ fprintf(stderr, "'%s' checksum is 0x%04x\n", inm, filesum);
+
+ return 0;
+}
+
+int
+convert_Intel_records(
+ FILE *ifp,
+ char *inm,
+ FILE *ofp,
+ char *onm)
+{
+ char buff[512];
+ char *p;
+ u8 cksum;
+ int incksum;
+ int c;
+ int rectype; /* record type */
+ int len; /* data length of current line */
+ u32 addr;
+ u32 base_address = 0;
+ bool endrecord = FALSE;
+ buffer_rec tb;
+
+ while ( ! endrecord && (fgets(buff, sizeof(buff), ifp)))
+ {
+ p = &buff[0];
+
+ if (p[strlen(p)-1] == '\n') /* get rid of newline */
+ p[strlen(p)-1] = '\0';
+
+ if (p[strlen(p)-1] == '\r') /* get rid of any CR */
+ p[strlen(p)-1] = '\0';
+
+ tb.dl_count = 0;
+
+ if (*p != ':')
+ badformat(p, inm, BADFMT);
+ p++;
+
+ if ((len = getbyte(&p)) == -1) /* record len */
+ badformat(buff, inm, BADLEN);
+
+ if ((addr = get2bytes(&p)) == -1L) /* record addr */
+ badformat(buff, inm, BADADDR);
+
+ rectype = getbyte(&p);
+
+ cksum = len + B0(addr) + B1(addr) + rectype;
+
+ switch (rectype)
+ {
+ case 0x00: /* normal data record */
+ tb.dl_destaddr = base_address + addr;
+ while (len--)
+ {
+ if ((c = getbyte(&p)) == -1)
+ badformat(buff, inm, BADDATA);
+ cksum += c;
+ filesum += c;
+ tb.dl_buf[tb.dl_count++] = c;
+ }
+ break;
+
+ case 0x01: /* execution start address */
+ base_address = addr;
+ endrecord = TRUE;
+ break;
+
+ case 0x02: /* new base */
+ if ((base_address = get2bytes(&p)) == -1L)
+ badformat(buff, inm, BADBASE);
+ cksum += B0(base_address) + B1(base_address);
+ base_address <<= 4;
+ break;
+
+ case 0x03: /* seg/off execution start address */
+ {
+ u32 seg, off;
+
+ seg = get2bytes(&p);
+ off = get2bytes(&p);
+ if ((seg == -1L) || (off == -1L))
+ badformat(buff, inm, BADADDR);
+
+ cksum += B0(seg) + B1(seg) + B0(off) + B1(off);
+
+ tb.dl_jumpaddr = (seg << 4) + off;
+ break;
+ }
+
+ default:
+ error(0, "unknown Intel-hex record type: 0x%02x", rectype);
+ badformat(buff, inm, BADTYPE);
+ }
+
+ /*
+ * Verify checksums are correct in file.
+ */
+
+ cksum = (-cksum) & 0xff;
+ if ((incksum = getbyte(&p)) == -1)
+ badformat(buff, inm, BADCSUM);
+ if (((u8) incksum) != cksum)
+ badformat(buff, inm, MISCSUM);
+
+ if (tb.dl_count)
+ write_record(&tb, ofp);
+ }
+ return 0;
+}
+
+int
+convert_S_records(
+ FILE *ifp,
+ char *inm,
+ FILE *ofp,
+ char *onm)
+{
+ char buff[512];
+ char *p;
+ u8 cksum;
+ int incksum;
+ int c;
+ int len; /* data length of current line */
+ int rectype; /* record type */
+ u32 addr;
+ bool endrecord = FALSE;
+ buffer_rec tb;
+
+ while ( ! endrecord && (fgets(buff, sizeof(buff), ifp)))
+ {
+ p = &buff[0];
+
+ if (p[strlen(p)-1] == '\n') /* get rid of newline */
+ p[strlen(p)-1] = '\0';
+
+ if (p[strlen(p)-1] == '\r') /* get rid of any CR */
+ p[strlen(p)-1] = '\0';
+
+ tb.dl_count = 0;
+
+ if (*p != 'S')
+ badformat(p, inm, BADFMT);
+ p++;
+
+ if ((rectype = getnibble(&p)) == -1) /* record type */
+ badformat(buff, inm, BADTYPE);
+
+ if ((len = getbyte(&p)) == -1) /* record len */
+ badformat(buff, inm, BADLEN);
+ cksum = len;
+
+ switch (rectype)
+ {
+ case 0x00: /* comment field, ignored */
+ goto write_it;
+
+ case 0x01: /* data record, 16 bit addr */
+ if ((addr = get2bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+ len -= 3;
+ goto doit;
+
+ case 0x02: /* ... 24 bit addr */
+ if ((addr = get3bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+ len -= 4;
+ goto doit;
+
+ case 0x03: /* ... 32 bit addr */
+ if ((addr = get4bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+ len -= 5;
+ doit:
+ cksum += B0(addr) + B1(addr) + B2(addr) + B3(addr);
+
+ tb.dl_destaddr = addr;
+ while (len--)
+ {
+ if ((c = getbyte(&p)) == -1)
+ badformat(buff, inm, BADDATA);
+ cksum += c;
+ filesum += c;
+ tb.dl_buf[tb.dl_count++] = c;
+ }
+ break;
+
+ case 0x07: /* 32 bit end record */
+ if ((addr = get4bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+ goto end_rec;
+
+ case 0x08: /* 24 bit end record */
+ if ((addr = get3bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+ goto end_rec;
+
+ case 0x09: /* 16 bit end record */
+ if ((addr = get2bytes(&p)) == -1L)
+ badformat(buff, inm, BADADDR);
+
+end_rec:
+ cksum += B0(addr) + B1(addr) + B2(addr) + B3(addr);
+ tb.dl_jumpaddr = addr;
+ break;
+
+ default:
+ error(0, "unknown Motorola-S record type: 0x%02x", rectype);
+ badformat(buff, inm, BADTYPE);
+ break;
+ }
+
+ /*
+ * Verify checksums are correct in file.
+ */
+
+ cksum = (~cksum) & 0xff;
+ if ((incksum = getbyte(&p)) == -1)
+ badformat(buff, inm, BADCSUM);
+ if (((u8) incksum) != cksum)
+ badformat(buff, inm, MISCSUM);
+
+write_it:
+ if (tb.dl_count)
+ write_record(&tb, ofp);
+ }
+ return 0;
+}
+
+int
+convert_TI_records(
+ FILE *ifp,
+ char *inm,
+ FILE *ofp,
+ char *onm)
+{
+ char buff[512];
+ char *p;
+ int c;
+ bool endrecord = FALSE;
+ bool eol;
+ buffer_rec tb;
+
+ while ( ! endrecord && (fgets(buff, sizeof(buff), ifp)))
+ {
+ if (p[strlen(p)-1] == '\n') /* get rid of newline */
+ p[strlen(p)-1] = '\0';
+
+ if (p[strlen(p)-1] == '\r') /* get rid of any CR */
+ p[strlen(p)-1] = '\0';
+
+ tb.dl_count = 0;
+
+ p = &buff[0];
+ eol = FALSE;
+ while ( ! eol && ! endrecord)
+ {
+ switch (*p++)
+ {
+ case '9':
+ if (tb.dl_count)
+ write_record(&tb, ofp);
+ tb.dl_destaddr = get2bytes(&p);
+ break;
+
+ case 'B':
+ c = getbyte(&p);
+ filesum += c;
+ tb.dl_buf[tb.dl_count++] = c;
+ c = getbyte(&p);
+ filesum += c;
+ tb.dl_buf[tb.dl_count++] = c;
+ break;
+
+ case 'F':
+ eol = TRUE;
+ break;
+
+ case ':':
+ endrecord = TRUE;
+ break;
+
+ default:
+ badformat(p, inm, BADFMT);
+ }
+ }
+ if (tb.dl_count)
+ write_record(&tb, ofp);
+ }
+ return 0;
+}
+
+void
+write_record(buffer_rec *tb,
+ FILE *fp)
+{
+ if ( ! linear)
+ {
+ if (tb->dl_destaddr < base)
+ error(ERR_FATAL, "record at address 0x%x precedes base of 0x%x",
+ tb->dl_destaddr, base);
+ (void) fseek(fp, tb->dl_destaddr - base, 0);
+ }
+
+ (void) fwrite(tb->dl_buf, tb->dl_count, 1, fp);
+ tb->dl_destaddr += tb->dl_count;
+ tb->dl_count = 0;
+}
+
+int
+getnibble(char **p)
+{
+ register int val;
+
+ **p = toupper(**p);
+ switch (**p)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ val = **p - '0';
+ break;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ val = 10 + (**p - 'A');
+ break;
+
+ default:
+ return(-1);
+ }
+ *p += 1;
+
+ return(val & 0x0f);
+}
+
+int
+getbyte(char **p)
+{
+ int n0, n1;
+
+ if ((n0 = getnibble(p)) == -1)
+ return(-1);
+ if ((n1 = getnibble(p)) == -1)
+ return(-1);
+
+ return(((n0 << 4) + n1) & 0xff);
+}
+
+long
+getNbytes(char **p,
+ int n)
+{
+ int t;
+ u32 val = 0;
+
+ while (n--)
+ {
+ if ((t = getbyte(p)) == -1)
+ return(-1L);
+ val <<= 8;
+ val += t;
+ }
+
+ return(val);
+}
+
+void
+badformat(char *s,
+ char *fname,
+ char *msg)
+{
+ if (s[strlen(s)-1] == '\n') /* get rid of newline */
+ s[strlen(s)-1] = '\0';
+ error(0, "line '%s'::\n\tfrom file '%s'; %s", s, fname, msg);
+ exit(1);
+}
+
+/*
+ * error(errn, arglist)
+ * report an error to stderr using printf(3) conventions.
+ * Any output is preceded by '<progname>: '
+ *
+ * Uses ERR_EXIT bit to request exit(errn)
+ * ERR_ABORT to request abort()
+ * ERR_ERRNO to indicate use of errno instead of argument.
+ *
+ * If resulting 'errn' is non-zero, it is assumed to be an 'errno' and its
+ * associated error message is appended to the output.
+ */
+
+/*VARARGS*/
+
+void
+error(int error_flag, ...)
+{
+ va_list arglist;
+ register char *format;
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ int local_errno;
+
+ extern int errno;
+
+ (void) fflush(stdout); /* in case stdout/stderr same */
+
+ local_errno = error_flag & ~ERR_MASK;
+ if (error_flag & ERR_ERRNO) /* use errno? */
+ local_errno = errno;
+
+ va_start(arglist, error_flag);
+ format = va_arg(arglist, char *);
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) vfprintf(stderr, format, arglist);
+ va_end(arglist);
+
+ if (local_errno)
+ if ((local_errno > 0) && (local_errno < sys_nerr))
+ (void) fprintf(stderr, " (%s)\n", sys_errlist[local_errno]);
+ else
+ (void) fprintf(stderr, " (unknown errno=%d)\n", local_errno);
+ else
+ (void) fprintf(stderr, "\n");
+
+ (void) fflush(stderr);
+
+ if (error_flag & (ERR_FATAL | ERR_ABORT))
+ {
+ if (error_flag & ERR_FATAL)
+ {
+ error(0, local_errno ? "fatal error, exiting" : "exiting");
+ exit(local_errno);
+ }
+ else
+ {
+ error(0, "fatal error, aborting");
+ abort();
+ }
+ }
+}
+