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.