448 lines
18 KiB
Plaintext
448 lines
18 KiB
Plaintext
S-Records
|
|
|
|
|
|
-S-Record Format-
|
|
|
|
Chaplin@keinstr.uucp (Roger Chaplin) reposted an article written
|
|
by mcdchg!motmpl!ron (Ron Widell) that explained how Motorola
|
|
S-Records are formatted. This comes from a unix man page. No
|
|
mention of which version of Unix is specified. This section
|
|
of the FAQ is a bit long. An anonymous ftp archive is currently
|
|
being sought. When one is found, the section will be placed in
|
|
the archive.
|
|
|
|
|
|
SREC(4) UNIX 5.0 (03/21/84) SREC(4)
|
|
|
|
|
|
An S-record file consists of a sequence of specially formatted
|
|
ASCII character strings. An S-record will be less than or equal to
|
|
78 bytes in length.
|
|
|
|
The order of S-records within a file is of no significance and no
|
|
particular order may be assumed.
|
|
|
|
The general format of an S-record follow:
|
|
|
|
+------------------//-------------------//-----------------------+
|
|
| type | count | address | data | checksum |
|
|
+------------------//-------------------//-----------------------+
|
|
|
|
type A char-2- field. These characters describe the
|
|
type of record (S0, S1, S2, S3, S5, S7, S8, or
|
|
S9).
|
|
count A char-2- field. These characters when paired and
|
|
interpreted as a hexadecimal value, display the
|
|
count of remaining character pairs in the record.
|
|
|
|
address A char-4,6, or 8- field. These characters grouped
|
|
and interpreted as a hexadecimal value, display
|
|
the address at which the data field is to be
|
|
loaded into memory. The length of the field
|
|
depends on the number of bytes necessary to hold
|
|
the address. A 2-byte address uses 4 characters,
|
|
a 3-byte address uses 6 characters, and a 4-byte
|
|
address uses 8 characters.
|
|
data A char -0-64- field. These characters when paired
|
|
and interpreted as hexadecimal values represent
|
|
the memory loadable data or descriptive
|
|
information.
|
|
|
|
checksum A char-2- field. These characters when paired and
|
|
interpreted as a hexadecimal value display the
|
|
least significant byte of the ones complement of
|
|
the sum of the byte values represented by the
|
|
pairs of characters making up the count, the
|
|
address, and the data fields.
|
|
|
|
Each record is terminated with a line feed. If any
|
|
additional or different record terminator(s) or delay
|
|
characters are needed during transmission to the target
|
|
system it is the responsibility of the transmitting program
|
|
to provide them.
|
|
|
|
S0 Record The type of record is 'S0' (0x5330). The address
|
|
|
|
|
|
field is unused and will be filled with zeros
|
|
(0x0000). The header information within the data
|
|
field is divided into the following subfields.
|
|
|
|
mname is char-20- and is the
|
|
module name.
|
|
ver is char-2- and is the
|
|
version number.
|
|
|
|
rev is char-2- and is the
|
|
revision number.
|
|
description is char-0-36- and is a
|
|
text comment.
|
|
|
|
Each of the subfields is composed of ASCII bytes
|
|
whose associated characters, when paired,
|
|
represent one byte hexadecimal values in the case
|
|
of the version and revision numbers, or represent
|
|
the hexadecimal values of the ASCII characters
|
|
comprising the module name and description.
|
|
|
|
S1 Record The type of record field is 'S1' (0x5331). The
|
|
address field is interpreted as a 2-byte address.
|
|
The data field is composed of memory loadable
|
|
data.
|
|
S2 Record The type of record field is 'S2' (0x5332). The
|
|
address field is interpreted as a 3-byte address.
|
|
The data field is composed of memory loadable
|
|
data.
|
|
|
|
S3 Record The type of record field is 'S3' (0x5333). The
|
|
address field is interpreted as a 4-byte address.
|
|
The data field is composed of memory loadable
|
|
data.
|
|
S5 Record The type of record field is 'S5' (0x5335). The
|
|
address field is interpreted as a 2-byte value
|
|
and contains the count of S1, S2, and S3 records
|
|
previously transmitted. There is no data field.
|
|
|
|
S7 Record The type of record field is 'S7' (0x5337). The
|
|
address field contains the starting execution
|
|
address and is interpreted as 4-byte address.
|
|
There is no data field.
|
|
S8 Record The type of record field is 'S8' (0x5338). The
|
|
address field contains the starting execution
|
|
address and is interpreted as 3-byte address.
|
|
There is no data field.
|
|
|
|
S9 Record The type of record field is 'S9' (0x5339). The
|
|
address field contains the starting execution
|
|
address and is interpreted as 2-byte address.
|
|
There is no data field.
|
|
|
|
EXAMPLE
|
|
|
|
Shown below is a typical S-record format file.
|
|
|
|
S00600004844521B
|
|
S1130000285F245F2212226A000424290008237C2A
|
|
S11300100002000800082629001853812341001813
|
|
S113002041E900084E42234300182342000824A952
|
|
S107003000144ED492
|
|
S5030004F8
|
|
S9030000FC
|
|
|
|
The file consists of one S0 record, four S1 records, one S5
|
|
record and an S9 record.
|
|
|
|
The S0 record is comprised as follows:
|
|
|
|
S0 S-record type S0, indicating it is a header
|
|
record.
|
|
06 Hexadecimal 06 (decimal 6), indicating that six
|
|
character pairs (or ASCII bytes) follow.
|
|
|
|
00 00 Four character 2-byte address field, zeroes in
|
|
this example.
|
|
48 ASCII H, D, and R - "HDR".
|
|
|
|
1B The checksum.
|
|
|
|
The first S1 record is comprised as follows:
|
|
S1 S-record type S1, indicating it is a data record
|
|
to be loaded at a 2-byte address.
|
|
|
|
13 Hexadecimal 13 (decimal 19), indicating that
|
|
nineteen character pairs, representing a 2 byte
|
|
address, 16 bytes of binary data, and a 1 byte
|
|
checksum, follow.
|
|
00 00 Four character 2-byte address field; hexidecimal
|
|
address 0x0000, where the data which follows is to
|
|
be loaded.
|
|
|
|
28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen
|
|
character pairs representing the actual binary
|
|
data.
|
|
2A The checksum.
|
|
|
|
The second and third S1 records each contain 0x13 (19)
|
|
character pairs and are ended with checksums of 13 and 52,
|
|
respectively. The fourth S1 record contains 07 character
|
|
pairs and has a checksum of 92.
|
|
|
|
The S5 record is comprised as follows:
|
|
|
|
S5 S-record type S5, indicating it is a count record
|
|
indicating the number of S1 records.
|
|
|
|
|
|
|
|
03 Hexadecimal 03 (decimal 3), indicating that three
|
|
character pairs follow.
|
|
|
|
00 04 Hexadecimal 0004 (decimal 4), indicating that
|
|
there are four data records previous to this
|
|
record.
|
|
F8 The checksum.
|
|
|
|
The S9 record is comprised as follows:
|
|
|
|
S9 S-record type S9, indicating it is a termination
|
|
record.
|
|
03 Hexadecimal 03 (decimal 3), indicating that three
|
|
character pairs follow.
|
|
|
|
00 00 The address field, hexadecimal 0 (decimal 0)
|
|
indicating the starting execution address.
|
|
FC The checksum.
|
|
|
|
|
|
-Intel Hex ASCII Format-
|
|
|
|
Intel HEX-ASCII format takes the form:
|
|
|
|
+----------------------------------- Start Character
|
|
|
|
|
| +-------------------------------- Byte Count
|
|
| | (# of data bytes)
|
|
| |
|
|
| | +-------------------------- Address of first data.
|
|
| | |
|
|
| | | +-------------------- Record Type (00 data,
|
|
| | | | 01 end of record)
|
|
| | | |
|
|
| | | | +------------ Data Bytes
|
|
| | | | |
|
|
| | | | | +---- Checksum
|
|
| | | | | |
|
|
| / \ / \ / \ / \ / \
|
|
: B C A A A A T T H H ... H H C C
|
|
|
|
An examples:
|
|
|
|
:10000000DB00E60F5F1600211100197ED300C3004C
|
|
:1000100000000101030307070F0F1F1F3F3F7F7FF2
|
|
:01002000FFE0
|
|
:00000001FF
|
|
|
|
This information comes from _Microprocessors and Programmed
|
|
Logic_, Second Edition, Kenneth L. Short, 1987, Prentice-Hall,
|
|
ISBN 0-13-580606-2.
|
|
|
|
Provisions have been made for data spaces larger than 64 kBytes.
|
|
The above reference does not discuss them. I suspect there is
|
|
a start of segment type record, but I do not know how it is
|
|
implemented.
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
/* This file contains source code to read a Motorola S-record file into
|
|
** a memory image. The size of the file cannot exceed BUFSIZE of data.
|
|
** The image is then written to disk either as binary data starting at
|
|
** address 0 with no data gaps, or as a C array of unsigned longs.
|
|
** Input lines must be no longer than MAXLINE. No check is made!
|
|
**
|
|
** Author: Eric McRae, Electro-Logic Machines, Inc.
|
|
** Date: Copyright 1994
|
|
**
|
|
** This source code is made available to the public "as is". No
|
|
** warranty is given or implied for it's proper operation. This source
|
|
** code may be used in whole or in part as long as this copyright is
|
|
** included.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* Comment the following line for non PC applications */
|
|
#define PCDOS
|
|
|
|
/* Uncomment the following line if you want a binary output instead of
|
|
** a structure
|
|
*/
|
|
/* #define BINARY */
|
|
|
|
#ifdef PCDOS /* Intel x86 architecture */
|
|
#define BUFSIZE 49152 /* 48K to avoid segment hopping */
|
|
#else /* Any reasonable (non-segmented) arch... */
|
|
#define BUFSIZE 65536 /* As big as you want */
|
|
#endif
|
|
|
|
#define MAXLINE 256 /* Length of longest input line + 1 */
|
|
/* Globals */
|
|
FILE *infilePH, *outfilePH; /* Handles for input and output files */
|
|
unsigned char *bufAC, /* Allocated image buffer */
|
|
*highestPC = NULL; /* Highest buffer address written */
|
|
|
|
/* Change this string to reflect the name of the output array */
|
|
char headerAC[] = "unsigned long sRec[] =\n{\n";
|
|
|
|
/* Predeclarations */
|
|
int parsebufN( char * ); /* Does the actual parsing */
|
|
|
|
void main(int argc, const char * argv[])
|
|
{
|
|
int c, /* Temp char storage */
|
|
resN; /* result status */
|
|
char *lbufPC, lbufAC[MAXLINE];
|
|
int linectrN = 0; /* Used to correlate parse fail to input line */
|
|
|
|
#ifndef BINARY
|
|
int i;
|
|
unsigned long *codePL;
|
|
unsigned char *codePC;
|
|
#endif
|
|
|
|
/* Check the argument count */
|
|
if( argc != 3 ) /* If didn't specify input and output files */
|
|
{
|
|
printf("Usage: %s: infile outfile\n", argv[0] );
|
|
exit(1);
|
|
}
|
|
|
|
/* OK, let's open some files */
|
|
if( ( infilePH = fopen( argv[1], "r" ) )== NULL )
|
|
{
|
|
printf("%s: Couldn't open input file %s\n", argv[0], argv[1] );
|
|
exit(2);
|
|
}
|
|
|
|
if( ( outfilePH = fopen( argv[2], "w" ) ) == NULL )
|
|
{
|
|
printf("%s: Couldn't open output file %s\n", argv[0], argv[3] );
|
|
exit(3);
|
|
}
|
|
|
|
/* OK, get a buffer and clear it. */
|
|
if( (bufAC = calloc( (size_t)BUFSIZE, (size_t)1 )) == NULL )
|
|
{
|
|
printf("%s: Couldn't malloc memory for buffer\n", argv[0] );
|
|
exit(4);
|
|
}
|
|
|
|
lbufPC = lbufAC; /* Point at beginning of line buffer */
|
|
while( c = fgetc( infilePH ))
|
|
{
|
|
if( (c == '\n') || (c == EOF) ) /* If found end of line or file */
|
|
{ /* Parse the Line */
|
|
if( ( c == EOF ) && ( ferror( infilePH ) ) )
|
|
{
|
|
printf("%s: Error reading input file\n", argv[0] );
|
|
exit(5);
|
|
}
|
|
else
|
|
{ /* OK, have a complete line in buffer */
|
|
linectrN++; /* Increment line counter */
|
|
if( lbufPC == lbufAC )
|
|
break; /* ignore blank lines */
|
|
*lbufPC = 0; /* Terminate the line string */
|
|
if( resN = parsebufN( lbufAC ) ) /* Parse data record to mem */
|
|
{
|
|
printf("%s: Error reading input file at line %d, return code = %d\n",
|
|
argv[0], linectrN, resN );
|
|
exit( resN );
|
|
}
|
|
lbufPC = lbufAC; /* Repoint line buffer pointer */
|
|
} /* End of have a complete line */
|
|
}
|
|
else
|
|
*lbufPC++ = c; /* Place char into line buffer */
|
|
}
|
|
|
|
/* At this point, the input file has been emptied. Now dispose of the
|
|
** output data according to compilation mode.
|
|
*/
|
|
|
|
#ifdef BINARY
|
|
|
|
/* Write the buffer back to disk as a binary image */
|
|
resN = fwrite( bufAC, 1, (size_t)((highestPC - bufAC) + 1), outfilePH );
|
|
if( resN != (int)( (highestPC - bufAC) + 1) )
|
|
{
|
|
printf("%s: Error writing output file\n", argv[0] );
|
|
exit( 6 );
|
|
}
|
|
|
|
#else
|
|
/* Produce a file that can be included in a C program. Data is read
|
|
** from buffer as bytes to avoid portability/endian problems with
|
|
** this program.
|
|
*/
|
|
/* Output header first, then 1 long per line */
|
|
fwrite( (void *)headerAC, 1, (size_t)(sizeof( headerAC )-1), outfilePH );
|
|
|
|
codePL = (unsigned long *)bufAC;
|
|
for( i = (highestPC - bufAC + 1) / 4; i; i-- ) /* for each long */
|
|
{
|
|
codePC = (unsigned char *)codePL++;
|
|
sprintf(lbufAC, "0x%02x%02x%02x%02x%s",
|
|
*codePC, *(codePC + 1), *(codePC + 2), *(codePC + 3),
|
|
i == 1 ? "\n" : ",\n" ); /* No comma after final long */
|
|
fwrite( lbufAC, 1, (size_t)(strlen( lbufAC )), outfilePH );
|
|
}
|
|
/* OK, data has been written out, close end of array */
|
|
fwrite( "};\n", 1, (size_t)3, outfilePH );
|
|
#endif
|
|
}
|
|
|
|
/* Function: parsebufV
|
|
** Parses an S-record in the buffer and writes it into the buffer
|
|
** if it is has a valid checksum.
|
|
**
|
|
** Args: pointer to character buffer for null terminated line
|
|
** Returns: int result code: 0 = success, else failure
|
|
*/
|
|
int parsebufN( char *lbufPC )
|
|
{
|
|
unsigned long addrL;
|
|
unsigned char cksmB, /* checksum of addr, count, & data length */
|
|
*bufPC; /* Pointer into memory array */
|
|
int i, countN, /* Number of bytes represented in record */
|
|
oheadN, /* Number of overhead (addr + chksum) bytes */
|
|
tvalN; /* Temp for check checksum */
|
|
|
|
switch( *(lbufPC+1) ) /* examine 2nd character on the line */
|
|
{
|
|
case '1': /* 16 bit address field */
|
|
if( sscanf(lbufPC, "S1%2x%4lx", &countN, &addrL ) != 2 )
|
|
return( 10 ); /* Flag error in S1 record */
|
|
oheadN = 3; /* 2 address + 1 checksum */
|
|
break;
|
|
|
|
case '2': /* 24 bit address field */
|
|
if( sscanf(lbufPC, "S2%2x%6lx", &countN, &addrL ) != 2 )
|
|
return( 11 ); /* Flag error in S2 record */
|
|
oheadN = 4; /* 3 address + 1 checksum */
|
|
break;
|
|
|
|
case '3': /* 32 bit address field */
|
|
if( sscanf(lbufPC, "S3%2x%8lx", &countN, &addrL ) != 2 )
|
|
return( 12 ); /* Flag error in S3 record */
|
|
oheadN = 5; /* 4 address + 1 checksum */
|
|
break;
|
|
|
|
default: /* ignore all but S1,2,3 records. */
|
|
return( 0 );
|
|
}
|
|
|
|
if( addrL > BUFSIZE ) return( 13 ); /* if address exceeds buffer size */
|
|
bufPC = bufAC + addrL; /* otherwise, point to right spot in buffer */
|
|
|
|
/* OK now see if checksum is OK, while reading data to buffer */
|
|
cksmB = 0;
|
|
countN++; /* Bump counter to read final checksum too */
|
|
for( i = 1; i <= countN; i++ )
|
|
{
|
|
sscanf( lbufPC + i*2, "%2x", &tvalN ); /* Scan a 2 hex digit byte */
|
|
cksmB += (unsigned char)tvalN;
|
|
if( ( i > oheadN ) && ( i < countN ) ) /* If scanned a data byte */
|
|
*bufPC++ = (unsigned char) tvalN; /* write it to the buffer */
|
|
}
|
|
if( cksmB += 1 ) return( 14 ); /* flag checksum error */
|
|
|
|
if( (bufPC - 1) > highestPC )
|
|
highestPC = bufPC - 1; /* track highest address loaded */
|
|
|
|
return( 0 ); /* Successful return */
|
|
}
|
|
|
|
|
|
|