source: rtems/c/build-tools/src/packhex.c @ 9eef52b

4.104.114.84.95
Last change on this file since 9eef52b was 185fab0, checked in by Joel Sherrill <joel.sherrill@…>, on 05/18/98 at 16:32:35

Added proper reference now that Embedded Systems Programming's WWW site
is available and has the code available.

  • Property mode set to 100644
File size: 14.8 KB
Line 
1
2/*****  P A C K H E X . C  ************************************************
3 *
4 *   Packhex is a hex-file compaction utility.  It attempts to concatenate
5 *   hex records to produce more size-efficient packaging.
6 *
7 *   Limitations: Input files must be correctly formatted.  This utility
8 *                is not robust enough to detect hex-record formatting
9 *                errors.
10 *
11 *   Published:   May 1993 Embedded Systems Programming magazine
12 *                "Creating Faster Hex Files"
13 *
14 *   URL:         ESP magazine: http://www.embedded.com
15 *                Source Code:  ftp://ftp.mfi.com/pub/espmag/1993/pakhex.zip
16 *
17 *   Author:      Mark Gringrich
18 *
19 *   Compiler:    Microsoft C 6.0
20 *                cl /F 1000 packhex.c
21 *
22 *
23 *   $Id$
24 *
25 **************************************************************************/
26
27
28/* #define SMALLER_RECORDS */
29#ifdef SMALLER_RECORDS
30#define MAX_LEN_S1_RECS 128
31#define MAX_LEN_S2_RECS 128
32#define MAX_LEN_S3_RECS 128
33#else
34#define MAX_LEN_S1_RECS 252
35#define MAX_LEN_S2_RECS 251
36#define MAX_LEN_S3_RECS 250
37#endif
38
39
40/*--------------------------------- includes ---------------------------------*/
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include "config.h"
47
48#ifndef VMS
49#ifndef HAVE_STRERROR
50extern int sys_nerr;
51extern char *sys_errlist[];
52
53#define strerror( _err ) \
54  ((_err) < sys_nerr) ? sys_errlist [(_err)] : "unknown error"
55
56#else   /* HAVE_STRERROR */
57char *strerror ();
58#endif
59#else   /* VMS */
60char *strerror (int,...);
61#endif
62
63#if defined(__unix__) && !defined(EXIT_FAILURE)
64#define EXIT_FAILURE -1
65#define EXIT_SUCCESS  0
66#endif
67
68/*--------------------------------- defines ----------------------------------*/
69
70#define YES                   1
71#define MAX_LINE_SIZE       600
72#define EOS                 '\0'
73
74
75/*---------------------------------- macros ----------------------------------*/
76
77/* Convert ASCII hexadecimal digit to value. */
78
79#define HEX_DIGIT( C )   ( ( ( ( C ) > '9' ) ? ( C ) + 25 : ( C ) ) & 0xF )
80
81
82/*--------------------------------- typedefs ---------------------------------*/
83
84typedef unsigned char   Boolean;
85typedef unsigned char   Uchar;
86typedef unsigned int    Uint;
87typedef unsigned long   Ulong;
88
89typedef struct   /* Functions and constant returning Hex-record vital stats. */
90{
91    Boolean    ( *is_data_record  )( char *              );
92    Ulong      ( *get_address     )( char *              );
93    Uint       ( *get_data_count  )( char *              );
94    const Uint    max_data_count;
95    char      *( *get_data_start  )( char *              );
96    void       ( *put_data_record )( Uint, Ulong, char * );
97} Rec_vitals;
98
99
100/*--------------------------- function prototypes ----------------------------*/
101
102Rec_vitals  * identify_first_data_record( char * );
103Ulong         get_ndigit_hex( char *, int );
104
105
106/*----------------------------- Intel Hex format -----------------------------*/
107
108/*
109 *    Intel Hex data-record layout
110 *
111 *    :aabbbbccd...dee
112 *
113 *    :      - header character
114 *    aa     - record data byte count, a 2-digit hex value
115 *    bbbb   - record address, a 4-digit hex value
116 *    cc     - record type, a 2-digit hex value:
117 *              "00" is a data record
118 *              "01" is an end-of-data record
119 *              "02" is an extended-address record
120 *              "03" is a start record
121 *    d...d  - data (always an even number of chars)
122 *    ee     - record checksum, a 2-digit hex value
123 *             checksum = 2's complement
124 *                 [ (sum of bytes: aabbbbccd...d) modulo 256 ]
125 */
126
127
128Boolean is_intel_data_rec( char * rec_str )
129{
130    return( ( rec_str[ 0 ] == ':' )  &&  ( rec_str[ 8 ] == '0' ) );
131}
132
133Uint get_intel_rec_data_count( char * rec_str )
134{
135    return( ( Uint ) get_ndigit_hex( rec_str + 1, 2 ) );
136}
137
138Ulong get_intel_rec_address( char * rec_str )
139{
140    return( get_ndigit_hex( rec_str + 3, 4 ) );
141}
142
143char * get_intel_rec_data_start( char * rec_str )
144{
145    return( rec_str + 9 );
146}
147
148void put_intel_data_rec( Uint count, Ulong address, char * data_str )
149{
150    char    *ptr;
151    Uint    sum = count + ( address >> 8 & 0xff ) + ( address & 0xff );
152
153    for ( ptr = data_str ; *ptr != EOS ; ptr += 2 )
154        sum += ( Uint ) get_ndigit_hex( ptr, 2 );
155
156    printf(
157      ":%02X%04lX00%s%02X\n", count, address, data_str, (~sum + 1) & 0xff
158    );
159}
160
161
162Rec_vitals  intel_hex =
163{
164    is_intel_data_rec,
165    get_intel_rec_address,
166    get_intel_rec_data_count,
167    255,                        /* Maximum data bytes in a record. */
168    get_intel_rec_data_start,
169    put_intel_data_rec
170};
171
172
173/*------------------------- Motorola S1-record format ------------------------*/
174
175/*
176 *    Motorola S-record data-record layout
177 *
178 *    Sabbc...cd...dee
179 *
180 *    S      - header character
181 *    a      - record type, a 1-digit value:
182 *               "0" is a header record
183 *               "1" is a 2-byte-address data record
184 *               "2" is a 3-byte-address data record
185 *               "3" is a 4-byte-address data record
186 *               "7" is a 4-byte-address end-of-data record
187 *               "8" is a 3-byte-address end-of-data record
188 *               "9" is a 2-byte-address end-of-data record
189 *    bb     - record length in bytes, a 2-digit hex value
190 *             (record length doesn't count the header/type
191 *              chars and checksum byte)
192 *    c...c  - record address, a 4-, 6-, or 8-digit value,
193 *               depending on record type
194 *    d...d  - data (always an even number of chars)
195 *    ee     - record checksum, a 2-digit hex value
196 *             checksum = 1's complement
197 *             [ (sum of all bytes: bbc..cd...d) modulo 256 ]
198 */
199
200#define S1_COUNT_OFFSET         3
201
202
203Boolean is_moto_s1_data_rec( char * rec_str )
204{
205    return ( ( rec_str[ 0 ] == 'S' )  &&  ( rec_str[ 1 ] == '1' ) );
206}
207
208Uint get_moto_s1_rec_data_count( char * rec_str )
209{
210    return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S1_COUNT_OFFSET );
211}
212
213Ulong get_moto_s1_rec_address( char * rec_str )
214{
215    return( get_ndigit_hex( rec_str + 4, 4 ) );
216}
217
218char * get_moto_s1_rec_data_start( char * rec_str )
219{
220    return( rec_str + 8 );
221}
222
223void put_moto_s1_data_rec( Uint count, Ulong address, char * data_str )
224{
225    char   *ptr;
226    Uint    sum = S1_COUNT_OFFSET + count +
227                    ( address >> 8 & 0xff ) + ( address & 0xff );
228
229    for ( ptr = data_str ; *ptr != EOS ; ptr += 2 )
230        sum += ( Uint ) get_ndigit_hex( ptr, 2 );
231
232    printf(
233      "S1%02X%04lX%s%02X\n",
234      count + S1_COUNT_OFFSET, address, data_str, ~sum & 0xff
235    );
236}
237
238
239Rec_vitals  motorola_s1_rec =
240{
241    is_moto_s1_data_rec,
242    get_moto_s1_rec_address,
243    get_moto_s1_rec_data_count,
244    MAX_LEN_S1_RECS,            /* Maximum data bytes in a record. */
245    get_moto_s1_rec_data_start,
246    put_moto_s1_data_rec
247};
248
249
250/*------------------------- Motorola S2-record format ------------------------*/
251
252#define S2_COUNT_OFFSET         4
253
254Boolean is_moto_s2_data_rec( char * rec_str )
255{
256    return ( ( rec_str[ 0 ] == 'S' )  &&  ( rec_str[ 1 ] == '2' ) );
257}
258
259Uint get_moto_s2_rec_data_count( char * rec_str )
260{
261    return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S2_COUNT_OFFSET );
262}
263
264Ulong get_moto_s2_rec_address( char * rec_str )
265{
266    return( get_ndigit_hex( rec_str + 4, 6 ) );
267}
268
269char * get_moto_s2_rec_data_start( char * rec_str )
270{
271    return( rec_str + 10 );
272}
273
274void put_moto_s2_data_rec( Uint count, Ulong address, char * data_str )
275{
276    char    *ptr;
277    Uint    sum = S2_COUNT_OFFSET + count + ( address >> 16 & 0xff ) +
278                                            ( address >>  8 & 0xff ) +
279                                            ( address       & 0xff );
280
281    for ( ptr = data_str ; *ptr != EOS ; ptr += 2 )
282        sum += ( Uint ) get_ndigit_hex( ptr, 2 );
283
284    printf(
285      "S2%02X%06lX%s%02X\n",
286      count + S2_COUNT_OFFSET, address, data_str, ~sum & 0xff
287    );
288}
289
290
291Rec_vitals  motorola_s2_rec =
292{
293    is_moto_s2_data_rec,
294    get_moto_s2_rec_address,
295    get_moto_s2_rec_data_count,
296    MAX_LEN_S2_RECS,            /* Maximum data bytes in a record. */
297    get_moto_s2_rec_data_start,
298    put_moto_s2_data_rec
299};
300
301
302/*------------------------- Motorola S3-record format ------------------------*/
303
304#define S3_COUNT_OFFSET         5
305
306Boolean is_moto_s3_data_rec( char * rec_str )
307{
308    return ( ( rec_str[ 0 ] == 'S' )  &&  ( rec_str[ 1 ] == '3' ) );
309}
310
311Uint get_moto_s3_rec_data_count( char * rec_str )
312{
313    return( ( Uint ) get_ndigit_hex( rec_str + 2, 2 ) - S3_COUNT_OFFSET );
314}
315
316Ulong get_moto_s3_rec_address( char * rec_str )
317{
318    return( get_ndigit_hex( rec_str + 4, 8 ) );
319}
320
321char * get_moto_s3_rec_data_start( char * rec_str )
322{
323    return( rec_str + 12 );
324}
325
326void put_moto_s3_data_rec( Uint count, Ulong address, char * data_str )
327{
328    char    *ptr;
329    Uint     sum = S3_COUNT_OFFSET + count + ( address >> 24 & 0xff ) +
330                                             ( address >> 16 & 0xff ) +
331                                             ( address >>  8 & 0xff ) +
332                                             ( address       & 0xff );
333
334    for ( ptr = data_str ; *ptr != EOS ; ptr += 2 )
335        sum += ( Uint ) get_ndigit_hex( ptr, 2 );
336
337    printf(
338      "S3%02X%08lX%s%02X\n",
339      count + S3_COUNT_OFFSET, address, data_str, ~sum & 0xff
340   );
341}
342
343
344Rec_vitals  motorola_s3_rec =
345{
346    is_moto_s3_data_rec,
347    get_moto_s3_rec_address,
348    get_moto_s3_rec_data_count,
349    MAX_LEN_S3_RECS,            /* Maximum data bytes in a record. */
350    get_moto_s3_rec_data_start,
351    put_moto_s3_data_rec
352};
353
354
355/*-------------------- Put your favorite hex format here ---------------------*/
356
357/*
358 *    * * *  The following is a template for an additional hex format:  * * *
359 *
360 *
361 *    Boolean is_X_data_rec( char * rec_str ) {}
362 *
363 *    Uint get_X_rec_data_count( char * rec_str ) {}
364 *
365 *    Ulong get_X_rec_address( char * rec_str ) {}
366 *
367 *    char * get_X_rec_data_start( char * rec_str ) {}
368 *
369 *    void put_X_data_rec( Uint count, Ulong address, char * data_str ) {}
370 *
371 *    Rec_vitals  X_rec =
372 *    {
373 *        is_X_data_rec,
374 *        get_X_rec_address,
375 *        get_X_rec_data_count,
376 *        MAXIMUM DATA BYTES IN A RECORD,
377 *        get_X_rec_data_start,
378 *        put_X_data_rec
379 *    };
380 *
381 */
382
383/*----------------------------------------------------------------------------*/
384
385
386/*
387 *   Put address of additional Rec_vitals structures
388 *   in this array, before the NULL entry.
389 */
390
391Rec_vitals  *formats[] =
392{
393    &intel_hex,
394    &motorola_s1_rec,
395    &motorola_s2_rec,
396    &motorola_s3_rec,
397    ( Rec_vitals * ) NULL
398};
399
400
401/****   main   *****************************************************************
402*
403*
404*       Expects: Nothing (no command-line parameters).
405*
406*       Returns: Exit status (EXIT_SUCCESS or EXIT_FAILURE).
407*
408*       Reads hex records on the standard input and attempts to
409*       splice adjacent data fields together.  Results appear on
410*       the standard output.
411*
412*******************************************************************************/
413
414int main(
415  int argc,
416  char **argv
417)
418{
419
420    char       inbuff[ MAX_LINE_SIZE ], outbuff[ MAX_LINE_SIZE ];
421    char       *in_dptr, *out_dptr;
422    int        d_total, d_count, d_excess, n;
423    Ulong      in_rec_addr, out_rec_addr = 0;
424    Rec_vitals *rptr;
425
426
427    /* Sift through file until first hex record is identified.    */
428    if ( ( rptr = identify_first_data_record( inbuff ) ) == NULL )
429    {
430        fputs( "No hex records found.\n", stderr );
431        exit( EXIT_FAILURE );
432    }
433
434
435    /* Attempt data-record splicing until end-of-file is reached. */
436    d_total = 0;
437    do
438    {
439        if ( rptr->is_data_record( inbuff ) == YES )
440        { /* Input record is a data record. */
441            d_count     = rptr->get_data_count( inbuff );
442            in_rec_addr = rptr->get_address( inbuff );
443            in_dptr     = rptr->get_data_start( inbuff );
444
445            if ( d_total == 0  ||  in_rec_addr != out_rec_addr + d_total )
446            { /* Begin a new output record. */
447                if ( d_total != 0 )
448                    rptr->put_data_record( d_total, out_rec_addr, outbuff );
449                out_dptr     = outbuff;
450                n = d_total  = d_count;
451                out_rec_addr = in_rec_addr;
452            }
453            else if
454              ( ( d_excess = d_total + d_count - rptr->max_data_count ) > 0 )
455            { /* Output a maximum-length record, then start a new record. */
456                strncat( outbuff, in_dptr, 2 * ( d_count - d_excess ) );
457                rptr->put_data_record(
458                  rptr->max_data_count, out_rec_addr, outbuff
459                );
460                in_dptr      += 2 * ( d_count - d_excess );
461                out_dptr      = outbuff;
462                n = d_total   = d_excess;
463                out_rec_addr += rptr->max_data_count;
464            }
465            else
466            { /* Append input record's data field with accumulated data. */
467                out_dptr = outbuff + ( 2 * d_total );
468                d_total += n = d_count;
469            }
470            strncpy( out_dptr, in_dptr, 2 * n );
471            out_dptr[ 2 * n ] = EOS;
472        }
473        else
474        { /* Not a data record;
475           * flush accumulated data then echo non-data record.
476           */
477            if ( d_total != 0 )
478            {
479                rptr->put_data_record( d_total, out_rec_addr, outbuff );
480                d_total = 0;
481            }
482            puts( inbuff );
483        }
484    } while ( gets( inbuff ) != NULL );
485
486
487    return ( EXIT_SUCCESS );
488
489}
490
491
492/****   identify_first_data_record   *******************************************
493*
494*       Expects: Pointer to hex-record line buffer.
495*
496*       Returns: Pointer to hex-record structure (NULL if no match found).
497*
498*       Reads the standard input, line by line, searching for a valid
499*       record header character.  If a valid header is found, a pointer
500*       to the hex-record's type structure is returned, otherwise NULL.
501*
502*       The input-stream pointer is left pointing to the first valid hex record.
503*
504*******************************************************************************/
505
506Rec_vitals * identify_first_data_record( char * buff_ptr )
507{
508    Rec_vitals  ** ptr;
509
510    while ( gets( buff_ptr ) != NULL )
511    {
512        for ( ptr = formats ; *ptr != ( Rec_vitals * ) NULL ; ptr++ )
513            if ( ( *ptr )->is_data_record( buff_ptr ) == YES )
514                return( *ptr );        /* Successful return.        */
515
516        puts( buff_ptr );              /* Echo non-hex-record line. */
517    }
518
519    return( ( Rec_vitals * ) NULL );   /* Unsuccessful return.      */
520}
521
522
523/****   get_ndigit_hex   *******************************************************
524*
525*       Expects: Pointer to first ASCII hexadecimal digit, number of digits.
526*
527*       Returns: Value of hexadecimal string as an unsigned long.
528*
529*******************************************************************************/
530
531Ulong get_ndigit_hex( char * cptr, int digits )
532{
533    Ulong    value;
534
535    for ( value = 0 ; --digits >= 0 ; cptr++ )
536        value = ( value * 16L ) + HEX_DIGIT( *cptr );
537
538    return( value );
539}
Note: See TracBrowser for help on using the repository browser.