S19 Records & 8192 baud on PC

William Boulton william at premier.apana.org.au
Wed Jul 17 13:49:14 GMT 1996


Hi All,

I have a couple of questions I hope someone can answer.

                              ---- HELP ----

Can anyone tell me where/if I can get data sheets on National
Semiconductor devices via FTP or e-mail?

Is it possible to get SAE papers via the internet, preferably ftp or
e-mail?

            ---- Motorolla Intel Hex to Motorola S record ----

Some time ago someone was asking about Motorola S-record formats. I found
the appended file in Magdeburg, Germany while ratting for some other
stuff. Will you look where it came from originally and the date! I have
included the full file here for your use and to give credit to its'
originator.

I've found a description of the Intel Hex format as well and included
that fyi. With this stuff, a program to convert one to the other should
only be a short time away.

                 ---- Non-standard Baud Rates on PCs ----

BTW. There has been talk about setting the baud rate on PC serial ports to
match the 8192 of the GM ECU data stream. No drama folks! As long as the
desired baud rate will divide evenly into 115200 you have your baud rate
generator pre-scaler value and 115200 / 8192 = 14.0625 or close enough.
The 0.4% error is far less than 1/4 bit time over 10 bits so no drama. The
clock is probably not that precise in any case. Mind you some libraries
will not allow entry of values not part of the general form so yoy may
have to rewrite the validation code slightly and re-compile. Anyway, be
assured that any limitation is in the software. The hardware can do it, no
sweat.

Please & Thank You.

Bill Boulton

PS: I have not verified that the code actually works or even compiles so
    good luck.

--------------------cut here--------------------

INTEL HEX OBJECT FORMAT.

This is the default format. This format is line oriented and uses only
printable ASCII characters except for the carriage return/line feed at the
end of each line. Each line in the file assumes the following format:

	:NNAAAARRHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCCTT

Where:

All fields marked 'hex' consist of two or four ASCII hexadecimal digits
(0-9, A-F). A maximum of 24 data bytes will be represented on each line
(override the 24 byte default with the '-o' command line option).

	:      = Record Start Character
	NN     = Byte Count (hex)
	AAAA   = Address of first byte (hex)
	RR     = Record Type (hex, 00 except for last record which is 01)
	HH     = Data Bytes (hex)
	CC     = Check Sum (hex)
	TT     = Line Terminator (carriage return, line feed)

The last line of the file will be a record conforming to the above format
with a byte count of zero:

         :00000001FF

The checksum is defined as:

	sum = byte_count + addr_hi + addr_lo + record_type +
		  (sum of data bytes)

	checksum =  ((-sum) & ffh)

--------------------cut here--------------------

From: 8918927y at levels.unisa.edu.au (GONDWANA)
Subject: Here is Motorola S-Record information!
Date: 6 Sep 94 20:33:49 +0930

Gday fellow M68k'ers .. seeing that some people have been asking about the
Motorola Srecord information I thought I'd post this.. it's the appendix
from the 332bug reference manual (copyright motorola) it explains all the
srecord terms well....

_ALSO_ i have included the ANSI C source code for a program that I wrote
that analyses S19/S28/S32 files & displays the header record, and any
blocks within the file & their sizes etc.. (I got anoyed with constantly
decifering the srecords to try & finf out what my assembler was stuffing
into the srecords)... anyway, it is 100% ansi compatible so, say on a UNIX
box, with ansi C you should be able to build it.

I have also built it on an IBM PC under turbo C, and an Atari ST under
Lattice C. Email if you want the binaries..

It is public domain, so copy it, but dont charge for it!!.. please let me
know of any bugs..!!!!!!

Thanks !

Kym Newbery
8918927y at lux.levels.unisa.edu.au
Final Year Electronic Engineering..

_____________cut here for SRECORD information---------------------------

S-RECORD OUTPUT FORMAT

The S-record format for output modules was devised for the purpose of
encoding programs or data files in a printable format for transportation
between computer systems. The transportation process can thus be visually
monitored and the S-records can be more easily edited.

S RECORD CONTENT

When viewed by the user, S-records are essentially character strings made
of several fields which identify the record type, record length, memory
address, code/data and checksum. Each byte of binary data is encoded as a
2-character hexadecimal number; the first character representing the
high-order 4 bits, and the second the low-order 4 bits of the byte.

The five fields which comprise an S-record are shown below:

 type | record length | address | code/data | checksum

Where the fields are composed as follows:

        Printable
Field   Characters      Contents

type    2       S-records type -- S0, S1, etc.

record length   2       The count of the character pairs in the
                        record, excluding type and record
                        length.

address 4, 6, or 8      The 2-, 3-, or 4-byte address at which
                        the data field is to be loaded into
                        memory.

code/data       0-n     From 0 to n bytes of executable code,
                        memory-loadable data, or descriptive
                        information.  For compatibility with
                        teletypewriters, some programs may
                        limit the number of bytes to as few as 28
                        (56 printable characters in the S-record).

checksum        2       The least significant byte of the one's
                        complement of the sum of the values
                        represented by the pairs of characters
                        making up the records length, address,
                        and the code/data fields.


Each record may be terminated with a CR/LF/NULL. Additionally, an S-record
may have an initial field to accommodate other data such as line numbers
generated by some time-sharing systems. An S-record file is a normal ASCII
text file in the operating system in which it resides.

Accuracy of transmission is ensured by the record length (byte count) and
checksum fields.

S-RECORD TYPES

Eight types of S-records have been defined to accommodate the several
needs of the encoding, transportation and decoding functions. The various
Motorola upload, download and other records transportation control
programs, as well as cross assemblers, linkers and other file-creating or
debugging programs, utilize only those S-records which serve the purpose
of the program. For specific information on which S-records are supported
by a particular program, the user's manual for the program must be
consulted. 332Bug supports S0, S1, S2, S3, S7, S8, and S9 records.

An S-record format module may contain S-records of the following  types:

S0      The header record for each block of S-records, The code/data.field
        may contain any descriptive information identifying the following
        block of S-records. The address field is normally zeros.

S1      A record containing code/data and the 2-byte address at which the
        code/data is to reside.

S2      A record containing code/data and the 3-byte address at which the
        code/data is to reside.

S3      A record containing code/data and the 4-byte address at which the
        code/data is to reside.

S5      A record containing the number of S1, S2, and S3 records
        trans-mitted in a particular block. This count appears in the
        address field. There is no code/data field.

S7      A termination record for a block of S3 records, The address field
        may optionally contain the 4-byte address of the instruction to
        which control is passed. There is no code/data field.

S8      A termination record for a block of S2 records. The address field
        may optionally contain the 3-byte address of the instruction to
        which control is passed. There is no code/data field.

S9      A termination record for a block of S1 records. The address field
        may optionally contain the 2-byte address of the instruction to
        which control is passed. If not specified, the first entry point
        specification encountered in the object module input will be used.
        There is no code/data field.

Only one termination record is used for each block of S-records. S7 and S8
records are usually used only when control is to be passed to a 3 or 4
byte address. Normally, only one header record is used, although it is
possible for multiple header records to occur.

S-RECORDS CREATION

S-record format files may be produced by dump utilities, debuggers,
linkage editors, cross assemblers or cross linkers. Several programs are
available for downloading a file in S-record format from a host system to
a microprocessor -based system.


EXAMPLE

Shown below is a typical S-record format module, as printed or displayed:

        S00600004844521B
        S1130000285F245F2212226A000424290008237C2A
        S11300100002000800082629001853812341001813
        S113002041E900084E42234300182342000824A952
        S113003000144ED492
        S9030000FC


The module consists of one S0 record, four S1 records, and an S9 record.

The S0 record is comprised of the following character pairs:

S0      S-record type S0, indicating that it is a header record.

06      Hexadecimal 06 (decimal 6), indicating that six character pairs (or
ASCII
        bytes) follow.

00      Four-character, 2-byte, address field; zeros in this example.
00

48
44      ASCII   H, D and R - "HDR".
52

1B      The checksum.

The first S1 record is explained as follows:

S1      S-record type S1, indicating that it is a code/data record to be
        loaded/verified at a 2-byte address.

13      Hexadecimal 13 (decimal 19), indicating that 19 character pairs,
        representing 19 bytes of binary data, follow.

00      Four-character, 2-byte, address field; hexadecimal address
00      0000, where the data which follows is to be loaded.

The next 16 character pairs of the first S1 record are the ASCII bytes of
the actual program code/data.  In this assembly language example, the
hexadecimal opcodes of the program are written in sequence in the code/data
fields of the S1 records:

OPCODE  INSTRUCTION

285F    MOVE.L  (A7)+,A4
245F    MOVE.L  (A7)+,A2
2212    MOVE.L  (A2),D1
226A0004        MOVE.L  4(A2),A1
24290008        MOVE.L  FUNCTION(A1),D2
237C    MOVE.L  #FORCEFUNC,FUNCTION(A1)

(The balance of this code is continued in the code/data fields of the
remaining S1 records and stored in memory.)


        2A      The checksum of the first S1 record.


The second and third S1 records also each contain $13 (19) character pairs
and are ended with checksums 13 and 52 respectively.  The fourth S1 record
contains 07 character pairs and has a checksum of 92.

The S9 record is explained as follows:

S9      S-record type S9, indicating that it is a termination record.

03      Hexadecimal 03, indicates that 3 character pairs (3 bytes) follow.

00
00      The address field, zeros.

FC      The checksum of the S9 record.


Each printable character in an S-record is encoded in a hexadecimal (ASCII
in this example) representation of the binary bits which are actually
transmitted.


============CUT HERE============SRECORD ANALYSER=============


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

#define FALSE 0
#define TRUE 1

#define DATA19 0
#define DATA28 1
#define DATA37 3
#define HEADER 4
#define TERMINATOR 5
#define NONE 6


/*
 * unsigned int ahdtoi(char digit)
 *
 * converts a hexadecimal char to an integer
 *
 * entry	: digit = character to convert
 *			: 0..15 = result
 *			: -1	= char is not a digit
 */

unsigned int ahdtoi(unsigned char digit) {
	/* check digit */
	if (!isxdigit(digit))
		return(-1);

	switch (toupper(digit)) {
		case 'A' : return(0xA);
		case 'B' : return(0xB);
		case 'C' : return(0xC);
		case 'D' : return(0xD);
		case 'E' : return(0xE);
		case 'F' : return(0xF);
		default  : return(digit - 0x30);
	}
}

/*
 * int issrec(char *str)
 *
 * attempts to identify the type of Srecord string passed
 *
 *  entry   : str =  pointer to null terminated string
 *  returns : 0,1,2,3,5,7,8,9 for S0..S9 except S6 & S4
 *	        : -1 = invalid header or header not found
 *		    : -2 = invalid header number
 */

int issrec(char *str) {

	/* Check first character for S */
	if ((isupper(str[0]) && (str[0] == 'S'))
	    || (islower(str[0]) && (str[0] == 's'))) {
                            		/* check for valid header number */
		switch (str[1]) {
			case '0' : return 0;	/* header record */
			case '1' : return 1;	/* data record, 2byte addr */
			case '2' : return 2;	/*  "	  "   , 3byte addr */
			case '3' : return 3;	/*  "	  "   , 4byte addr */
			case '5' : return 5;	/* number of S1,S2,S3 blocks */
			case '7' : return 7;	/* S3 terminator */
			case '8' : return 8;	/* S2 terminator */
			case '9' : return 9;	/* S1 terminator */
			default  : return -2;	/* all others are invalid */
		}
	}
	return(-1);
}


/*
 * int validrec(char *str)
 *
 * Tests for a valid srecord. tests checksum & for nondigit characters
 * doesn't rely on any other srecord routines.
 *
 *  entry   : str   =  pointer to null terminated string
 *  returns : -1	=  srecord contains invalid characters
 *			: -2	=  srecord checksum is invalid
 *		    : -3    =  srecord record length is invalid
 *	        : 0     =  srecord is valid
 */

int validrec(char *str) {
	int cn = 1, rlen=0;
	int mchksum=0, rchksum=0;

	/* first check if there are any non-digit characters except S */
	while (str[cn]!=0)
		if (!isxdigit(str[cn++]))
			return(-1);

	/* test number of data bytes */
	rlen = ahdtoi(str[2])* 0x10 + ahdtoi(str[3]);
	if (((strlen(str)-4)/2) != rlen)
	    return(-3);

	/* get checksum from string */
	rchksum = ahdtoi(str[rlen*2+2])*0x10 + ahdtoi(str[rlen*2+3]);
	/* string chksum */

	/* now calculate my own checksum */
	for (cn=2; cn <= rlen*2; )
		mchksum += ahdtoi(str[cn++])*0x10 + ahdtoi(str[cn++]);
	mchksum = ~mchksum & 0xFF;
	if (mchksum != rchksum)
	    return(-2); /* return -2 in not equal */

	/* return OK if we didn't fail any of these tests */
	return(0);
}

/*
 * void hdr2str(char *sstr, char *pstr)
 *
 * converts header record (S0) string into a plain string
 *
 * entry	: sstr = pointer to S0 string record
 * exit 	: pstr = pointer to string long enough to hold string
 *				 (caller must allocate enough space for string)
 */

void hdr2str(char *sstr, char *pstr) {
	int rlen,cn,pn=0;

	rlen = ahdtoi(sstr[2])* 0x10 + ahdtoi(sstr[3]);
	for (cn=8; cn <= rlen*2; )
		pstr[pn++] = ahdtoi(sstr[cn++])*0x10 + ahdtoi(sstr[cn++]);
	pstr[pn]=0;
}

/*
 * long getaddr(char *str)
 *
 * returns the address of the srecord in str. assumes record is valid.
 *
 * entry	: str = pointer to srecord string
 * exit 	: address of data, word or long.
 *
 */
unsigned long getaddr(char *str) {
	unsigned long addr=0;

	switch (issrec(str)) {
		case 0 : ;
		case 1 : ;
		case 5 : ;
		case 9 : { addr = ahdtoi(str[4])*0x1000 + ahdtoi(str[5])*0x100
						+ ahdtoi(str[6])*0x10 + ahdtoi(str[7]);
						return(addr); }
		case 2 : ;
		case 8 : { addr = ahdtoi(str[4])*0x100000 + ahdtoi(str[5])*0x10000
						+ ahdtoi(str[6])*0x1000 + ahdtoi(str[7])*0x100
						+ ahdtoi(str[8])*0x10 + ahdtoi(str[9]);
						return(addr); }
		case 3 : ;
		case 7 : { addr = ahdtoi(str[4])*0x10000000 + ahdtoi(str[5])*0x1000000
						+ ahdtoi(str[6])*0x100000 + ahdtoi(str[7])*0x10000
						+ ahdtoi(str[8])*0x1000 + ahdtoi(str[9])*0x100
						+ ahdtoi(str[10])*0x10 + ahdtoi(str[11]);
						return(addr); }
		default : return(-1);
	}
}

/*
 * int datasize(char *str)
 *
 * returns the number of data bytes in the srecord. assumes record is valid.
 *
 * entry	: str = pointer to srecord string
 * exit 	: number of bytes of data in the data field.
 *
 */

unsigned int datasize(char *str) {
	unsigned int size=0;

	switch (issrec(str)) {
		case 0	:
		case 1	:
		case 5	:
		case 7	:
		case 8	:
		case 9	: {	size = ahdtoi(str[2])*0x10 + ahdtoi(str[3]);
								return(size-3);}
		case 2	: {	size = ahdtoi(str[2])*0x10 + ahdtoi(str[3]);
						return(size-4);}
		case 3	: {	size = ahdtoi(str[2])*0x10 + ahdtoi(str[3]);
						return(size-5);}
		default : return(-1);
	}
}

main(int argc, char *argv[]) {

	char inpstr[255];
	char hdrstr[30];
	int result,size,line=0,lastrec=0,donebanner=0;
	long addr,tsize=0,naddr=0,blksize=0,blknum=1;
	FILE *in;

	if ((in = fopen(argv[1], "r")) == NULL) {
	   fprintf(stderr, "Cannot open input file.\n");
	   printf("\nUSAGE :  SRECINF <filename>.<ext>\n");
	   printf("  where <filename>.<ext> is the name of the \n");
	   printf("  Motorola format S19,S28 or S37 ascii text file.\n\n");
	   printf("  (C)1994 KBN. V1.0 Public Domain\n");
	   return 1;
	}

	while (fscanf(in,"%s",&inpstr) != EOF) {
		line++;
		if (validrec(inpstr) == 0) {
		switch (issrec(inpstr))
		{
		case 0 : {
					hdr2str(inpstr, hdrstr);
					printf("HEADER string = `%s'\n",hdrstr);
					lastrec=HEADER;
					break;
				 }
		case 1 : {
					if (donebanner==0) {

        printf("Record\tType\tStart\tEnd\tSize\tBlock\n");

    printf("-------------------------------------------------------------\n");
					  donebanner=1;
					}
					addr = getaddr(inpstr);
					size = datasize(inpstr);
					if (blksize == 0) {
						blksize+=size;
						naddr=addr+size;
                        printf("DATA\tS19\t$%04lX",addr);
						lastrec=DATA19;
					}
					else
					    if ((blksize!=0) && (addr==naddr)) {
    						blksize+=size;
	    					naddr=addr+size;
		    			}
			    		else {
                            printf("\t$%04lX\t%lu",naddr-1,blksize);
					    	printf("\t%d\n",blknum);
						    blknum+=1;
    						naddr=addr+size;
	    					blksize=size;
                            printf("DATA\tS19\t$%04lX",addr);
			    			lastrec=DATA19;
				    	}
					tsize += size;
					break;
				 }
		case 2 : {
					if (donebanner==0) {

        printf("Record\tType\tStart\tEnd\tSize\tBlock\n");

    printf("-------------------------------------------------------------\n");
					  donebanner=1;
					}
					addr = getaddr(inpstr);
					size = datasize(inpstr);
					if (blksize == 0) {
						blksize+=size;
						naddr=addr+size;
                        printf("DATA\tS28\t$%06lX",addr);
						lastrec=DATA28;
					}
					else
					    if ((blksize!=0) && (addr==naddr)) {
    						blksize+=size;
	    					naddr=addr+size;
					    }
    					else {
                            printf("\t$%06lX\t%lu",naddr-1,blksize);
   						    printf("\t%d\n",blknum);
   						    blknum+=1;
   						    naddr=addr+size;
   						    blksize=size;
                            printf("DATA\tS28\t$%06lX",addr);
   						    lastrec=DATA28;
   					    }
					tsize += size;
					break;
				 }
		case 3 : {
					if (donebanner==0) {

        printf("Record\tType\tStart\t\tEnd\t\tSize\tBlock\n");

    printf("-------------------------------------------------------------\n");
					  donebanner=1;
					}
					addr = getaddr(inpstr);
					size = datasize(inpstr);
					if (blksize == 0) {
						blksize+=size;
						naddr=addr+size;
                        printf("DATA\tS37\t$%08lX",addr);
						lastrec=DATA37;
					}
					else
					if ((blksize!=0) && (addr==naddr)){
						blksize+=size;
						naddr=addr+size;
					}
					else {
                        printf("\t$%08lX\t%lu",naddr-1,blksize);
						printf("\t%d\n",blknum);
						blknum+=1;
						naddr=addr+size;
						blksize=size;
                        printf("DATA\tS37\t$%08lX",addr);
						lastrec=DATA37;
					}
					tsize += size;
					break;
				 }
		case 7 : {
					if (lastrec==DATA19){
					    printf("\t$%04lX\t%lu",naddr-1,blksize);}
					if (lastrec==DATA28){
					    printf("\t$%06lX\t%lu",naddr-1,blksize);}
					if (lastrec==DATA37){
					    printf("\t$%08lX\t%lu",naddr-1,blksize);}
					printf("\t%d\n",blknum);
					addr = getaddr(inpstr);
					addr = getaddr(inpstr);
					printf("TERM\tS37\nExecution address = $%08lX\n",addr,addr);
					lastrec=TERMINATOR;
					break;
				 }
		case 8 : {
					if (lastrec==DATA19){
					    printf("\t$%04lX\t%lu",naddr-1,blksize);
					}
					if (lastrec==DATA28){
					    printf("\t$%06lX\t%lu",naddr-1,blksize);
					}
					if (lastrec==DATA37){
					    printf("\t$%08lX\t%lu",naddr-1,blksize);
					}
					printf("\t%d\n",blknum);
					addr = getaddr(inpstr);
					addr = getaddr(inpstr);
					printf("TERM\tS28\nExecution address = $%06lX\n",addr,addr);
					lastrec=TERMINATOR;
					break;
				 }
		case 9 : {
					if (lastrec==DATA19){
					    printf("\t$%04lX\t%lu",naddr-1,blksize);
					}
					if (lastrec==DATA28){
					    printf("\t$%06lX\t%lu",naddr-1,blksize);
					}
					if (lastrec==DATA37){
					    printf("\t$%08lX\t%lu",naddr-1,blksize);
					}
					printf("\t%d\n",blknum);
					addr = getaddr(inpstr);
					printf("TERM\tS19\nExecution address = $%04lX\n",addr,addr);
					lastrec=TERMINATOR;
					break;
				 }
		}
		}
		else
		{
		  printf("\nError on line %d. ",line);
		  switch (validrec(inpstr))
		  {
			case -1 : {printf("SRecord contains invalid characters.\n"); break; }
			case -2 : {printf("SRecord checksum is invalid.\n");
                        break;}
			case -3 : {printf("SRecord length is invalid.\n");
                        break;}
		  }
		  exit(1);
		}
	}

	if ((lastrec==DATA19) || (lastrec==DATA28) || (lastrec==DATA37)) {
		if (lastrec==DATA19){
		    printf("\t$%04lX\t%lu",naddr-1,blksize);}
		if (lastrec==DATA28){
		    printf("\t$%06lX\t%lu",naddr-1,blksize);}
		if (lastrec==DATA37){
		    printf("\t$%08lX\t%lu",naddr-1,blksize);}
		printf("\t%d\n",blknum);
		printf("ERROR: terminator record not found.\n");
	}
	printf("total data size = %lu bytes\n",tsize);
}

--
     __0    	_=0   | Kym Newbery | 8918927y at lux.levels.unisa.edu.au
    -\<,       -\<,   | "Don't let anybody tell you about the good 'ol days...
.. 0/ 0 .... 0/ 0 ... | there weren't any. the best time is now!" R.Frick94




More information about the Diy_efi mailing list