mirror of
https://github.com/VARCem/ihex.git
synced 2026-09-24 07:54:27 +00:00
Implement reading IHEX
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
*.o
|
||||
bbin2ihex
|
||||
bin2ihex
|
||||
ihex2bin
|
||||
|
||||
9
Makefile
9
Makefile
@@ -2,12 +2,15 @@ CC=clang
|
||||
CFLAGS=-Wall -std=c99 -pedantic
|
||||
|
||||
OBJS = kk_ihex.o
|
||||
BINS = bin2ihex
|
||||
BINS = bin2ihex ihex2bin
|
||||
|
||||
all: $(BINS)
|
||||
|
||||
bin2ihex: bin2ihex.c $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||
bin2ihex: bin2ihex.c kk_ihex.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -DKK_IHEX_DISABLE_READING -o $@ $^
|
||||
|
||||
ihex2bin: ihex2bin.c kk_ihex.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -DKK_IHEX_DISABLE_WRITING -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(BINS)
|
||||
|
||||
12
bin2ihex.c
12
bin2ihex.c
@@ -1,14 +1,16 @@
|
||||
/*
|
||||
* bbin2ihex.c: Read binary data from stdin, output in IHEX format.
|
||||
* bin2ihex.c: Read binary data from stdin, output in IHEX format.
|
||||
*
|
||||
* By Kimmo Kulovesi, http://arkku.com
|
||||
* Copyright (c) 2013 Kimmo Kulovesi, http://arkku.com
|
||||
* Provided with absolutely no warranty, use at your own risk only.
|
||||
* Distribute freely, mark modified copies as such.
|
||||
*/
|
||||
|
||||
#include "kk_ihex.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//#define IHEX_WRITE_INITIAL_EXTENDED_ADDRESS_RECORD
|
||||
//#define KK_IHEX_WRITE_INITIAL_EXTENDED_ADDRESS_RECORD
|
||||
|
||||
int
|
||||
main (void) {
|
||||
@@ -18,7 +20,7 @@ main (void) {
|
||||
|
||||
ihex_init(&ihex);
|
||||
ihex_write_at_address(&ihex, 0);
|
||||
#ifdef IHEX_WRITE_INITIAL_EXTENDED_ADDRESS_RECORD
|
||||
#ifdef KK_IHEX_WRITE_INITIAL_EXTENDED_ADDRESS_RECORD
|
||||
ihex.flags |= IHEX_FLAG_ADDRESS_OVERFLOW;
|
||||
#endif
|
||||
while ((count = fread(buf, 1, sizeof(buf), stdin))) {
|
||||
@@ -29,7 +31,7 @@ main (void) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void ihex_flush_buffer(char *buffer, char *eptr) {
|
||||
void ihex_flush_buffer(struct ihex_state *ihex, char *buffer, char *eptr) {
|
||||
*eptr = '\0';
|
||||
(void) fputs(buffer, stdout);
|
||||
}
|
||||
|
||||
88
ihex2bin.c
Normal file
88
ihex2bin.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* ihex2bin.c: Read Intel HEX data from stdin, write binary data to stdout
|
||||
* or a file specified on the command line. Specifying output file allows
|
||||
* sparse and/or unordered data to be written.
|
||||
*
|
||||
* Copyright (c) 2013 Kimmo Kulovesi, http://arkku.com
|
||||
* Provided with absolutely no warranty, use at your own risk only.
|
||||
* Distribute freely, mark modified copies as such.
|
||||
*/
|
||||
|
||||
#include "kk_ihex.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static FILE *outfile;
|
||||
static unsigned long line_number = 1;
|
||||
static unsigned long file_position = 0L;
|
||||
|
||||
int
|
||||
main (int argc, char *argv[]) {
|
||||
struct ihex_state ihex;
|
||||
int c;
|
||||
|
||||
if (argc == 2) {
|
||||
if (!(outfile = fopen(argv[1], "wb"))) {
|
||||
perror(argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
outfile = stdout;
|
||||
}
|
||||
ihex_init(&ihex);
|
||||
ihex_begin_read(&ihex);
|
||||
|
||||
while ((c = fgetc(stdin)) != EOF) {
|
||||
ihex_read_byte(&ihex, c);
|
||||
line_number += (c == '\n') ? 1 : 0;
|
||||
}
|
||||
ihex_end_read(&ihex);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
bool ihex_data_read (struct ihex_state *ihex,
|
||||
enum ihex_record_type type,
|
||||
bool error) {
|
||||
if (error) {
|
||||
(void) fprintf(stderr, "Checksum error on line %lu\n", line_number);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((error = (ihex->length < ihex->line_length))) {
|
||||
(void) fprintf(stderr, "Line length error on line %lu\n", line_number);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!outfile) {
|
||||
(void) fprintf(stderr, "Excess data after end of file record\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (type == IHEX_DATA_RECORD) {
|
||||
unsigned long address = (unsigned long) ihex_linear_address(ihex);
|
||||
if (address != file_position) {
|
||||
(void) fprintf(stderr, "Seeking from %lu to %lu on line %lu\n",
|
||||
file_position, address, line_number);
|
||||
if (file_position < address && outfile == stdout) {
|
||||
// "seek" forward in stdout by writing NUL bytes
|
||||
do {
|
||||
(void) fputc('\0', outfile);
|
||||
} while (++file_position < address);
|
||||
} else if (fseek(outfile, address, SEEK_SET)) {
|
||||
perror("fseek");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
file_position = address;
|
||||
}
|
||||
if (!fwrite(ihex->data, ihex->length, 1, outfile)) {
|
||||
perror("fwrite");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
file_position += ihex->length;
|
||||
} else if (type == IHEX_END_OF_FILE_RECORD) {
|
||||
(void) fprintf(stderr, "%lu bytes written\n", file_position);
|
||||
if (outfile != stdout) {
|
||||
(void) fclose(outfile);
|
||||
}
|
||||
outfile = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
240
kk_ihex.c
240
kk_ihex.c
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* kk_ihex.c: A simple library to read and write Intel HEX data.
|
||||
*
|
||||
* See the header `kk_ihex.h` for instructions.
|
||||
*
|
||||
* Copyright (c) 2013 Kimmo Kulovesi, http://arkku.com/
|
||||
* Provided with absolutely no warranty, use at your own risk only.
|
||||
* Use and distribute freely, mark modified copies as such.
|
||||
*/
|
||||
|
||||
#include "kk_ihex.h"
|
||||
|
||||
static const char IHEX_START = ':';
|
||||
@@ -8,7 +18,25 @@ static const char IHEX_NEWLINE[] = "\n"; // end of line, e.g., "\n" or "\r\n"
|
||||
|
||||
#define HEX_DIGIT(n) ((n) + ( ((n) < 10) ? '0' : ('A' - 10)))
|
||||
|
||||
static char line_buffer[1+2+4+2+IHEX_LINE_LENGTH+2+sizeof(IHEX_NEWLINE)];
|
||||
enum ihex_read_state {
|
||||
READ_WAIT_FOR_START = 1,
|
||||
READ_COUNT_HIGH,
|
||||
READ_COUNT_LOW,
|
||||
READ_ADDRESS_MSB_HIGH,
|
||||
READ_ADDRESS_MSB_LOW,
|
||||
READ_ADDRESS_LSB_HIGH,
|
||||
READ_ADDRESS_LSB_LOW,
|
||||
READ_RECORD_TYPE_HIGH,
|
||||
READ_RECORD_TYPE_LOW,
|
||||
READ_DATA_HIGH,
|
||||
READ_DATA_LOW,
|
||||
READ_CHECKSUM_HIGH,
|
||||
READ_CHECKSUM_LOW
|
||||
};
|
||||
|
||||
#define IHEX_READ_RECORD_TYPE_MASK 0x07
|
||||
#define IHEX_READ_STATE_MASK 0x78
|
||||
#define IHEX_READ_STATE_OFFSET 3
|
||||
|
||||
void
|
||||
ihex_init (struct ihex_state * const ihex) {
|
||||
@@ -16,8 +44,13 @@ ihex_init (struct ihex_state * const ihex) {
|
||||
ihex->segment = 0;
|
||||
ihex->length = 0;
|
||||
ihex->flags = 0;
|
||||
ihex->line_length = IHEX_DEFAULT_OUTPUT_LINE_LENGTH;
|
||||
}
|
||||
|
||||
#ifndef KK_IHEX_DISABLE_WRITING
|
||||
|
||||
static char line_buffer[1+2+4+2+IHEX_LINE_MAX_LENGTH+2+sizeof(IHEX_NEWLINE)];
|
||||
|
||||
static char *
|
||||
ihex_buffer_byte (char *w, const unsigned int byte) {
|
||||
unsigned int n = (byte & 0xF0U) >> 4; // high nybble
|
||||
@@ -96,16 +129,6 @@ ihex_buffer_data (char *w, struct ihex_state * const ihex) {
|
||||
return ihex_buffer_newline(w);
|
||||
}
|
||||
|
||||
ihex_address_t
|
||||
ihex_linear_address (struct ihex_state *ihex) {
|
||||
ihex_address_t address = ihex->address;
|
||||
unsigned int segment = ihex->segment;
|
||||
if (segment) {
|
||||
address += ((ihex_address_t)segment) << 4;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
static char *
|
||||
ihex_buffer_extended_address (char *w, const ihex_segment_t address,
|
||||
const enum ihex_record_type type) {
|
||||
@@ -141,7 +164,7 @@ ihex_check_address_overflow (struct ihex_state *ihex) {
|
||||
char *w = ihex_buffer_extended_address(line_buffer,
|
||||
ADDRESS_HIGH_BYTES(ihex->address),
|
||||
IHEX_EXTENDED_LINEAR_ADDRESS_RECORD);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
ihex->flags &= ~IHEX_FLAG_ADDRESS_OVERFLOW;
|
||||
}
|
||||
}
|
||||
@@ -152,7 +175,7 @@ ihex_write_at_address (struct ihex_state *ihex, ihex_address_t address) {
|
||||
ihex_check_address_overflow(ihex);
|
||||
// flush any existing data
|
||||
char *w = ihex_buffer_data(line_buffer, ihex);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
if ((ihex->address & ADDRESS_HIGH_MASK) != (address & ADDRESS_HIGH_MASK)) {
|
||||
ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW;
|
||||
@@ -160,6 +183,17 @@ ihex_write_at_address (struct ihex_state *ihex, ihex_address_t address) {
|
||||
ihex->flags &= ~IHEX_FLAG_ADDRESS_OVERFLOW;
|
||||
}
|
||||
ihex->address = address;
|
||||
ihex_set_output_line_length(ihex, ihex->line_length);
|
||||
}
|
||||
|
||||
void
|
||||
ihex_set_output_line_length (struct ihex_state *ihex, uint8_t line_length) {
|
||||
if (line_length > IHEX_LINE_MAX_LENGTH) {
|
||||
line_length = IHEX_LINE_MAX_LENGTH;
|
||||
} else if (!line_length) {
|
||||
line_length = IHEX_DEFAULT_OUTPUT_LINE_LENGTH;
|
||||
}
|
||||
ihex->line_length = line_length;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -170,17 +204,17 @@ ihex_write_at_segment (struct ihex_state *ihex, ihex_segment_t segment, ihex_add
|
||||
char *w = ihex_buffer_extended_address(line_buffer,
|
||||
(ihex->segment = segment),
|
||||
IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ihex_write_byte (struct ihex_state *ihex, uint8_t byte) {
|
||||
ihex->data[(ihex->length)++] = byte;
|
||||
if (ihex->length == IHEX_LINE_LENGTH) {
|
||||
if (ihex->length >= ihex->line_length) {
|
||||
ihex_check_address_overflow(ihex);
|
||||
char *w = ihex_buffer_data(line_buffer, ihex);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,17 +224,17 @@ ihex_end_write (struct ihex_state *ihex) {
|
||||
if (ihex->length) {
|
||||
ihex_check_address_overflow(ihex);
|
||||
w = ihex_buffer_data(line_buffer, ihex);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
w = ihex_buffer_end_of_file(line_buffer);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
|
||||
void
|
||||
ihex_write_bytes (struct ihex_state *ihex, uint8_t *data, unsigned int count) {
|
||||
uint8_t *r = data;
|
||||
while (count) {
|
||||
unsigned int i = IHEX_LINE_LENGTH - ihex->length;
|
||||
unsigned int i = ihex->line_length - ihex->length;
|
||||
if (i) {
|
||||
uint8_t *w = &(ihex->data[ihex->length]);
|
||||
i = (i > count) ? count : i;
|
||||
@@ -210,11 +244,175 @@ ihex_write_bytes (struct ihex_state *ihex, uint8_t *data, unsigned int count) {
|
||||
*w++ = *r++;
|
||||
} while (--i);
|
||||
}
|
||||
if (ihex->length == IHEX_LINE_LENGTH) {
|
||||
if (ihex->length >= ihex->line_length) {
|
||||
char *w;
|
||||
ihex_check_address_overflow(ihex);
|
||||
w = ihex_buffer_data(line_buffer, ihex);
|
||||
ihex_flush_buffer(line_buffer, w);
|
||||
ihex_flush_buffer(ihex, line_buffer, w);
|
||||
}
|
||||
} while (count);
|
||||
}
|
||||
|
||||
#endif // !KK_IHEX_DISABLE_WRITING
|
||||
|
||||
#ifndef KK_IHEX_DISABLE_READING
|
||||
|
||||
void
|
||||
ihex_begin_read (struct ihex_state *ihex) {
|
||||
ihex->line_length = 0;
|
||||
ihex->length = 0;
|
||||
ihex->flags = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ihex_read_at_address (struct ihex_state *ihex, ihex_address_t address) {
|
||||
ihex_begin_read(ihex);
|
||||
ihex->address = address;
|
||||
}
|
||||
|
||||
void
|
||||
ihex_read_at_segment (struct ihex_state *ihex, ihex_segment_t segment) {
|
||||
ihex_begin_read(ihex);
|
||||
ihex->address = 0;
|
||||
ihex->segment = segment;
|
||||
}
|
||||
|
||||
void
|
||||
ihex_end_read (struct ihex_state *ihex) {
|
||||
enum ihex_record_type type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK;
|
||||
unsigned int len = ihex->length;
|
||||
if (len == 0 && type == IHEX_DATA_RECORD) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
// compute checksum
|
||||
const uint8_t *r = ihex->data;
|
||||
unsigned int sum = len + type + (ihex->address & 0x00FFU) +
|
||||
((ihex->address & 0xFF00U) >> 8);
|
||||
while (len--) {
|
||||
sum += *r++;
|
||||
}
|
||||
ihex->data[IHEX_LINE_MAX_LENGTH] ^= 0x100U - (sum & 0xFF);
|
||||
len = ihex->length;
|
||||
}
|
||||
if (ihex_data_read(ihex, type, ihex->data[IHEX_LINE_MAX_LENGTH] != 0)) {
|
||||
if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) {
|
||||
ihex_address_t addr = ihex->address & 0xFFFFU;
|
||||
addr |= ((ihex_address_t) ihex->data[0]) << 24;
|
||||
addr |= ((ihex_address_t) ihex->data[1]) << 16;
|
||||
ihex->address = addr;
|
||||
} else if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) {
|
||||
ihex->segment = ihex->data[0] << 8 | ihex->data[1];
|
||||
}
|
||||
}
|
||||
ihex_begin_read(ihex);
|
||||
}
|
||||
|
||||
void
|
||||
ihex_read_byte (struct ihex_state *ihex, char byte) {
|
||||
enum ihex_read_state state = (ihex->flags & IHEX_READ_STATE_MASK);
|
||||
ihex->flags ^= state; // turn off the old state
|
||||
state >>= IHEX_READ_STATE_OFFSET;
|
||||
if (state && state != READ_WAIT_FOR_START) {
|
||||
int n;
|
||||
if (byte >= '0' && byte <= '9') {
|
||||
n = byte - '0';
|
||||
} else if (byte >= 'A' && byte <= 'F') {
|
||||
n = byte - ('A' - 10);
|
||||
} else if (byte >= 'a' && byte <= 'f') {
|
||||
n = byte - ('a' - 10);
|
||||
} else if (byte == '\n' || byte == '\r' || byte == '\0') {
|
||||
ihex_end_read(ihex);
|
||||
return;
|
||||
} else {
|
||||
goto save_read_state;
|
||||
}
|
||||
if ((state & 1) == (READ_COUNT_HIGH & 1)) {
|
||||
n <<= 4;
|
||||
}
|
||||
switch (state) {
|
||||
case READ_COUNT_HIGH:
|
||||
ihex->line_length = n;
|
||||
ihex->data[IHEX_LINE_MAX_LENGTH] = 0xFF;
|
||||
break;
|
||||
case READ_COUNT_LOW:
|
||||
ihex->line_length |= n;
|
||||
if (ihex->line_length > IHEX_LINE_MAX_LENGTH) {
|
||||
ihex_end_read(ihex);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case READ_ADDRESS_MSB_HIGH:
|
||||
ihex->address = (ihex->address & ADDRESS_HIGH_MASK) | (n << 8);
|
||||
break;
|
||||
case READ_ADDRESS_MSB_LOW:
|
||||
ihex->address |= n << 8;
|
||||
break;
|
||||
case READ_ADDRESS_LSB_HIGH:
|
||||
case READ_ADDRESS_LSB_LOW:
|
||||
ihex->address |= n;
|
||||
break;
|
||||
case READ_RECORD_TYPE_HIGH:
|
||||
ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK);
|
||||
case READ_RECORD_TYPE_LOW:
|
||||
if (n > IHEX_READ_RECORD_TYPE_MASK) {
|
||||
// unknown record type
|
||||
state = READ_WAIT_FOR_START;
|
||||
goto save_read_state;
|
||||
}
|
||||
ihex->flags |= (n & IHEX_READ_RECORD_TYPE_MASK);
|
||||
if (ihex->line_length == 0 && state == READ_RECORD_TYPE_LOW) {
|
||||
state = READ_CHECKSUM_HIGH;
|
||||
goto save_read_state;
|
||||
}
|
||||
break;
|
||||
case READ_DATA_HIGH:
|
||||
ihex->data[ihex->length] = n;
|
||||
break;
|
||||
case READ_DATA_LOW: {
|
||||
unsigned int len = ihex->length;
|
||||
ihex->data[len++] |= n;
|
||||
if (len == ihex->line_length) {
|
||||
state = READ_CHECKSUM_HIGH;
|
||||
} else {
|
||||
state = READ_DATA_HIGH;
|
||||
}
|
||||
ihex->length = len;
|
||||
goto save_read_state;
|
||||
}
|
||||
case READ_CHECKSUM_HIGH:
|
||||
ihex->data[IHEX_LINE_MAX_LENGTH] = n;
|
||||
break;
|
||||
case READ_CHECKSUM_LOW:
|
||||
ihex->data[IHEX_LINE_MAX_LENGTH] |= n;
|
||||
ihex_end_read(ihex);
|
||||
default:
|
||||
state = READ_WAIT_FOR_START;
|
||||
goto save_read_state;
|
||||
}
|
||||
++state;
|
||||
} else if (byte == IHEX_START) {
|
||||
state = READ_COUNT_HIGH;
|
||||
}
|
||||
save_read_state:
|
||||
ihex->flags |= state << IHEX_READ_STATE_OFFSET;
|
||||
}
|
||||
|
||||
void
|
||||
ihex_read_bytes (struct ihex_state *ihex, char *data, unsigned int count) {
|
||||
while (count--) {
|
||||
ihex_read_byte(ihex, *data++);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !KK_IHEX_DISABLE_READING
|
||||
|
||||
ihex_address_t
|
||||
ihex_linear_address (struct ihex_state *ihex) {
|
||||
ihex_address_t address = ihex->address;
|
||||
if (ihex->segment) {
|
||||
address += ((ihex_address_t) ihex->segment) << 4;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
169
kk_ihex.h
169
kk_ihex.h
@@ -1,26 +1,48 @@
|
||||
/*
|
||||
* kk_ihex.h: A simple library to read and write Intel HEX data. Intended
|
||||
* mainly for embedded systems.
|
||||
* mainly for embedded systems, and thus somewhat optimised for size at
|
||||
* the expense of error handling and generality.
|
||||
*
|
||||
* Usage
|
||||
*
|
||||
* In order to actually write out data, you must provide an implementation
|
||||
* of the function:
|
||||
* void ihex_flush_buffer(char *buffer, char *eptr);
|
||||
* READING INTEL HEX DATA
|
||||
* ----------------------
|
||||
*
|
||||
* The sequence to write data in IHEX format is then:
|
||||
* To read data in the Intel HEX format, you must perform the actual reading
|
||||
* of bytes using other means (e.g., stdio). The bytes read must then be
|
||||
* passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions
|
||||
* will then call `ihex_data_read`, at which stage the `struct ihex_state`
|
||||
* structure will contain the data along with its address. See below for
|
||||
* details and example implementation of `ihex_data_read`.
|
||||
*
|
||||
* The sequence to read data in IHEX format is:
|
||||
* struct ihex_state ihex;
|
||||
* ihex_init(&ihex);
|
||||
* ihex_begin_read(&ihex);
|
||||
* ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes);
|
||||
* ihex_end_read(&ihex);
|
||||
*
|
||||
*
|
||||
* WRITING BINARY DATA AS INTEL HEX
|
||||
* --------------------------------
|
||||
*
|
||||
* In order to write out data, the `ihex_write_at_address` or
|
||||
* `ihex_write_at_segment` functions are used to set the data location,
|
||||
* and then the binary bytes are written with `ihex_write_byte` and/or
|
||||
* `ihex_write_bytes`. The writing functions will then call the function
|
||||
* `ihex_flush_buffer` whenever the internal write buffer needs to be
|
||||
* cleared - it is up to the caller to provide an implementation of
|
||||
* `ihex_flush_buffer` to do the actual writing. See below for details
|
||||
* and an example implementation.
|
||||
*
|
||||
* See the declaration further down for an example implementation.
|
||||
*
|
||||
* The sequence to write data in IHEX format is:
|
||||
* struct ihex_state ihex;
|
||||
* ihex_init(&ihex);
|
||||
* ihex_write_at_address(&ihex, 0);
|
||||
* ihex_write_bytes(&ihex, my_data, length_of_my_data);
|
||||
* ihex_end_write(&ihex);
|
||||
*
|
||||
* These functions will then call `ihex_flush_buffer` as necessary - in
|
||||
* practice each line will be buffered internally. Note that the line buffer
|
||||
* may be overwritten immediately after `ihex_flush_buffer` returns;
|
||||
* it must be copied if it needs to be preserved.
|
||||
*
|
||||
* For outputs larger than 64KiB, 32-bit linear addresses are output. Normally
|
||||
* the initial linear extended address record of zero is NOT written - it can
|
||||
* be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before
|
||||
@@ -30,6 +52,12 @@
|
||||
* new starting address without calling `ihex_end_write` in between.
|
||||
*
|
||||
*
|
||||
* The same `struct ihex_state` may be used either for reading or writing,
|
||||
* but NOT both at the same time. Furthermore, a global output buffer is
|
||||
* used for writing, i.e., multiple threads must not write simultaneously
|
||||
* (but multiple writes may be interleaved).
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2013 Kimmo Kulovesi, http://arkku.com/
|
||||
* Provided with absolutely no warranty, use at your own risk only.
|
||||
* Use and distribute freely, mark modified copies as such.
|
||||
@@ -39,22 +67,30 @@
|
||||
#define KK_IHEX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint_least32_t ihex_address_t;
|
||||
typedef uint_least16_t ihex_segment_t;
|
||||
|
||||
// Maximum number of data bytes per line (applies to both reading and writing!)
|
||||
#define IHEX_LINE_LENGTH 32
|
||||
// Maximum number of data bytes per line (applies to both reading and
|
||||
// writing!); specify 255 to support reading all possible lengths. Less
|
||||
// can be used to limit memory footprint on embedded systems, e.g.,
|
||||
// most programs with IHEX output use 32.
|
||||
#define IHEX_LINE_MAX_LENGTH 255
|
||||
|
||||
// Default number of data bytes written per line
|
||||
#define IHEX_DEFAULT_OUTPUT_LINE_LENGTH 32
|
||||
|
||||
struct ihex_state {
|
||||
ihex_address_t address;
|
||||
ihex_segment_t segment;
|
||||
uint8_t flags;
|
||||
uint8_t line_length;
|
||||
uint8_t length;
|
||||
uint8_t data[IHEX_LINE_LENGTH];
|
||||
uint8_t data[IHEX_LINE_MAX_LENGTH + 1];
|
||||
};
|
||||
|
||||
#define IHEX_FLAG_ADDRESS_OVERFLOW 1 // 16-bit address overflow
|
||||
#define IHEX_FLAG_ADDRESS_OVERFLOW 0x80 // 16-bit address overflow
|
||||
|
||||
enum ihex_record_type {
|
||||
IHEX_DATA_RECORD,
|
||||
@@ -65,10 +101,73 @@ enum ihex_record_type {
|
||||
IHEX_START_LINEAR_ADDRESS_RECORD
|
||||
};
|
||||
|
||||
// Initialise the structure `ihex`
|
||||
void ihex_init(struct ihex_state * const ihex);
|
||||
|
||||
#ifndef KK_IHEX_DISABLE_READING
|
||||
|
||||
/*** INPUT ***/
|
||||
|
||||
void ihex_begin_read(struct ihex_state *ihex);
|
||||
|
||||
// Begin reading at `address` (the lowest 16 bits of which will be ignored)
|
||||
void ihex_read_at_address(struct ihex_state *ihex, ihex_address_t address);
|
||||
|
||||
// Begin reading at `segment`
|
||||
void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment);
|
||||
|
||||
// Read a single byte
|
||||
void ihex_read_byte(struct ihex_state *ihex, char b);
|
||||
|
||||
// Read `count` bytes from `data`
|
||||
void ihex_read_bytes(struct ihex_state *ihex, char *data, unsigned int count);
|
||||
|
||||
// End reading (may call `ihex_data_read` if there is data waiting)
|
||||
void ihex_end_read(struct ihex_state *ihex);
|
||||
|
||||
// Called when a complete line has been read, the record type of which is
|
||||
// passed as `type`. The `ihex` structure will have its fields `data`,
|
||||
// `line_length`, `address`, and `segment` set appropriately. In case
|
||||
// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an
|
||||
// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not
|
||||
// yet parsed - it will be parsed into the `address` or `segment` field
|
||||
// only if this function returns true.
|
||||
//
|
||||
// Possible error cases include checksum mismatch (which is indicated
|
||||
// as an argument), and excessive line length (in case this has been
|
||||
// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated
|
||||
// by `line_length` greater than `length`. Unknown record types and
|
||||
// other erroneous data is usually silently ignored by this minimalistic
|
||||
// parser. (It is recommended to compute a hash over the complete data
|
||||
// once received and verify that.)
|
||||
//
|
||||
// Example implementation:
|
||||
//
|
||||
// bool ihex_data_read(struct ihex_state *ihex,
|
||||
// enum ihex_record_type type, bool error) {
|
||||
// error = error || (ihex->length < ihex->line_length);
|
||||
// if (type == IHEX_DATA_RECORD && !error) {
|
||||
// (void) fseek(outfile, ihex_linear_address(ihex), SEEK_SET);
|
||||
// (void) fwrite(ihex->data, 1, ihex->length, outfile);
|
||||
// } else if (type == IHEX_END_OF_FILE_RECORD) {
|
||||
// (void) fclose(outfile);
|
||||
// }
|
||||
// return !error;
|
||||
// }
|
||||
//
|
||||
extern bool ihex_data_read(struct ihex_state *ihex,
|
||||
enum ihex_record_type type,
|
||||
bool checksum_mismatch);
|
||||
|
||||
#endif // !KK_IHEX_DISABLE_READING
|
||||
#ifndef KK_IHEX_DISABLE_WRITING
|
||||
|
||||
/*** OUTPUT ***/
|
||||
|
||||
// Begin writing at the given 32-bit `address`
|
||||
// (can also be used to skip to a new address without calling `ihex_end_write`)
|
||||
// (can also be used to skip to a new address without calling
|
||||
// `ihex_end_write`); set ihex->line_length after calling to
|
||||
// specify output line length (default 32)
|
||||
void ihex_write_at_address(struct ihex_state *ihex, ihex_address_t address);
|
||||
|
||||
// Write a single byte
|
||||
@@ -80,17 +179,24 @@ void ihex_write_bytes(struct ihex_state *ihex, uint8_t *data, unsigned int count
|
||||
// End writing (flush buffers, write end of file record)
|
||||
void ihex_end_write(struct ihex_state *ihex);
|
||||
|
||||
|
||||
// Implement this to write out (eptr - buffer) bytes from buffer:
|
||||
void ihex_flush_buffer(char *buffer, char *eptr);
|
||||
|
||||
/* Example implementation of ihex_flush_buffer:
|
||||
*
|
||||
* void ihex_flush_buffer(char *buffer, char *eptr) {
|
||||
* *eptr = '\0';
|
||||
* (void) fputs(buffer, stdout);
|
||||
* }
|
||||
*/
|
||||
// Called whenever the global, internal write buffer needs to be flushed by
|
||||
// the write functions. The implementation is NOT provided by this library;
|
||||
// this must be implemented to perform the actual output, i.e., write out
|
||||
// `(eptr - buffer)` bytes from `buffer` (which is not NUL-terminated, but
|
||||
// may be modified to make it thus).
|
||||
//
|
||||
// Example implementation:
|
||||
//
|
||||
// void ihex_flush_buffer(char *buffer, char *eptr) {
|
||||
// *eptr = '\0';
|
||||
// (void) fputs(buffer, stdout);
|
||||
// }
|
||||
//
|
||||
// Note that the contents of `buffer` can become invalid immediately after
|
||||
// this function returns - the data must be copied if it needs to be preserved!
|
||||
//
|
||||
extern void ihex_flush_buffer(struct ihex_state *ihex,
|
||||
char *buffer, char *eptr);
|
||||
|
||||
// As `ihex_write_at_address`, but specify a segment selector. Note that
|
||||
// segments are not automatically incremented when the 16-bit address
|
||||
@@ -101,7 +207,14 @@ void ihex_flush_buffer(char *buffer, char *eptr);
|
||||
void ihex_write_at_segment(struct ihex_state *ihex, ihex_segment_t segment,
|
||||
ihex_address_t address);
|
||||
|
||||
// Set the output line length to `length` - may be safely called only right
|
||||
// after `ihex_write_at_address` or `ihex_write_at_segment`. The maximum
|
||||
// is IHEX_LINE_MAX_LENGTH (which may be changed at compile time).
|
||||
void ihex_set_output_line_length(struct ihex_state *ihex, uint8_t line_length);
|
||||
|
||||
#endif // !KK_IHEX_DISABLE_WRITING
|
||||
|
||||
// Resolve segmented address (if any), return the linear address
|
||||
ihex_address_t ihex_linear_address(struct ihex_state *ihex);
|
||||
|
||||
#endif
|
||||
#endif // !KK_IHEX_H
|
||||
|
||||
Reference in New Issue
Block a user