From fb4bc68ee6180b1e3cded2133d3e1dd593a37b1a Mon Sep 17 00:00:00 2001 From: Kimmo Kulovesi Date: Fri, 27 Dec 2013 01:26:25 +0200 Subject: [PATCH] Implement reading IHEX --- .gitignore | 2 +- Makefile | 9 +- bin2ihex.c | 12 +-- ihex2bin.c | 88 ++++++++++++++++++++ kk_ihex.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++----- kk_ihex.h | 169 ++++++++++++++++++++++++++++++------- 6 files changed, 462 insertions(+), 58 deletions(-) create mode 100644 ihex2bin.c diff --git a/.gitignore b/.gitignore index e7249fd..457b1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ *.o -bbin2ihex bin2ihex +ihex2bin diff --git a/Makefile b/Makefile index 8cda8f4..7da7b8c 100644 --- a/Makefile +++ b/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) diff --git a/bin2ihex.c b/bin2ihex.c index b0553be..22a495c 100644 --- a/bin2ihex.c +++ b/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 #include -//#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); } diff --git a/ihex2bin.c b/ihex2bin.c new file mode 100644 index 0000000..5072b5c --- /dev/null +++ b/ihex2bin.c @@ -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 +#include + +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; +} diff --git a/kk_ihex.c b/kk_ihex.c index 0bc4c05..c89b722 100644 --- a/kk_ihex.c +++ b/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; +} + diff --git a/kk_ihex.h b/kk_ihex.h index 85bc831..2a321f8 100644 --- a/kk_ihex.h +++ b/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 +#include 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