This is srec.c in view mode; [Download] [Up]
#include <stdio.h> #include <ctype.h> #include <signal.h> #include <setjmp.h> #include "srec.h" /* NAME srec - convert DSP56000 load file records to Motorola S-record format SYNOPSIS srec [-1 | -3 | -r] <input file ... > DESCRIPTION Srec takes as input a DSP56000 absolute load file and produces byte-wide Motorola S-record files suitable for PROM burning. The DSP56000 START and END records are mapped into S0 and S9 records respectively. All other DSP56000 record types are mapped into S1-type records (SYMBOL and COMMENT records are currently ignored). Since the DSP56000 uses a 24-bit word size, the words must be split into bytes and stored in an appropriate format. In the default mode of operation the program writes the low, middle, and high bytes of each word consecutively to the current S1 record being written. For example, given the DSP56000 DATA record below: address field | _DATA P 0000 0008F8 300000 340000 094E3E | | | | | | | fourth word | | third word | second word first word srec would create the following S1 record: byte count field | address field checksum field | | | S10D0000F808000000300000343E4E09F9 | | | | | | | fourth word | | third word | second word first word Output records are written to a file named according to the following convention: <basename>.M where <basename> is the filename of the input load file without extension and M is the memory space specifier (X, Y, L, or P) for this set of data words. Note that a separate file is created for each memory space encountered in the input file; thus the maximum number of output files in the default mode is 4. When the -3 option is specified, srec splits each 24-bit word into bytes and stores the bytes in three parallel S1 records. For example, the following DSP56000 DATA record: address field | _DATA P 0000 0008F8 300000 340000 094E3E | | | | | | | fourth word | | third word | second word first word would be converted by srec into the three S1 records below: byte count field | address field | | S1070000F800003EC2 -- low byte S10700000800004EA2 -- mid byte S1070000003034098B -- high byte | | | | | | | | | checksum field | | | fourth word | | third word | second word first word The three records corresponding to the high, middle, and low bytes of each data word are written to separate files. The files are named according to the following convention: <basename>.<M><#> where <basename> is the filename of the input load file without extension, <M> is the memory space specifier (X, Y, L, or P) for this set of data words, and # is one of the digits 0, 1, or 2 corresponding to low, middle, and high bytes, respectively. Note that a separate set of byte-wide files is created for each memory space encountered in the input file. Thus the number of output files generated is (number of memory spaces in input * 3). The maximum number of output files generated with the -3 option is 12. The -1 option writes all information to a single file, storing the memory space information in the address field of the S0 header record. The values stored in the address field and their correspondence to the DSP56000 memory spaces are as follows: Value DSP56000 Memory Space ----- --------------------- 1 X 2 Y 3 L 4 P When the memory space changes in the DATA or BLOCKDATA record, a new S0 header record is generated. The resulting output file is named <basename>.s, where <basename> is the filename of the input load file without extension. The -1 and -3 options are mutually exclusive. The -r option causes srec to produce the same type of output as the default mode, except that the bytes are written high to low, rather than low to high. Address fields in the DSP56000 records are two bytes and are copied as is to the appropriate S1 record (except for incrementing as necessary in very long DATA records). Only the module id in the START record is passed as header data for the S0 record; the version number, revision number, and comment are ignored. NOTES One (and only one) of the operating system DEFINEs below must be activated to obtain appropriate conditional compilation. This can be done either directly in the source by changing the 0 DEFINE for the desired operating system to a 1, or via the compiler command line (ex. -DATT=1). Note that the UNIX flag is simply a logical ORing of the more specific Unix OS flags (ATT and BSD) and thus should not be specified explicitly. OPTIONS -1 - write data to a single file, putting memory space information into the address field of the S0 header record. Bytes may be reversed with the -r option. The -1 and -3 options are mutually exclusive. -3 - split each 24-bit word into bytes and store the bytes in three parallel S1 records. The -1 and -3 options are mutually exclusive. -r - produce the same type of output as default mode, but write bytes high to low, rather than low to high. to low, rather than low to high. DIAGNOSTICS The program does some checking for invalid input record formats, memory spaces, and hex values. It obviously cannot check for bad data values if the load file has been tampered with. Both START and END records must be found in the input file. HISTORY 1.0 The beginning. 1.1 Added code to support new default mode and -r option (-3 was old default mode). 1.2 Added support for ATT Unix System V. 1.3 Fix for bug in default mode where S-record address was not being computed correctly (e.g. not a byte multiple of the 56000 word size). 1.4 Added support for Macintosh II. 1.5 Added separate include file, getopt () support, -1 (single output file) option. 1.6 Fix for bug in default mode when handling BLOCKDATA records; repeated call to reverse bytes was reversing bytes in place! */ char *optarg = NULL; /* command argument pointer */ optind = 0; /* command argument index */ char Progdef[] = "srec"; /* program default name */ char *Progname = Progdef; /* pointer to program name */ static char *ifname; /* pointer to input file name */ static FILE *ifile = NULL; /* file pointer for input file */ static opt1 = NO; /* 1 file option flag */ static opt3 = NO; /* 3 file option flag */ static optrev = NO; /* reverse byte option flag */ static unsigned linecount = 1; /* input file line count */ static char fldbuf[MAXSTR] = {EOS}; /* OMF field buffer */ static char strbuf[MAXSTR] = {EOS}; /* Global string buffer */ static char s0[MAXSTR]; /* S0 record string */ static char s0str[MAXSTR]; /* S0 string buffer */ static unsigned s0cnt = 1; /* S0 byte count */ static unsigned s0addr = 0; /* S0 address */ static unsigned s0sum = 0; /* S0 checksum */ /* pointers to S-record structure arrays */ static struct srec *rec[] = {NULL, NULL, NULL, NULL}; extern char *calloc (), *malloc (); #if BSD extern char *index (), *rindex (); #else extern char *strchr (), *strrchr (); #endif char *strup (); #if LSC jmp_buf Env; #endif #if !LSC main (argc, argv) #else _main (argc, argv) #endif int argc; char *argv[]; { #if BSD int onintr (), onsegv (); #else void onintr (), onsegv (); #endif int c; char *p, *q; char *basename(), *getenv(); char *fix_fname (); /* set up for signals; get prog. name, check for command line options */ #if MPW || AZTEC (void)sigset (SIGINT, onintr); /* set up for signals */ #else (void)signal (SIGINT, onintr); /* set up for signals */ #endif p = basename (argv[0]); /* get command base name */ if ((q = strrchr (p, '.')) != NULL) *q = EOS; #if VMS if ((q = strrchr (p, ';')) != NULL) *q = EOS; #endif #if MSDOS if (_osmajor > 2) #endif Progname = p; optarg = NULL; /* command argument pointer */ optind = 0; /* command argument index */ if (argc < 2) /* not enough arguments */ usage (); while ((c = getopt (argc, argv, "13r?")) != EOF) { switch (c) { case '1': opt1 = YES; break; case '3': opt3 = YES; break; case 'r': optrev = YES; break; case '?': default: usage (); break; } if (optind >= argc) /* no more args? error */ usage (); } /* reset argument vector and count */ argc -= optind; /* adjust argument count */ argv = argv[optind]; /* reset argv pointer */ if (argc < 1 || (opt1 & opt3)) usage (); /* loop to process files */ while (argc--) { ifname = fix_fname (*argv); /* fix file name */ if (!(ifile = fopen (ifname, "r"))) error2 ("cannot open input file %s", ifname); if ((p = strrchr (ifname, '.')) != NULL) *p = EOS; /* strip extension */ do_srec (); /* process records */ fclose (ifile); /* close input file */ free (ifname); /* free file name */ argv++; /* bump argument pointer */ } #if LSC return (OK); #else exit (OK); #endif } static do_srec () /* process load file records */ { if (!get_start ()) /* no START record */ error ("no START record"); for (;;) { /* loop forever */ switch (get_record ()) { case START: error ("duplicate START record"); case END: get_end (); return; case DATA: get_data (); break; case BDATA: get_bdata (); break; case COMMENT: get_comment (); break; case SYMBOL: fldbuf[0] = EOS; /* skip SYMBOL records */ break; default: if (feof (ifile)) error ("no END record"); else error ("invalid record type"); break; } } } static get_start () /* process START record */ { int type; char *fbp = fldbuf; char *sbp = s0str; /* locate START record */ while ((type = get_record ()) != EOF & type != START) ; if (type != START) return (NO); /* get program name; loop to put into header string */ if (get_field () < 0) /* pick up program name */ error ("invalid START record"); while (*fbp) { sprintf (sbp, "%02x", *fbp); s0sum += (unsigned)(*fbp++ 0xff); sbp += 2; s0cnt++; } /* build S0 record; read past comment */ bld_s0 ((unsigned)NONE); if ( (get_field () < 0) || /* skip version number */ (get_field () < 0) || /* skip revision number */ (get_comment () < 0) ) /* skip comment */ error ("invalid START record"); if (opt1) /* single file option */ open_files (NONE); return (YES); } static get_record () /* look for next record in OMF input */ { int field = 0; char *fbp = fldbuf + 1; while (fldbuf[0] != NEWREC & (field = get_field ()) == 0) ; if (field < 0) return (field); if (strcmp (fbp, "DATA") == 0) return (DATA); if (strcmp (fbp, "BLOCKDATA") == 0) return (BDATA); if (strcmp (fbp, "START") == 0) return (START); if (strcmp (fbp, "END") == 0) return (END); if (strcmp (fbp, "SYMBOL") == 0) return (SYMBOL); if (strcmp (fbp, "COMMENT") == 0) return (COMMENT); return (NONE); } static get_data () /* process DATA records */ { int space, i; unsigned addr, count = OVRHD; struct srec *drec; if (get_field () < 0) /* pick up memory space */ error ("invalid DATA record"); if ((space = get_space ()) < 0) error ("invalid memory space specifier"); if (opt1) { /* single file option */ i = space + 1; space = NONE; if (s0addr != (unsigned)i) { bld_s0 ((unsigned)(i)); fputs (s0, rec[0]->fp); s0addr = (unsigned)i; } } else if (!rec[space]) /* see if files are open */ open_files (space); if (get_field () < 0) /* read in address field */ error ("invalid DATA record"); if (!fldhex ()) error ("invalid address value"); sscanf (fldbuf, "%x", addr); /* convert address */ if (addr > MAXADDR) error ("address value too large"); if (!opt3) addr *= WSIZE; /* adjust address for serial bytes */ /* initialize data record fields */ for (i = 0, drec = rec[space]; i < (opt3 ? WSIZE : 1); i++, drec++) { drec->checksum = 0; drec->p = drec->buf; } /* loop to pick up data */ while (get_field () == 0) { /* get next data field */ if (strlen (fldbuf) != WSIZE * 2) error ("improper number of bytes in word"); if (!fldhex ()) error ("invalid data value"); if (!optrev & !opt3) rev_bytes (); get_bytes (space); /* extract bytes from field */ /* if max record count reached, print out current record */ count += opt3 ? 1 : WSIZE; if (count >= MAXBYTE + OVRHD) { flush_rec (space, addr, count); addr += count - OVRHD; count = OVRHD; } } /* new record or EOF encountered; flush out current record */ if (rec[space]->p != rec[space]->buf) flush_rec (space, addr, count); } static get_bdata () /* process BLOCKDATA records */ { int space, i, j; unsigned addr, repeat, count = OVRHD; struct srec *drec; if (get_field () < 0) /* pick up memory space */ error ("invalid BLOCKDATA record"); if ((space = get_space ()) < 0) error ("invalid memory space specifier"); if (opt1) { /* single file option */ i = space + 1; space = NONE; if (s0addr != (unsigned)i) { bld_s0 ((unsigned)(i)); fputs (s0, rec[0]->fp); s0addr = (unsigned)i; } } else if (!rec[space]) /* see if files are open */ open_files (space); if (get_field () < 0) /* read in address field */ error ("invalid BLOCKDATA record"); if (!fldhex ()) error ("invalid address value"); sscanf (fldbuf, "%x", addr); /* convert address */ if (addr > MAXADDR) error ("address value too large"); if (!opt3) addr *= WSIZE; /* adjust address for serial bytes */ if (get_field () < 0) /* read in repeat field */ error ("invalid BLOCKDATA record"); if (!fldhex ()) error ("invalid count value"); sscanf (fldbuf, "%x", repeat); /* save repeat value */ if (get_field () < 0) /* read in value field */ error ("invalid BLOCKDATA record"); if (!fldhex ()) error ("invalid data value"); if (strlen (fldbuf) != WSIZE * 2) error ("improper number of bytes in word"); if (!fldhex ()) error ("invalid data value"); if (!optrev & !opt3) rev_bytes (); /* initialize data record fields */ for (i = 0, drec = rec[space]; i < (opt3 ? WSIZE : 1); i++, drec++) { drec->checksum = 0; drec->p = drec->buf; } /* loop to generate data records */ for (j = 0; j < repeat; j++) { get_bytes (space); /* extract bytes from field */ /* if max record count reached, print out current record */ count += opt3 ? 1 : WSIZE; if (count >= MAXBYTE + OVRHD) { flush_rec (space, addr, count); addr += count - OVRHD; count = OVRHD; } } /* new record or EOF encountered; flush out current record */ if (rec[space]->p != rec[space]->buf) flush_rec (space, addr, count); } static get_end () /* process END record, clean up, and exit */ { int i, field, space; unsigned count = 1; /* always a checksum byte */ unsigned addr, checksum = 0; struct srec *drec; if ((field = get_field ()) > 0) /* try to get address field */ error ("invalid END record"); if (field == 0) { if (!fldhex ()) error ("invalid address value"); sscanf (fldbuf, "%x", addr); /* convert address */ if (addr > MAXADDR) error ("address value too large"); if (!opt3) addr *= WSIZE; /* adjust address for serial bytes */ checksum = (unsigned)sum_addr (addr); count = OVRHD; } for (space = 0; space < (opt1 ? 1 : MSPACES); space++) { if (rec[space]) { for (i = 0, drec = opt1 ? rec[0] : rec[space]; i < (opt3 ? WSIZE : 1); i++, drec++) { sprintf (strbuf, "S9%02x%s%02x\n", count, field == 0 ? fldbuf : "", ~(checksum + count) 0xff); fputs (strup (strbuf), drec->fp); fclose (drec->fp); } free (rec[space]); rec[space] = NULL; } } } static sum_addr (addr) /* sum bytes of address */ unsigned addr; { return ((addr 0xff) + ((addr >> 8) 0xff)); } static get_bytes (space) /* move bytes from field buffer into record buffer */ int space; { int i, j; register char *fbp, *p; struct srec *drec; fbp = fldbuf; /* loop to move bytes, sum for checksum */ for (i = 0, drec = rec[space]; i < WSIZE; i++, drec += opt3 ? 1 : 0) { p = drec->p; *p++ = *fbp++; *p++ = *fbp++; *p = EOS; sscanf (drec->p, "%x", j); drec->checksum += (unsigned)j; drec->p = p; } } static flush_rec (space, addr, count) /* flush S1 record to the appropriate file */ int space; unsigned addr, count; { int i; struct srec *drec; for (i = 0, drec = rec[space]; i < (opt3 ? WSIZE : 1); i++, drec++) { drec->checksum += (unsigned)sum_addr (addr); sprintf (strbuf, "S1%02x%04x%s%02x\n", count, addr, drec->buf, ~(drec->checksum + count) 0xff); fputs (strup (strbuf), drec->fp); drec->checksum = 0; drec->p = drec->buf; } } static rev_bytes () /* reverse the bytes in fldbuf */ { char c; c = fldbuf[0]; fldbuf[0] = fldbuf[4]; fldbuf[4] = c; c = fldbuf[1]; fldbuf[1] = fldbuf[5]; fldbuf[5] = c; } static open_files (space) /* open S-record ouput files */ int space; { struct srec *drec; char fn[MAXSTR], *p; register i; char c; /* allocate S-record structure array; construct output file name */ if ((drec = (struct srec *)calloc (opt3 ? WSIZE : 1, sizeof (struct srec))) == NULL) error ("cannot allocate S-record structure"); rec[space] = drec; /* squirrel pointer away */ c = opt1 ? 's' : fldbuf[0]; sprintf (fn, "%s.%c", ifname, isupper (c) ? tolower (c) : c); p = fn + strlen (fn); /* loop to open all files, write out S0 record */ if (!opt3) { /* don't need three files */ if ((drec->fp = fopen (fn, "w")) == NULL) error2 ("cannot open output file %s", fn); if (!opt1) fputs (s0, drec->fp); } else for (i = WSIZE - 1, rec[space] = drec; i >= 0; i--, drec++) { *p = (char)(i + '0'); *(p + 1) = EOS; if ((drec->fp = fopen (fn, "w")) == NULL) error2 ("cannot open output file %s", fn); fputs (s0, drec->fp); } } static get_field () /* get next OMF field from ifile; put in fldbuf */ { register c; register char *p; while ((c = fgetc (ifile)) != EOF & isspace (c)) if (c == '\n') linecount++; if (c == EOF) /* end of object file */ return (EOF); for (p = fldbuf, *p++ = c; /* loop to pick up field value */ (c = fgetc (ifile)) != EOF & !isspace (c); *p++ = c) ; *p = EOS; /* null at end of value */ if (c != EOF) ungetc (c, ifile); /* put back last char. if not EOF */ return (*fldbuf == NEWREC ? 1 : 0);/* let caller know if new record */ } static get_comment () /* get comment from OMF file; put in fldbuf */ { register c; register char *p; while ((c = fgetc (ifile)) != EOF & c != '\n' & isspace (c)) ; /* skip white space (except newline) */ if (c == EOF || c != '\n') /* end of file or synch error */ return (EOF); linecount++; for (p = fldbuf; (c = fgetc (ifile)) != EOF & c != '\n'; *p++ = c) ; /* loop to pick up comment */ if (c == '\n') linecount++; *p = EOS; /* null at end of comment */ return (0); /* good return */ } static bld_s0 (space) unsigned space; { sprintf (s0, "S0%02x%04x%s%02x\n", s0cnt, space, s0str, ~(s0sum + s0cnt + space) 0xff); strup (s0); } static get_space () /* return memory space attribute */ { switch (fldbuf[0]) { case 'X': case 'x': return (XMEM); case 'Y': case 'y': return (YMEM); case 'P': case 'p': return (PMEM); case 'L': case 'l': return (LMEM); default: return (-1); } } static char * fix_fname (fname) /* add extension to file name if reqd */ char *fname; { int len = strlen (fname); char *fn; register char *p, *np, *ep = NULL; static char *defext = ".lod"; if ((fn = malloc (len + MAXEXTLEN + 1)) == NULL) error ("cannot allocate file name"); strcpy (fn, fname); np = p = fn + len; /* save end */ while (p != fn) { if (*--p == '.') ep = p; #if MSDOS if (*p == '\\' || *p == ':') #endif #if VMS if (*p == ']' || *p == ':') #endif #if UNIX if (*p == '/') #endif #if MAC if (*p == ':') #endif break; else continue; } if (p != fn) p++; if (!ep) { /* no extension supplied */ strcpy (np, defext); ep = np; np += strlen (defext); } if (np - p > BASENAMLEN) { /* filename too long -- truncate */ np = p + (BASENAMLEN - MAXEXTLEN); p = np; while (*ep & np < p + MAXEXTLEN) *np++ = *ep++; *np = EOS; } return (fn); } static char * strup (str) /* convert all alpha chars in str to upper */ char *str; { register char *p = str; while (*p) { if (isalpha (*p) & islower (*p)) *p = toupper (*p); p++; } return (str); } static fldhex () /* insure fldbuf contains hex value */ { register char *p = fldbuf; while (*p) { if (!isxdigit (*p)) break; p++; } if (!*p) return (YES); else return (NO); } static char * basename (str) /* return base part of file name in str */ char *str; { register char *p; if (!str) /* empty input */ return (NULL); for (p = str + strlen (str); p >= str; --p) #if MSDOS if( *p == '\\' || *p == ':') #endif #if VMS if( *p == ']' || *p == ':') #endif #if UNIX if( *p == '/' ) #endif #if MAC if( *p == ':' ) #endif break; return (p < str ? str : ++p); } getopt (argc, argv, optstring) /* get option letter from argv */ int argc; char *argv[]; char *optstring; { register char c; register char *place; static char *scan = NULL; extern char *index(); optarg = NULL; if (scan == NULL || *scan == '\0') { if (optind == 0) optind++; if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); if (strcmp(argv[optind], "--")==0) { optind++; return(EOF); } scan = argv[optind]+1; optind++; } c = *scan++; place = index(optstring, c); if (place == NULL || c == ':') { (void)fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); return('?'); } place++; if (*place == ':') { if (*scan != '\0') { optarg = scan; scan = NULL; } else if (optind < argc) { optarg = argv[optind]; optind++; } else { (void)fprintf( stderr, "%s: -%c argument missing\n", argv[0], c); return( '?' ); } } return(c); } #if BSD static #else static void #endif onintr () /* clean up from signal */ { error ("Interrupted"); } static usage () /* display usage on stderr, exit nonzero */ { fprintf (stderr, "usage: %s [-1 | -3 | -r] <input file ... >\n", Progname); #if LSC longjmp (Env, 1); #else exit (ERR); #endif } static error (str) char *str; { fprintf (stderr, "%s: at line %d: ", Progname, linecount); fprintf (stderr, "%s\n", str); #if LSC longjmp (Env, 1); #else exit (ERR); #endif } static error2 (fmt, str) char *fmt, *str; { fprintf (stderr, "%s: at line %d: ", Progname, linecount); fprintf (stderr, fmt, str); fprintf (stderr, "\n"); #if LSC longjmp (Env, 1); #else exit (ERR); #endif }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.