From e57077a3d134eb493673da001ad9939926b2b2fc Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 10 Aug 2023 20:18:06 +0200 Subject: [PATCH] Supermogrify the thing into a lib --- .gitignore | 1 + src/Makefile | 19 ++---- src/fvhex2bin.c | 133 ++---------------------------------------- src/kk_ihex.h | 57 +++++++++--------- src/kk_ihex_read.c | 127 ++++++++++++++++++++-------------------- src/kk_ihex_read.h | 106 +++++++++++++++++----------------- src/libfvhex2bin.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ src/libfvhex2bin.h | 3 + 8 files changed, 302 insertions(+), 284 deletions(-) create mode 100644 src/libfvhex2bin.c create mode 100644 src/libfvhex2bin.h diff --git a/.gitignore b/.gitignore index 4c8472f..e16cf7f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.d src/fvhex2bin src/libfvhex2bin.a +/data/flangers.bin diff --git a/src/Makefile b/src/Makefile index feab14a..c19fa3d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,32 +1,19 @@ TARGET_EXE=fvhex2bin TARGET_LIB=lib$(TARGET_EXE).a -# default config -#SYS_PREFIX?=/opt/local - -# System -#SYS_INC_PATH?=$(SYS_PREFIX)/include -#SYS_LIB_PATH?=$(SYS_PREFIX)/lib - CFLAGS+=-std=c99 -MMD -MP -Wno-format-extra-args -Wno-deprecated-declarations -fvisibility=hidden -#CFLAGS+=-isystem$(SYS_INC_PATH) -#LDFLAGS+=-DMACOSX_DEPLOYMENT_TARGET=11.00 -framework Foundation -framework IOKit -framework Security -#STATIC_LINK_LIBS=$(SYS_LIB_PATH)/libusb-1.0.a SRC_ALL=$(wildcard *.c) SRC_EXE=$(TARGET_EXE).c SRC_LIB=$(filter-out $(SRC_EXE), $(SRC_ALL)) - -#OBJ_EXE=$(subst .c,.o,$(SRC_EXE)) OBJ_LIB=$(subst .c,.o,$(SRC_LIB)) - DEPENDS=$(subst .c,.d,$(SRC_ALL)) ifneq ($(MAKECMDGOALS),clean) -include $(DEPENDS) endif -.PHONY: all clean uninstall install +.PHONY: all clean test .DEFAULT_GOAL := all all: $(TARGET_EXE) @@ -39,6 +26,10 @@ $(TARGET_EXE): $(SRC_EXE) $(TARGET_LIB) $(TARGET_LIB): $(OBJ_LIB) $(AR) -cr $@ $^ +test: all + ./fvhex2bin -i ../data/flangers.hex -o ../data/flangers.bin + cmp -l ../data/flangers.bin ../data/flangers.hex.bin.target + clean: rm $(OBJ_LIB) $(TARGET_LIB) $(TARGET_EXE) $(DEPENDS) diff --git a/src/fvhex2bin.c b/src/fvhex2bin.c index ac263cf..c50e890 100644 --- a/src/fvhex2bin.c +++ b/src/fvhex2bin.c @@ -21,69 +21,33 @@ * Distribute freely, mark modified copies as such. */ -#include "kk_ihex_read.h" -#include +#include "libfvhex2bin.h" #include #include #include -#include - -#define AUTODETECT_ADDRESS (~0UL) - -static FILE *outfile; -static unsigned long line_number = 1L; -static unsigned long file_position = 0L; -static unsigned long address_offset = 0UL; -static bool debug_enabled = 0; int main(int argc, char *argv[]) { - struct ihex_state ihex; - FILE *infile = stdin; - ihex_count_t count; - char buf[256]; - - outfile = stdout; + char *infile = NULL; + char *outfile = NULL; while (--argc) { char *arg = *(++argv); if (arg[0] == '-' && arg[1] && arg[2] == '\0') { switch (arg[1]) { - case 'a': - if (--argc == 0) { - goto invalid_argument; - } - ++argv; - errno = 0; - address_offset = strtoul(*argv, &arg, 0); - if (errno || arg == *argv) { - errno = errno ? errno : EINVAL; - goto argument_error; - } - break; - case 'A': - address_offset = AUTODETECT_ADDRESS; - break; case 'i': if (--argc == 0) { goto invalid_argument; } ++argv; - if (!(infile = fopen(*argv, "r"))) { - goto argument_error; - } + infile = *argv; break; case 'o': if (--argc == 0) { goto invalid_argument; } ++argv; - if (!(outfile = fopen(*argv, "wb"))) { - goto argument_error; - } - break; - case 'v': - debug_enabled = 1; + outfile = *argv; break; case 'h': case '?': @@ -97,98 +61,13 @@ int main(int argc, char *argv[]) invalid_argument: (void)fprintf(stderr, "Invalid argument: %s\n", arg); usage: - (void)fprintf(stderr, "kk_ihex " KK_IHEX_VERSION " - Copyright (c) 2013-2015 Kimmo Kulovesi\n"); (void)fprintf( stderr, "Usage: ihex2bin ([-a ]|[-A])" " [-o ] [-i ] [-v]\n"); return arg ? EXIT_FAILURE : EXIT_SUCCESS; - argument_error: - perror(*argv); - return EXIT_FAILURE; - } - - ihex_read_at_address( - &ihex, - (address_offset != AUTODETECT_ADDRESS) ? (ihex_address_t)address_offset : 0); - while (fgets(buf, sizeof(buf), infile)) { - count = (ihex_count_t)strlen(buf); - ihex_read_bytes(&ihex, buf, count); - line_number += (count && buf[count - 1] == '\n'); - } - ihex_end_read(&ihex); - - if (infile != stdin) { - (void)fclose(infile); } + hex2bin(infile, outfile); return EXIT_SUCCESS; } - -ihex_bool_t ihex_data_read(struct ihex_state *ihex, ihex_record_type_t type, ihex_bool_t 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 < address_offset) { - if (address_offset == AUTODETECT_ADDRESS) { - // autodetect initial address - address_offset = address; - if (debug_enabled) { - (void)fprintf(stderr, "Address offset: 0x%lx\n", address_offset); - } - } else { - (void)fprintf(stderr, "Address underflow on line %lu\n", line_number); - exit(EXIT_FAILURE); - } - } - address -= address_offset; - if (address != file_position) { - if (debug_enabled) { - (void)fprintf( - stderr, - "Seeking from 0x%lx to 0x%lx on line %lu\n", - file_position, - address, - line_number); - } - if (outfile == stdout || fseek(outfile, (long)address, SEEK_SET)) { - if (file_position < address) { - // "seek" forward in stdout by writing NUL bytes - do { - (void)fputc('\0', outfile); - } while (++file_position < address); - } else { - 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) { - if (debug_enabled) { - (void)fprintf(stderr, "%lu bytes written\n", file_position); - } - if (outfile != stdout) { - (void)fclose(outfile); - } - outfile = NULL; - } - return true; -} diff --git a/src/kk_ihex.h b/src/kk_ihex.h index ad744fe..80fef7d 100644 --- a/src/kk_ihex.h +++ b/src/kk_ihex.h @@ -111,7 +111,7 @@ #include #ifdef IHEX_USE_STDBOOL -#include + #include typedef bool ihex_bool_t; #else typedef uint_fast8_t ihex_bool_t; @@ -126,23 +126,23 @@ typedef int ihex_count_t; // can be used to limit memory footprint on embedded systems, e.g., // most programs with IHEX output use 32. #ifndef IHEX_LINE_MAX_LENGTH -#define IHEX_LINE_MAX_LENGTH 255 + #define IHEX_LINE_MAX_LENGTH 255 #endif enum ihex_flags { - IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow + IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow }; typedef uint8_t ihex_flags_t; typedef struct ihex_state { - ihex_address_t address; + ihex_address_t address; #ifndef IHEX_DISABLE_SEGMENTS - ihex_segment_t segment; + ihex_segment_t segment; #endif - ihex_flags_t flags; - uint8_t line_length; - uint8_t length; - uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; + ihex_flags_t flags; + uint8_t line_length; + uint8_t length; + uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; } kk_ihex_t; enum ihex_record_type { @@ -157,33 +157,34 @@ typedef uint8_t ihex_record_type_t; #ifndef IHEX_DISABLE_SEGMENTS -// Resolve segmented address (if any). It is the author's recommendation that -// segmented addressing not be used (and indeed the write function of this -// library uses linear 32-bit addressing unless manually overridden). -// -#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) -// -// Note that segmented addressing with the above macro is not strictly adherent -// to the IHEX specification, which mandates that the lowest 16 bits of the -// address and the index of the data byte must be added modulo 64K (i.e., -// at 16 bits precision with wraparound) and the segment address only added -// afterwards. -// -// To implement fully correct segmented addressing, compute the address -// of _each byte_ with its index in `data` as follows: -// -#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) + // Resolve segmented address (if any). It is the author's recommendation that + // segmented addressing not be used (and indeed the write function of this + // library uses linear 32-bit addressing unless manually overridden). + // + #define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) + // + // Note that segmented addressing with the above macro is not strictly adherent + // to the IHEX specification, which mandates that the lowest 16 bits of the + // address and the index of the data byte must be added modulo 64K (i.e., + // at 16 bits precision with wraparound) and the segment address only added + // afterwards. + // + // To implement fully correct segmented addressing, compute the address + // of _each byte_ with its index in `data` as follows: + // + #define IHEX_BYTE_ADDRESS(ihex, byte_index) \ + ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) #else // IHEX_DISABLE_SEGMENTS: -#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) -#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) + #define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) + #define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) #endif // The newline string (appended to every output line, e.g., "\r\n") #ifndef IHEX_NEWLINE_STRING -#define IHEX_NEWLINE_STRING "\n" + #define IHEX_NEWLINE_STRING "\n" #endif // See kk_ihex_read.h and kk_ihex_write.h for function declarations! diff --git a/src/kk_ihex_read.c b/src/kk_ihex_read.c index bea44be..4b8e536 100644 --- a/src/kk_ihex_read.c +++ b/src/kk_ihex_read.c @@ -10,9 +10,17 @@ #include "kk_ihex_read.h" +#include +#include +#include +#include +#include +#include +#include + #define IHEX_START ':' -#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) +#define ADDRESS_HIGH_MASK ((ihex_address_t)0xFFFF0000U) enum ihex_read_state { READ_WAIT_FOR_START = 0, @@ -32,8 +40,8 @@ enum ihex_read_state { #define IHEX_READ_STATE_MASK 0x78 #define IHEX_READ_STATE_OFFSET 3 -void -ihex_begin_read (struct ihex_state * const ihex) { +void ihex_begin_read(struct ihex_state *const ihex) +{ ihex->address = 0; #ifndef IHEX_DISABLE_SEGMENTS ihex->segment = 0; @@ -43,22 +51,22 @@ ihex_begin_read (struct ihex_state * const ihex) { ihex->length = 0; } -void -ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) { +void ihex_read_at_address(struct ihex_state *const ihex, ihex_address_t address) +{ ihex_begin_read(ihex); ihex->address = address; } #ifndef IHEX_DISABLE_SEGMENTS -void -ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) { +void ihex_read_at_segment(struct ihex_state *const ihex, ihex_segment_t segment) +{ ihex_begin_read(ihex); ihex->segment = segment; } #endif -void -ihex_end_read (struct ihex_state * const ihex) { +void ihex_end_read(struct ihex_state *const ihex) +{ uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK; uint_fast8_t sum; if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) { @@ -66,7 +74,7 @@ ihex_end_read (struct ihex_state * const ihex) { } { // compute and validate checksum - const uint8_t * const eptr = ihex->data + sum; + const uint8_t *const eptr = ihex->data + sum; const uint8_t *r = ihex->data; sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU); while (r != eptr) { @@ -74,14 +82,14 @@ ihex_end_read (struct ihex_state * const ihex) { } sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum } - if (ihex_data_read(ihex, type, (uint8_t) sum)) { + if (ihex_data_read(ihex, type, (uint8_t)sum)) { if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) { ihex->address &= 0xFFFFU; - ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) | - (((ihex_address_t) ihex->data[1]) << 16); + ihex->address |= (((ihex_address_t)ihex->data[0]) << 24) | + (((ihex_address_t)ihex->data[1]) << 16); #ifndef IHEX_DISABLE_SEGMENTS } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { - ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); + ihex->segment = (ihex_segment_t)((ihex->data[0] << 8) | ihex->data[1]); #endif } } @@ -89,9 +97,9 @@ ihex_end_read (struct ihex_state * const ihex) { ihex->flags = 0; } -void -ihex_read_byte (struct ihex_state * const ihex, const char byte) { - uint_fast8_t b = (uint_fast8_t) byte; +void ihex_read_byte(struct ihex_state *const ihex, const char byte) +{ + uint_fast8_t b = (uint_fast8_t)byte; uint_fast8_t len = ihex->length; uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK); ihex->flags ^= state; // turn off the old state @@ -122,60 +130,57 @@ ihex_read_byte (struct ihex_state * const ihex, const char byte) { // We already know the lowest bit of `state`, dropping it may produce // smaller code, hence the `>> 1` in switch and its cases. switch (state >> 1) { - default: - // remain in initial state while waiting for : - return; - case (READ_COUNT_LOW >> 1): - // data length - ihex->line_length = b; -#if IHEX_LINE_MAX_LENGTH < 255 - if (b > IHEX_LINE_MAX_LENGTH) { - ihex_end_read(ihex); + default: + // remain in initial state while waiting for : return; - } + case (READ_COUNT_LOW >> 1): + // data length + ihex->line_length = b; +#if IHEX_LINE_MAX_LENGTH < 255 + if (b > IHEX_LINE_MAX_LENGTH) { + ihex_end_read(ihex); + return; + } #endif - break; - case (READ_ADDRESS_MSB_LOW >> 1): - // high byte of 16-bit address - ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address - ihex->address |= ((ihex_address_t) b) << 8U; - break; - case (READ_ADDRESS_LSB_LOW >> 1): - // low byte of 16-bit address - ihex->address |= (ihex_address_t) b; - break; - case (READ_RECORD_TYPE_LOW >> 1): - // record type - if (b & ~IHEX_READ_RECORD_TYPE_MASK) { - // skip unknown record types silently - return; - } - ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; - break; - case (READ_DATA_LOW >> 1): - if (len < ihex->line_length) { - // data byte - ihex->length = len + 1; - state = READ_DATA_HIGH; - goto save_read_state; - } - // end of line (last "data" byte is checksum) - state = READ_WAIT_FOR_START; - end_read: - ihex_end_read(ihex); + break; + case (READ_ADDRESS_MSB_LOW >> 1): + // high byte of 16-bit address + ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address + ihex->address |= ((ihex_address_t)b) << 8U; + break; + case (READ_ADDRESS_LSB_LOW >> 1): + // low byte of 16-bit address + ihex->address |= (ihex_address_t)b; + break; + case (READ_RECORD_TYPE_LOW >> 1): + // record type + if (b & ~IHEX_READ_RECORD_TYPE_MASK) { + // skip unknown record types silently + return; + } + ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; + break; + case (READ_DATA_LOW >> 1): + if (len < ihex->line_length) { + // data byte + ihex->length = len + 1; + state = READ_DATA_HIGH; + goto save_read_state; + } + // end of line (last "data" byte is checksum) + state = READ_WAIT_FOR_START; + end_read: + ihex_end_read(ihex); } } save_read_state: ihex->flags |= state << IHEX_READ_STATE_OFFSET; } -void -ihex_read_bytes (struct ihex_state * restrict ihex, - const char * restrict data, - ihex_count_t count) { +void ihex_read_bytes(struct ihex_state *restrict ihex, const char *restrict data, ihex_count_t count) +{ while (count > 0) { ihex_read_byte(ihex, *data++); --count; } } - diff --git a/src/kk_ihex_read.h b/src/kk_ihex_read.h index 1b776fd..521a54a 100644 --- a/src/kk_ihex_read.h +++ b/src/kk_ihex_read.h @@ -40,75 +40,73 @@ #define KK_IHEX_READ_H #ifdef __cplusplus -#ifndef restrict -#define restrict -#endif + #ifndef restrict + #define restrict + #endif extern "C" { #endif #include "kk_ihex.h" -// Begin reading at address 0 -void ihex_begin_read(struct ihex_state *ihex); + // Begin reading at address 0 + void ihex_begin_read(struct ihex_state *ihex); -// Begin reading at `address` (the lowest 16 bits of which will be ignored); -// this is required only if the high bytes of the 32-bit starting address -// are not specified in the input data and they are non-zero -void ihex_read_at_address(struct ihex_state *ihex, - ihex_address_t address); + // Begin reading at `address` (the lowest 16 bits of which will be ignored); + // this is required only if the high bytes of the 32-bit starting address + // are not specified in the input data and they are non-zero + void ihex_read_at_address(struct ihex_state *ihex, ihex_address_t address); -// Read a single character -void ihex_read_byte(struct ihex_state *ihex, char chr); + // Read a single character + void ihex_read_byte(struct ihex_state *ihex, char chr); -// Read `count` bytes from `data` -void ihex_read_bytes(struct ihex_state * restrict ihex, - const char * restrict data, - ihex_count_t count); + // Read `count` bytes from `data` + void ihex_read_bytes(struct ihex_state *restrict ihex, const char *restrict data, ihex_count_t count); -// End reading (may call `ihex_data_read` if there is data waiting) -void ihex_end_read(struct ihex_state *ihex); + // 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 `ihex_data_read` returns `true`. This allows manual handling -// of extended addresses by parsing the `ihex->data` bytes. -// -// 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 against the source.) -// -// Example implementation: -// -// ihex_bool_t ihex_data_read(struct ihex_state *ihex, -// ihex_record_type_t type, -// ihex_bool_t 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 ihex_bool_t ihex_data_read(struct ihex_state *ihex, - ihex_record_type_t type, - ihex_bool_t checksum_mismatch); + // 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 `ihex_data_read` returns `true`. This allows manual handling + // of extended addresses by parsing the `ihex->data` bytes. + // + // 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 against the source.) + // + // Example implementation: + // + // ihex_bool_t ihex_data_read(struct ihex_state *ihex, + // ihex_record_type_t type, + // ihex_bool_t 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 ihex_bool_t ihex_data_read( + struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t checksum_mismatch); // Begin reading at `segment`; this is required only if the initial segment // is not specified in the input data and it is non-zero. // #ifndef IHEX_DISABLE_SEGMENTS -void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); + void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); #endif #ifdef __cplusplus diff --git a/src/libfvhex2bin.c b/src/libfvhex2bin.c new file mode 100644 index 0000000..7428607 --- /dev/null +++ b/src/libfvhex2bin.c @@ -0,0 +1,140 @@ +/* + * ihex2bin.c: Read Intel HEX format, write binary data. + * + * By default reads from stdin and writes to stdout. The command-line + * options `-i` and `-o` can be used to specify the input and output + * file, respectively. Specifying an output file allows sparse writes. + * + * NOTE: Many Intel HEX files produced by compilers/etc have data + * beginning at an address greater than zero, potentially causing very + * unnecessarily large files to be created. The command-line option + * `-a` can be used to specify the start address of the output file, + * i.e., the value will be subtracted from the IHEX addresses (the + * result must not be negative). + * + * Alternatively, the command-line option `-A` sets the address offset + * to the first address that would be written (i.e., first byte of + * data written will be at address 0). + * + * Copyright (c) 2013-2019 Kimmo Kulovesi, https://arkku.com + * Provided with absolutely no warranty, use at your own risk only. + * Distribute freely, mark modified copies as such. + */ + +#include "kk_ihex_read.h" +#include +#include +#include +#include + +#define AUTODETECT_ADDRESS (~0UL) + + +static unsigned long line_number = 1L; +static unsigned long file_position = 0L; +static unsigned long address_offset = 0UL; +static bool debug_enabled = 0; + + + +static FILE *infile = NULL; +static FILE *outfile = NULL; + +int hex2bin(const char *infilename, const char *outfilename) +{ + int ret = 0; + if (!(infile = fopen(infilename, "r"))) { + return 255; + } + if (!(outfile = fopen(outfilename, "wb"))) { + return 255; + } + + struct ihex_state ihex; + + ihex_count_t count; + char buf[256]; + + ihex_read_at_address( + &ihex, + (address_offset != AUTODETECT_ADDRESS) ? (ihex_address_t)address_offset : 0); + while (fgets(buf, sizeof(buf), infile)) { + count = (ihex_count_t)strlen(buf); + ihex_read_bytes(&ihex, buf, count); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex); + + if (infile != stdin) { + (void)fclose(infile); + } + return ret; +} + +ihex_bool_t ihex_data_read(struct ihex_state *ihex, ihex_record_type_t type, ihex_bool_t 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 < address_offset) { + if (address_offset == AUTODETECT_ADDRESS) { + // autodetect initial address + address_offset = address; + if (debug_enabled) { + (void)fprintf(stderr, "Address offset: 0x%lx\n", address_offset); + } + } else { + (void)fprintf(stderr, "Address underflow on line %lu\n", line_number); + exit(EXIT_FAILURE); + } + } + address -= address_offset; + if (address != file_position) { + if (debug_enabled) { + (void)fprintf( + stderr, + "Seeking from 0x%lx to 0x%lx on line %lu\n", + file_position, + address, + line_number); + } + if (outfile == stdout || fseek(outfile, (long)address, SEEK_SET)) { + if (file_position < address) { + // "seek" forward in stdout by writing NUL bytes + do { + (void)fputc('\0', outfile); + } while (++file_position < address); + } else { + 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) { + if (debug_enabled) { + (void)fprintf(stderr, "%lu bytes written\n", file_position); + } + if (outfile != stdout) { + (void)fclose(outfile); + } + outfile = NULL; + } + return true; +} diff --git a/src/libfvhex2bin.h b/src/libfvhex2bin.h new file mode 100644 index 0000000..52f9526 --- /dev/null +++ b/src/libfvhex2bin.h @@ -0,0 +1,3 @@ +#pragma once + +int hex2bin(const char *infilename, const char *outfile);