1 /*-----------------------------------------------------------------------
4 * utility to pack an Intel HEX format file by removing redundant
5 * extended offset records and accumulating data records up to
6 * OUTPUT_CHUNK (currently 16) bytes.
8 * Released to the public domain 10/16/2000 Kevin Vigor.
17 #if defined(_MSC_VER) || defined(__BORLANDC__)
19 typedef unsigned char Uint8
;
20 typedef unsigned Uint16
;
27 /* A cooked line of input. */
30 Uint8 len
; /* length of data portion of record. */
37 /* Largest expected line of raw input. */
38 #define MAX_INPUT_RAW 128
39 /* Largest expected cooked data portion of an input line. */
40 #define MAX_INPUT_COOKED (MAX_INPUT_RAW / 2)
42 /* Globals: current input & output line numbers. */
46 /* Convert hex digit to numeric value 0 - 15; assumes input is a
47 * valid digit (i.e. passes isxdigit()).
49 static Uint8
hexDigit(const unsigned char c
)
53 return (Uint8
)(c
- '0');
57 return (Uint8
)((islower(c
) ? toupper(c
) : c
) - 'A' + 10);
61 /* Convert two hex digits from cp to a byte value. */
62 static int getHexByte(const char *cp
, Uint8
*byte
)
64 if (cp
&& cp
[0] && isxdigit(cp
[0]) && cp
[1] && isxdigit(cp
[1]))
66 *byte
= (hexDigit(cp
[0]) << 4) + hexDigit(cp
[1]);
75 /* Convert four hex digits from cp to a 2 byte value. */
76 static int getHexWord(const char *cp
, Uint16
*word
)
80 if (getHexByte(cp
, &byte1
) || getHexByte(cp
+ 2, &byte2
))
84 *word
= (byte1
<< 8) + byte2
;
88 /* Return a single cooked line of input from the passed file handle. */
89 Line
*readLine(FILE *inFile
)
91 static char buffer
[MAX_INPUT_RAW
];
96 line
= (Line
*)malloc(sizeof(Line
));
99 fprintf(stderr
, "packihx: no memory!\n");
105 if (!fgets(buffer
, MAX_INPUT_RAW
, inFile
))
111 if (!buffer
[0] || buffer
[0] == '\r' || buffer
[0] == '\n')
113 /* Empty input line. */
116 } while (buffer
[0] != ':');
119 bp
++; /* Skip leading : */
121 if (getHexByte(bp
, &line
->len
))
123 fprintf(stderr
, "packihx: can't read line length @ line %d\n",
128 bp
+= 2; /* Two digits consumed. */
130 if (line
->len
> MAX_INPUT_COOKED
)
132 fprintf(stderr
, "packihx: line length %X too long @ line %d\n",
133 (int)line
->len
, lineno
);
138 if (getHexWord(bp
, &line
->offset
))
140 fprintf(stderr
, "packihx: can't read line offset @ line %d\n",
145 bp
+= 4; /* Four digits consumed. */
147 if (getHexByte(bp
, &line
->type
))
149 fprintf(stderr
, "packihx: can't read record type @ line %d\n",
154 bp
+= 2; /* Two digits consumed. */
156 /* Hack - always allocate something, even if len is zero.
157 * Avoids special case for len == 0. */
158 line
->data
= (Uint8
*)malloc(line
->len
? line
->len
: 1);
162 fprintf(stderr
, "packihx: no memory!\n");
166 for (i
= 0; i
< (unsigned)line
->len
; i
++)
168 if (getHexByte(bp
, &(line
->data
[i
])))
171 "packihx: can't read data byte %u of %u @ line %d\n",
172 i
, (unsigned) line
->len
, lineno
);
177 bp
+= 2; /* Two digits consumed. */
180 if (getHexByte(bp
, &line
->checksum
))
182 fprintf(stderr
, "packihx: can't read checksum @ line %d\n",
188 /* bp += 2; */ /* Two digits consumed. */
193 /* Compute the checksum of a line. */
194 Uint16
lineChecksum(unsigned len
, unsigned offset
, unsigned type
,
200 checksum
= len
+ type
+ (offset
>> 8) +
203 for (i
= 0; i
< len
; i
++)
211 checksum
= 0x100 - checksum
;
216 /* Ensure that the checksum of a line matches the expected value. */
217 int validateChecksum(Line
*line
)
221 checksum
= lineChecksum(line
->len
, line
->offset
, line
->type
, line
->data
);
223 if (checksum
!= line
->checksum
)
225 fprintf(stderr
, "packihx: invalid checksum %X (want %X) @ line %d\n",
226 (unsigned)(line
->checksum
), (unsigned)checksum
,
234 /* Write a single record line. */
235 int writeRecord(unsigned len
, unsigned offset
, unsigned type
,
240 if (printf(":%02X%04X%02X", len
, offset
, type
) == EOF
)
245 for (i
= 0; i
< len
; i
++)
247 if (printf("%02X", data
[i
]) == EOF
)
253 if (printf("%02X\n", lineChecksum(len
, offset
, type
, data
)) == EOF
)
261 #define OUTPUT_CHUNK 16
262 static unsigned pendingLen
= 0;
263 static unsigned pendingOffset
= 0;
264 static Uint8 pending
[MAX_INPUT_COOKED
+ OUTPUT_CHUNK
];
266 /* Buffer up a data record. */
267 int bufferOutput(Line
*line
)
272 /* Stick the data onto any pending data. */
273 assert(pendingLen
< OUTPUT_CHUNK
);
274 memcpy(&pending
[pendingLen
], line
->data
, line
->len
);
275 pendingLen
+= line
->len
;
277 /* Write it out untill we have less than an OUTPUT_CHUNK left. */
278 while (!rc
&& pendingLen
>= OUTPUT_CHUNK
)
280 rc
= writeRecord(OUTPUT_CHUNK
, pendingOffset
, 0, &pending
[offset
]);
281 offset
+= OUTPUT_CHUNK
;
282 pendingOffset
+= OUTPUT_CHUNK
;
283 pendingLen
-= OUTPUT_CHUNK
;
286 /* Copy any remaining bits back to the beginning of the buffer. */
289 memmove(pending
, &pending
[offset
], pendingLen
);
294 /* Write out any pending data. */
295 int flushPendingData(void)
299 assert(pendingLen
< OUTPUT_CHUNK
);
303 rc
= writeRecord(pendingLen
, pendingOffset
, 0, pending
);
304 pendingLen
= pendingOffset
= 0;
309 /* Write an arbitrary line of output (buffering if possible) */
310 int writeLine(Line
*line
)
312 static Uint16 lastExtendedOffset
= 0;
317 /* Not a data record. */
322 /* Extended offset record. */
326 "packihx: invalid extended offset record @ line %d\n",
331 offset
= (line
->data
[0] << 8) + line
->data
[1];
333 if (offset
== lastExtendedOffset
)
335 /* We can simply skip this line. */
340 lastExtendedOffset
= offset
;
344 if (flushPendingData())
349 /* Write the line as is. */
350 rc
= writeRecord(line
->len
, line
->offset
, line
->type
, line
->data
);
354 if (pendingOffset
+ pendingLen
!= (unsigned)line
->offset
)
356 /* This line is not contigous with the last one. Dump pending. */
357 if (flushPendingData())
361 pendingOffset
= line
->offset
;
363 rc
= bufferOutput(line
);
368 int main(int argc
, char *argv
[])
377 inFile
= fopen(argv
[1], "rt");
380 fprintf(stderr
, "packihx: cannot open %s\n",
392 while (!rc
&& ((line
= readLine(inFile
)) != NULL
))
394 rc
= validateChecksum(line
);
398 rc
= writeLine(line
);
405 if (!rc
&& !feof(inFile
))
407 /* readLine must have failed for some reason. */
408 fprintf(stderr
, "packihx: aborting after %d lines.\n", lineno
);
414 /* Just in case there's something still pending. */
415 rc
= flushPendingData();
420 fprintf(stderr
, "packihx: read %d lines, wrote %d: OK.\n", lineno
, outlineno
);