/***** P A C K H E X . C ************************************************ * * Packhex is a hex-file compaction utility. It attempts to concatenate * hex records to produce more size-efficient packaging. * * Limitations: Input files must be correctly formatted. This utility * is not robust enough to detect hex-record formatting * errors. * * Published: May 1993 Embedded Systems Programming magazine * "Creating Faster Hex Files" * * URL: ESP magazine: http://www.embedded.com * Source Code: ftp://ftp.mfi.com/pub/espmag/1993/pakhex.zip * * Author: Mark Gringrich * * Compiler: Microsoft C 6.0 * cl /F 1000 packhex.c * **************************************************************************/ /* #define SMALLER_RECORDS */ #ifdef SMALLER_RECORDS #define MAX_LEN_S1_RECS 128 #define MAX_LEN_S2_RECS 128 #define MAX_LEN_S3_RECS 128 #else #define MAX_LEN_S1_RECS 252 #define MAX_LEN_S2_RECS 251 #define MAX_LEN_S3_RECS 250 #endif /*--------------------------------- includes ---------------------------------*/ #include #include #include #include "config.h" #ifndef VMS #ifndef HAVE_STRERROR extern int sys_nerr; extern char *sys_errlist[]; #define strerror( _err ) \ ((_err) < sys_nerr) ? sys_errlist [(_err)] : "unknown error" #else /* HAVE_STRERROR */ char *strerror (); #endif #else /* VMS */ char *strerror (int,...); #endif #if defined(__unix__) && !defined(EXIT_FAILURE) #define EXIT_FAILURE -1 #define EXIT_SUCCESS 0 #endif /*--------------------------------- defines ----------------------------------*/ #define YES 1 #define MAX_LINE_SIZE 600 #define EOS '\0' /*---------------------------------- macros ----------------------------------*/ /* Convert ASCII hexadecimal digit to value. */ #define HEX_DIGIT( C ) ( ( ( ( C ) > '9' ) ? ( C ) + 25 : ( C ) ) & 0xF ) /*--------------------------------- typedefs ---------------------------------*/ typedef unsigned char Boolean; typedef unsigned char Uchar; typedef unsigned int Uint; typedef unsigned long Ulong; typedef struct /* Functions and constant returning Hex-record vital stats. */ { Boolean ( *is_data_record )( char * ); Ulong ( *get_address )( char * ); Uint ( *get_data_count )( char * ); const Uint max_data_count; char *( *get_data_start )( char * ); void ( *put_data_record )( Uint, Ulong, char * ); } Rec_vitals; /*--------------------------- function prototypes ----------------------------*/ Rec_vitals * identify_first_data_record( char *, int ); Ulong get_ndigit_hex( char *, int ); /*----------------------------- Intel Hex format -----------------------------*/ /* * Intel Hex data-record layout * * :aabbbbccd...dee * * : - header character * aa - record data byte count, a 2-digit hex value * bbbb - record address, a 4-digit hex value * cc - record type, a 2-digit hex value: * "00" is a data record * "01" is an end-of-data record * "02" is an extended-address record * "03" is a start record * d...d - data (always an even number of chars) * ee - record checksum, a 2-digit hex value * checksum = 2's complement * [ (sum of bytes: aabbbbccd...d) modulo 256 ] */ Boolean is_intel_data_rec( char * rec_str ) { return( ( rec_str[ 0 ] == ':' ) && ( rec_str[ 8 ] == '0' ) ); } Uint get_intel_rec_data_count( char * rec_str ) { return( ( Uint ) get_ndigit_hex( rec_str + 1, 2 ) ); } Ulong get_intel_rec_address( char * rec_str ) { return( get_ndigit_hex( rec_str + 3, 4 ) ); } char * get_intel_rec_data_start( char * rec_str ) { return( rec_str + 9 ); } void put_intel_data_rec( Uint count, Ulong address, char * data_str ) { char *ptr; Uint sum = count + ( address >> 8 & 0xff ) + ( address & 0xff ); for ( ptr = data_str ; *ptr != EOS ; ptr += 2 ) sum += ( Uint ) get_ndigit_hex( ptr, 2 ); printf( ":%02X%04lX00%s%02X\n", count, address, data_str, (~sum + 1) & 0xff ); } Rec_vitals intel_hex = { is_intel_data_rec, get_intel_rec_address, get_intel_rec_data_count, 255, /* Maximum data bytes in a record. */ get_intel_rec_data_start, put_intel_data_rec }; /*------------------------- Motorola S1-record format ------------------------*/ /* * Motorola S-record data-record layout * * Sabbc...cd...dee * * S - header character * a - record type, a 1-digit value: * "0" is a header record * "1" is a 2-byte-address data record * "2" is a 3-byte-address data record * "3" is a 4-byte-address data record * "7" is a 4-byte-address end-of-data record * "8" is a 3-byte-address end-of-data record * "9" is a 2-byte-address end-of-data record * bb - record length in bytes, a 2-digit hex value * (record length doesn't count the header/type * chars and checksum byte) * c...c - record address, a 4-, 6-, or 8-digit value, * depending on record type * d...d - data (always an even number of chars) * ee - record checksum, a 2-digit hex value * checksum = 1's complement * [ (sum of all bytes: bbc..cd...d) modulo 256 ] */ #define S1_COUNT_OFFSET 3 Boolean is_moto_s1_data_rec( char * rec_str ) { return ( ( rec_str[ 0 ] == 'S' ) && ( rec_str[ 1 ] == '1' ) ); } Uint get_moto_s1_rec_data_count( char * rec_str ) { return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S1_COUNT_OFFSET ); } Ulong get_moto_s1_rec_address( char * rec_str ) { return( get_ndigit_hex( rec_str + 4, 4 ) ); } char * get_moto_s1_rec_data_start( char * rec_str ) { return( rec_str + 8 ); } void put_moto_s1_data_rec( Uint count, Ulong address, char * data_str ) { char *ptr; Uint sum = S1_COUNT_OFFSET + count + ( address >> 8 & 0xff ) + ( address & 0xff ); for ( ptr = data_str ; *ptr != EOS ; ptr += 2 ) sum += ( Uint ) get_ndigit_hex( ptr, 2 ); printf( "S1%02X%04lX%s%02X\n", count + S1_COUNT_OFFSET, address, data_str, ~sum & 0xff ); } Rec_vitals motorola_s1_rec = { is_moto_s1_data_rec, get_moto_s1_rec_address, get_moto_s1_rec_data_count, MAX_LEN_S1_RECS, /* Maximum data bytes in a record. */ get_moto_s1_rec_data_start, put_moto_s1_data_rec }; /*------------------------- Motorola S2-record format ------------------------*/ #define S2_COUNT_OFFSET 4 Boolean is_moto_s2_data_rec( char * rec_str ) { return ( ( rec_str[ 0 ] == 'S' ) && ( rec_str[ 1 ] == '2' ) ); } Uint get_moto_s2_rec_data_count( char * rec_str ) { return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S2_COUNT_OFFSET ); } Ulong get_moto_s2_rec_address( char * rec_str ) { return( get_ndigit_hex( rec_str + 4, 6 ) ); } char * get_moto_s2_rec_data_start( char * rec_str ) { return( rec_str + 10 ); } void put_moto_s2_data_rec( Uint count, Ulong address, char * data_str ) { char *ptr; Uint sum = S2_COUNT_OFFSET + count + ( address >> 16 & 0xff ) + ( address >> 8 & 0xff ) + ( address & 0xff ); for ( ptr = data_str ; *ptr != EOS ; ptr += 2 ) sum += ( Uint ) get_ndigit_hex( ptr, 2 ); printf( "S2%02X%06lX%s%02X\n", count + S2_COUNT_OFFSET, address, data_str, ~sum & 0xff ); } Rec_vitals motorola_s2_rec = { is_moto_s2_data_rec, get_moto_s2_rec_address, get_moto_s2_rec_data_count, MAX_LEN_S2_RECS, /* Maximum data bytes in a record. */ get_moto_s2_rec_data_start, put_moto_s2_data_rec }; /*------------------------- Motorola S3-record format ------------------------*/ #define S3_COUNT_OFFSET 5 Boolean is_moto_s3_data_rec( char * rec_str ) { return ( ( rec_str[ 0 ] == 'S' ) && ( rec_str[ 1 ] == '3' ) ); } Uint get_moto_s3_rec_data_count( char * rec_str ) { return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S3_COUNT_OFFSET ); } Ulong get_moto_s3_rec_address( char * rec_str ) { return( get_ndigit_hex( rec_str + 4, 8 ) ); } char * get_moto_s3_rec_data_start( char * rec_str ) { return( rec_str + 12 ); } void put_moto_s3_data_rec( Uint count, Ulong address, char * data_str ) { char *ptr; Uint sum = S3_COUNT_OFFSET + count + ( address >> 24 & 0xff ) + ( address >> 16 & 0xff ) + ( address >> 8 & 0xff ) + ( address & 0xff ); for ( ptr = data_str ; *ptr != EOS ; ptr += 2 ) sum += ( Uint ) get_ndigit_hex( ptr, 2 ); printf( "S3%02X%08lX%s%02X\n", count + S3_COUNT_OFFSET, address, data_str, ~sum & 0xff ); } Rec_vitals motorola_s3_rec = { is_moto_s3_data_rec, get_moto_s3_rec_address, get_moto_s3_rec_data_count, MAX_LEN_S3_RECS, /* Maximum data bytes in a record. */ get_moto_s3_rec_data_start, put_moto_s3_data_rec }; /*-------------------- Put your favorite hex format here ---------------------*/ /* * * * * The following is a template for an additional hex format: * * * * * * Boolean is_X_data_rec( char * rec_str ) {} * * Uint get_X_rec_data_count( char * rec_str ) {} * * Ulong get_X_rec_address( char * rec_str ) {} * * char * get_X_rec_data_start( char * rec_str ) {} * * void put_X_data_rec( Uint count, Ulong address, char * data_str ) {} * * Rec_vitals X_rec = * { * is_X_data_rec, * get_X_rec_address, * get_X_rec_data_count, * MAXIMUM DATA BYTES IN A RECORD, * get_X_rec_data_start, * put_X_data_rec * }; * */ /*----------------------------------------------------------------------------*/ /* * Put address of additional Rec_vitals structures * in this array, before the NULL entry. */ Rec_vitals *formats[] = { &intel_hex, &motorola_s1_rec, &motorola_s2_rec, &motorola_s3_rec, ( Rec_vitals * ) NULL }; /**** main ***************************************************************** * * * Expects: Nothing (no command-line parameters). * * Returns: Exit status (EXIT_SUCCESS or EXIT_FAILURE). * * Reads hex records on the standard input and attempts to * splice adjacent data fields together. Results appear on * the standard output. * *******************************************************************************/ int main( int argc, char **argv ) { char inbuff[ MAX_LINE_SIZE ], outbuff[ MAX_LINE_SIZE ]; char *in_dptr, *out_dptr; int d_total, d_count, d_excess, n; int length; Ulong in_rec_addr, out_rec_addr = 0; Rec_vitals *rptr; /* Sift through file until first hex record is identified. */ rptr = identify_first_data_record( inbuff, MAX_LINE_SIZE ); if ( rptr == NULL ) { fputs( "No hex records found.\n", stderr ); exit( EXIT_FAILURE ); } /* Attempt data-record splicing until end-of-file is reached. */ d_total = 0; for (;;) { if ( rptr->is_data_record( inbuff ) == YES ) { /* Input record is a data record. */ d_count = rptr->get_data_count( inbuff ); in_rec_addr = rptr->get_address( inbuff ); in_dptr = rptr->get_data_start( inbuff ); if ( d_total == 0 || in_rec_addr != out_rec_addr + d_total ) { /* Begin a new output record. */ if ( d_total != 0 ) rptr->put_data_record( d_total, out_rec_addr, outbuff ); out_dptr = outbuff; n = d_total = d_count; out_rec_addr = in_rec_addr; } else if ( ( d_excess = d_total + d_count - rptr->max_data_count ) > 0 ) { /* Output a maximum-length record, then start a new record. */ strncat( outbuff, in_dptr, 2 * ( d_count - d_excess ) ); rptr->put_data_record( rptr->max_data_count, out_rec_addr, outbuff ); in_dptr += 2 * ( d_count - d_excess ); out_dptr = outbuff; n = d_total = d_excess; out_rec_addr += rptr->max_data_count; } else { /* Append input record's data field with accumulated data. */ out_dptr = outbuff + ( 2 * d_total ); d_total += n = d_count; } strncpy( out_dptr, in_dptr, 2 * n ); out_dptr[ 2 * n ] = EOS; } else { /* Not a data record; * flush accumulated data then echo non-data record. */ if ( d_total != 0 ) { rptr->put_data_record( d_total, out_rec_addr, outbuff ); d_total = 0; } puts( inbuff ); } inbuff[ MAX_LINE_SIZE - 1 ] = '\0'; if ( !fgets( inbuff, MAX_LINE_SIZE, stdin ) ) break; if ( inbuff[ MAX_LINE_SIZE - 1 ] ) { fprintf( stderr, "Input line too long" ); exit( 1 ); } length = strlen(inbuff); inbuff[length - 1] = '\0'; } return ( EXIT_SUCCESS ); } /**** identify_first_data_record ******************************************* * * Expects: Pointer to hex-record line buffer. * * Returns: Pointer to hex-record structure (NULL if no match found). * * Reads the standard input, line by line, searching for a valid * record header character. If a valid header is found, a pointer * to the hex-record's type structure is returned, otherwise NULL. * * The input-stream pointer is left pointing to the first valid hex record. * *******************************************************************************/ Rec_vitals * identify_first_data_record( char * buff_ptr, int max_length ) { Rec_vitals ** ptr; int length; for ( ;; ) { buff_ptr[ max_length - 1 ] = '\0'; if ( !fgets( buff_ptr, max_length, stdin ) ) break; if ( buff_ptr[ max_length - 1 ] ) { fprintf( stderr, "Input line too long" ); exit( 1 ); } length = strlen(buff_ptr); buff_ptr[length - 1] = '\0'; for ( ptr = formats ; *ptr != ( Rec_vitals * ) NULL ; ptr++ ) if ( ( *ptr )->is_data_record( buff_ptr ) == YES ) return( *ptr ); /* Successful return. */ puts( buff_ptr ); /* Echo non-hex-record line. */ } return( ( Rec_vitals * ) NULL ); /* Unsuccessful return. */ } /**** get_ndigit_hex ******************************************************* * * Expects: Pointer to first ASCII hexadecimal digit, number of digits. * * Returns: Value of hexadecimal string as an unsigned long. * *******************************************************************************/ Ulong get_ndigit_hex( char * cptr, int digits ) { Ulong value; for ( value = 0 ; --digits >= 0 ; cptr++ ) value = ( value * 16L ) + HEX_DIGIT( *cptr ); return( value ); }