/* * @(#) $Id: hexsum.c,v 1.2 90/05/01 21:45:29 marc Exp $ * * Copyright (c) 1989, 1990 Marco S. Hyman * * Permission to copy all or part of this material for any purpose is * granted provided that the above copyright notice and this paragraph * are duplicated in all copies. THIS SOFTWARE IS PROVIDED ``AS IS'' * AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS * FOR A PARTICULAR PURPOSE. * * $Log: hexsum.c,v $ * Revision 1.2 90/05/01 21:45:29 marc * - use getopt, clean up coments. * * Revision 1.1 89/11/19 13:40:09 marc * Initial revision * */ # include # include # include "hexsum.h" static char version[] = "@(#) $Id: hexsum.c,v 1.2 90/05/01 21:45:29 marc Exp $"; /* hexsum calculates a checksum from an intel hex file by: - building an array in memory of an appropriate size - pre-filling the array with a known value - expanding the hex file into the array ignoring out of bounds data - summing all the data in the array modulus 256 (save the location of the calculated checksum if it is within the array) - negating the checksum hexsum then formats the checksum into intel hex format and adds it to the hex file. The run command is: hexsum [-a] [-f] [-l] [-h] infile outfile The options are: -a The calculated checksum is located at adrs. This address is used as the load address for the checksum. The default address 0xFFFF. When the -a address is within the bounds set by the -l and -h addresses it will not be included in the checksum calculation. -f value is used to pre-fill `memory' before expanding the hex file. Default value is 0xFF. -l The -l option sets the lower bounds of the `memory' array. Data from the hexfile located at an address lower than adrs is ignored. Default adrs is 0x0000. -h The upper bounds of the `memory' array. Data in the hex file with a load address greater than adrs is ignored in checksum calculation. Default adrs is 0xFFFE. infile This is the name (including path if required) of the hex file to be checksummed. outfile This is the updated hex file. It consists of the of the input hex file with the addition of a record specifying the calculated checksum. adrs and val fields may be entered in decimal, octal, or hex. If the first digit is 0 and the second digit is x or X the number is interpreted as a hex integer. If the first digit is 0 and the second digit is in the range 0 thru 7 the number is interpreted as an octal integer. Numbers starting with the digit 1 thru 9 are interpreted as decimal integers. Spaces between option letters and values/addresses are optional. */ /* procedures returning other that int */ extern void main(); extern void hexsum(); extern char *buildArray(); extern long strtol(); extern char *malloc(); extern void expandFile(); extern void fillMem(); extern void calcSum(); /* getopt externals */ extern char* optarg; extern int optind; /* Default hexsum values are defined. These are the values that can be overridden at the command line. */ static unsigned int sumAdrs = 0xFFFF; /* -a value */ static unsigned int fillVal = 0xFF; /* -f value */ static unsigned int lowAdrs = 0x0000; /* -l value */ static unsigned int hiAdrs = 0xFFFE; /* -h value */ /* Note: Old style C is used as this program compiles under DOS and three flavors of UNIX. ANSI C isn't everywhere, yet. */ void main( argc, argv ) int argc; char *argv[]; { int opt; int badOptions = 0; /* Use getopt to process command line switches. */ while ((opt = getopt(argc, argv, "a:f:l:h:")) != -1) { unsigned int* whichOpt; char* endPtr; Report(("hexsum: processing option -%c value %s", opt, optarg )); switch (opt) { case 'a': whichOpt = &sumAdrs; break; case 'f': whichOpt = &fillVal; break; case 'l': whichOpt = &lowAdrs; break; case 'h': whichOpt = &hiAdrs; break; default: ++badOptions; } *whichOpt = (unsigned int) strtol(optarg, &endPtr, 0); if ( *endPtr ) { fprintf(stderr, "hexsum: invalid switch value (%s)\n", optarg); exit(1); } Report(("hexsum: param '-%c' value changed to %s", opt, optarg)); } Report(("hexsum: sumAdrs = %x", sumAdrs)); Report(("hexsum: fillVal = %x", fillVal)); Report(("hexsum: lowAdrs = %x", lowAdrs)); Report(("hexsum: hiAdrs = %x", hiAdrs)); /* options have been taken care of. Ensure that there are at two more command line args, the input file name and the output file name */ if (badOptions || ((optind + 2) != argc)) { fprintf(stderr, "usage: hexsum [-a] [-f] "); fprintf(stderr, "[-l] [-h] infile outfile\n"); exit(1); } hexsum(argv[optind], argv[optind+1]); exit(0); } /* hexsum verifies the input hex file exists and opens the output file. Hexsum then builds an array of the appropriate type, maps the input hexfile into the array as well as copying it to the output hexfile, calculates the checksum and adds it to the output hex file. */ void hexsum( inFileName, outFileName ) char *inFileName; char *outFileName; { FILE *hexFile; FILE *newFile; char *hexArray; Report(("hexsum: processing file %s", inFileName )); if ((hexFile = fopen( inFileName, "r")) == NULL ) { fprintf(stderr, "hexsum: can't open hex file %s\n", inFileName); exit(2); } Report(("hexsum: creating file %s", outFileName)); if ((newFile = fopen( outFileName, "w")) == NULL) { fprintf(stderr, "hexsum: can't create file %s\n", outFileName); exit(2); } hexArray = buildArray(lowAdrs, hiAdrs, (char) fillVal); expandFile( hexArray, hexFile, newFile ); free( hexArray ); fclose( hexFile ); fclose( newFile ); } /* buildArray verifies the array bounds, the array size, and then allocates memory for the array. Assuming the memory allocation did not fail the array is initialized to a passed value. Due to MS-DOS limitation the array size is limited to 65535 (0xFFFF) bytes long. */ char * buildArray( lowRange, highRange, initValue ) unsigned int lowRange; unsigned int highRange; char initValue; { unsigned int memSize; char *memArray; Report(("hexsum: building Array, L:%X H:%X", lowRange, highRange)); if ( lowRange >= highRange ) { fprintf( stderr, "hexsum: Invalid bounds L:%X H:%X\n", lowRange, highRange ); exit(2); } /* test the range before incrementing so this works on MS-DOS machines. */ memSize = highRange - lowRange; if (memSize == 0xFFFF) { fprintf( stderr, "hexsum: maximum size (0xFFFF) exceeded\n"); exit(2); } ++memSize; if ( (memArray = malloc( memSize )) == NULL ) { fprintf( stderr, "hexsum: can not get %d bytes for array\n", memSize ); exit(2); } memset( memArray, (int) initValue, memSize ); return memArray; } /* expandFile reads the input hex file (inFile) until an end of file record is read (:00aaaa01kk). The records are copied to the output hex file and decoded into the memory array. */ # define maxLineLen 255 void expandFile( mem, inFile, outFile ) char *mem; FILE *inFile; FILE *outFile; { char inStr[ maxLineLen ]; Report(( "hexsum: expanding hex file" )); while (fgets( inStr, maxLineLen, inFile)) { int count; unsigned int adrs; int type; int i; int tempSum; char data[maxLineLen]; /* verify this is a valid record. Must start with a ':' and must checksum to 0. Note: checksum code assumes hex file is composed of the digits 0 thru 9 and A thru F only. */ if (inStr[0] != ':') { fprintf( stderr, "hexsum: unknown record type: %s\n", inStr); exit(2); } for (tempSum = 0, i = 1; inStr[i] && (inStr[i] != '\n'); ++i ) { tempSum += toInt( inStr[i], i & 1 ); } if (tempSum & 0xFF) { fprintf( stderr, "hexsum: invalid hex record checksum (%X) %s", tempSum & 0xFF, inStr ); exit(2); } /* convert record to its component fields. Move the contents of the data field to the mem array (if it fits). If this is an EOF record calculate and add the check sum before writing the EOF record to the output file. */ sscanf(inStr, ":%2x%4x%2x%s", &count, &adrs, &type, data); if (type == 01) { Report(( "hexsum: calculating checksum" )); calcSum( mem, outFile ); fputs(inStr, outFile); break; } if ( ((adrs + count) > lowAdrs) && (adrs <= hiAdrs) ) { fillMem( adrs, count, data, mem ); } fputs(inStr, outFile); } } /* convert count ascii byte-pairs in data to hex and place in mem at adrs offset by lowAdrs. Check the bounds -- not every byte may fit. */ void fillMem( adrs, count, data, mem ) unsigned int adrs; int count; char *data; char *mem; { int next; for (next = 0; next < count; ++next ) { int value; if (((adrs + next) < lowAdrs) || ((adrs + next) > hiAdrs)) { continue; } value = toInt( data[next*2], 1); /* 1 = high nibble */ value += toInt( data[next*2+1], 0); /* 0 = low nibble */ mem[adrs + next - lowAdrs] = value & 0xFF; } } int toInt( aChar, highNibble ) char aChar; int highNibble; { int workVal; if (('A' <= aChar) && (aChar <= 'F')) { workVal = 10 + aChar - 'A'; } else { workVal = aChar - '0'; } if (highNibble) { workVal *= 16; } return workVal; } /* Calculate the checksum on memArray. Array bounds are lowAdrs and hiAdrs. Create a hex record to place the checksum at sumAdrs. Add the hex record to the hexFile. */ void calcSum( memArray, hexFile ) char *memArray; FILE *hexFile; { unsigned int i; unsigned int limit; int checksum; int recsum; limit = hiAdrs - lowAdrs + 1; /* initialize the checksum. Subtract out any value located where we will * place the calculated checksum if it is within the array bounds. */ if ((lowAdrs <= sumAdrs) && (sumAdrs <= hiAdrs)) { checksum = - (int) memArray[ sumAdrs - lowAdrs ]; } else { checksum = 0; } for ( i = 0; i < limit; ++i ) { checksum += (int) memArray[i]; } checksum = -checksum & 0xFF; Report(("hexsum: calculated checksum is %2.2X", checksum)); /* calculate the record sum */ recsum = -(1 + (sumAdrs/256) + (sumAdrs%256) + checksum) & 0xFF; fprintf( hexFile, ":01%4.4X00%2.2X%2.2X\n", sumAdrs, checksum, recsum ); Report(("hexsum: :01%4.4X00%2.2X%2.2X", sumAdrs, checksum, recsum)); }