Implement reading IHEX

This commit is contained in:
Kimmo Kulovesi
2013-12-27 01:26:25 +02:00
parent 24a1df4e32
commit fb4bc68ee6
6 changed files with 462 additions and 58 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
*.o
bbin2ihex
bin2ihex
ihex2bin

View File

@@ -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)

View File

@@ -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
View 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
View File

@@ -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
View File

@@ -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