/*\ * INTEL hex <-> binary file Converter. * * T.Bohning * 11851 NW 37 Place * Sunrise, FL 33323 * * Compiler: Microsoft C 5.1 * 2/20/89 * * Compuserve User ID: [71036,1066] * GEnie address: T.BOHNING * INTEL hex description: 8 bit codes are split into two nibbles, and each nibble stored as a hex ascii digit '0' through 'F'. Each line of the intel hex file is a record, with the following format: :NNAAAATTD1D2D3D4....DnCC The colon means start of record, NN is the number of data bytes in the record given as two hex digits. AAAA is the starting load address of the record. * TT is a record type, 00 for data records. D1,D2...Dn are the hex ASCII representations of the data bytes. CC is a hex ASCII checksum, chosen such that the sum of all preceding byte values in the record (not just the data bytes) modulo 256 = 0. * The end of the hex file is marked by a record with a data length of 0 and a record type of 1. * * This description is for "old" INTEL hex, which could only support * 64K loads. "Extended" INTEL hex was developed when the 8086 came * along. \*/ #include #include #include #include enum bool { FALSE, TRUE }; /*\ * function prototypes \*/ void genbin( FILE *inptr, FILE *outptr); void genhex( FILE *inptr, FILE *outptr); int getyn( char *msg ); char get_hexbyte( char *cptr ); int hexext( char *filename ); void main( int argc, char *argv[] ); char * put_hexbyte( char *cptr, char val ); void read_exit( void ); void usexit( void ); void write_exit( void ); /* file i/o buffer size (2 allocated) */ #define FILE_BUFSIZE 0x6000 void main( argc, argv) int argc; char *argv[]; { FILE *inptr, *outptr; int tohex; /* TRUE -> binary to HEX */ char *inbuf, *outbuf; /* file I/O buffers */ /*\ * Check args. \*/ if (argc != 3) { usexit(); } /*\ * Open files, check for .HEX extension, establish * conversion direction. \*/ tohex = hexext(argv[2]) ? TRUE : (hexext(argv[1]) ? FALSE : usexit()); /*\ * Open the files. \*/ if ( (inptr = fopen( argv[1], tohex ? "rb" : "rt" )) == NULL ) { printf("can't open %s for reading", argv[1]); } /*\ * Test for output file existence first. \*/ if ( (outptr = fopen( argv[2], "rb" )) != NULL ) { if ( getyn("Output file exists, overwrite (Y/N)? ") == 'N' ) { usexit(); } else { fclose( outptr ); } } if ( (outptr = fopen( argv[2], tohex ? "wt" : "wb" )) == NULL ) { printf("can't open %s for writing", argv[1]); } /*\ * Allocate and set up file I/O buffers \*/ if ( ( (inbuf = malloc( FILE_BUFSIZE)) == NULL) || ( (outbuf = malloc( FILE_BUFSIZE)) == NULL) ) { puts("Can't allocate file I/O buffers"); exit(1); } if ( setvbuf( inptr, inbuf, _IOFBF, FILE_BUFSIZE ) || setvbuf( outptr, outbuf, _IOFBF, FILE_BUFSIZE ) ) { puts("Error setting file buffers"); exit(1); } printf("Converting: %s -> %s\n", argv[1], argv[2] ); if (tohex) { genhex(inptr, outptr); } else { genbin(inptr, outptr); } } /*\ * Print msg, Get y or n from user. * Return upper case variant. \*/ int getyn( msg ) char *msg; { int c; puts( msg ); while( 1) { c = getche(); puts(""); if ( (c == 'y') || (c == 'Y') ) { return( 'Y' ); } if ( (c == 'n') || (c == 'N') ) { return( 'N' ); } } } /*\ * Get a byte from hex ascii string, return the value. \*/ char get_hexbyte( cptr ) char *cptr; { char retval; char nbl; int shift; retval = 0; for( shift = 4; shift >= 0; shift -= 4 ) { if ((*cptr >= '0') && (*cptr <= '9')) { nbl = *cptr - '0'; } else { if ((*cptr >= 'A') && (*cptr <= 'F')) { nbl = *cptr - 'A' + 10; } else { puts("Hex file contains invalid character"); exit(1); } } ++cptr; retval |= (nbl << shift); } return( retval ); } /*\ * Convert INTEL hex at infile to binary at outfile. \*/ void genbin( inptr, outptr) FILE *inptr, *outptr; { char linebuf[256]; /* input buffer */ char c; char *bufptr; int numbytes; char chksum; int i; int linenum = 1; printf("Processing hex file line number: %5d", linenum ); /*\ * process input file 1 line at a time. \*/ while( fgets( linebuf, sizeof(linebuf)-1, inptr) != NULL ) { chksum = 0; bufptr = linebuf; if ( *bufptr++ != ':' ) { printf("Intel hex format error in line %d\n", linenum); exit(1); } /*\ * Get number of data bytes and add into checksum. \*/ numbytes = get_hexbyte( bufptr ); chksum += (char)numbytes; bufptr += 2; /*\ * Add load address and record type into checksum. \*/ for( i = 0; i < 3; ++i ) { chksum += get_hexbyte( bufptr ); bufptr += 2; } /*\ * Write the binary data. \*/ for( i = 0; i < numbytes; ++i ) { c = get_hexbyte(bufptr); bufptr += 2; putc( c, outptr); chksum += c; } if ( ferror( outptr ) ) { write_exit(); } /*\ * Sum in checksum byte and check the sum. \*/ chksum += get_hexbyte(bufptr); if (chksum != 0) { printf("Checksum error in line %d\n", linenum); exit(1); } if( numbytes == 0 ) { puts(""); exit(0); /* end of hex file */ } ++linenum; if ( (linenum & 0x3F) == 0 ) { printf("\b\b\b\b\b%5d", linenum); } } if (ferror(inptr)) { read_exit(); } puts("\nWarning: Terminator record not found, hex file probably truncated."); exit(1); } /*\ * Convert infile to INTEL hex at outfile. \*/ void genhex( inptr, outptr) FILE *inptr, *outptr; { #define DATA_BYTES 0x10 /* data bytes per record */ /* hex file line buffer, one space for a NULL, * one space for \n * : len addr 00 cks \n null */ char hexline[ DATA_BYTES*2 + 1 + 2 + 4 + 2 + 2 + 1 + 1 ]; char data_buf[ DATA_BYTES ]; unsigned int load_addr = 0; int numbytes, i; unsigned char chksum; unsigned char *bufptr; unsigned int linenum = 1; hexline[0] = ':'; /* colon always starts a record */ hexline[7] = '0'; /* type for data records is */ hexline[8] = '0'; /* ... 00 */ printf("Processing hex file line number: %5d", linenum ); /*\ * Build a line \*/ while( (numbytes = fread( data_buf, sizeof(char), DATA_BYTES, inptr)) != 0 ) { /*\ * Write out all the bytes as hex, * updating chechksum as we go. \*/ bufptr = &hexline[1]; /* skip the colon */ chksum = (char)numbytes; bufptr = put_hexbyte( bufptr, (char)numbytes ); chksum += (char)(load_addr >> 8); chksum += (char)load_addr; bufptr = put_hexbyte( bufptr, (char)(load_addr >> 8) ); bufptr = put_hexbyte( bufptr, (char)load_addr ); bufptr += 2; /* skip over data record type */ /*\ * Write out actual data bytes. \*/ for(i = 0; i < numbytes; i++) { chksum += data_buf[i]; bufptr = put_hexbyte( bufptr, data_buf[i] ); } chksum = ~chksum+1; bufptr = put_hexbyte( bufptr, chksum ); *bufptr++ = '\n'; *bufptr = NULL; /*\ * write this line of the hex file \*/ fputs( hexline, outptr ); if ( ferror(outptr) ) { write_exit(); } load_addr += numbytes; ++linenum; if ( (linenum & 0x3F) == 0 ) { printf("\b\b\b\b\b%5d", linenum); } } puts(""); if ( ferror(inptr) ) { read_exit(); } fputs(":00000001FF", outptr); /* Standard termination record */ } /*\ * Try to find .HEX extension on a filename. \*/ hexext( cptr ) char *cptr; { return( !strcmpi( cptr + strlen(cptr) - 4, ".hex") ); } /*\ * Put a byte as hex ascii, return pointer to next location. \*/ char * put_hexbyte(cptr, val) char *cptr; char val; { static char hextbl[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; *cptr++ = hextbl[ ((val >> 4) & 0x0F) ]; *cptr++ = hextbl[ val & 0x0F ]; return(cptr); } /*\ * read error on input file \*/ void read_exit() { puts("Error on input file read"); exit(1); } /*\ * Show usage and die. \*/ void usexit() { puts("\nINTEL hex <-> binary file converter"); puts("\nUsage: HEXBIN infile outfile" ); puts("\nEither infile or outfile must have .HEX extension"); puts("\nIf infile has .HEX extension, HEX to binary conversion is performed"); puts("If outfile has .HEX extension, binary to HEX conversion is performed"); exit(1); } /*\ * write error on output file \*/ void write_exit() { puts("Error on output file write"); exit(1); } /************************ EOF *************************/