
commit
14e7cf69fe
11 changed files with 1819 additions and 0 deletions
@ -0,0 +1,42 @@ |
|||||
|
BasedOnStyle: LLVM |
||||
|
ReflowComments: false |
||||
|
|
||||
|
MacroBlockBegin: "^BEGIN_OPERATOR" |
||||
|
MacroBlockEnd: "^END_OPERATOR" |
||||
|
|
||||
|
Language: Cpp |
||||
|
DerivePointerAlignment: true |
||||
|
SortIncludes: Never |
||||
|
PointerAlignment: Left |
||||
|
AlignAfterOpenBracket: AlwaysBreak |
||||
|
AlignOperands: AlignAfterOperator |
||||
|
AlignTrailingComments: true |
||||
|
AllowAllArgumentsOnNextLine: false |
||||
|
AllowAllParametersOfDeclarationOnNextLine: false |
||||
|
AllowShortEnumsOnASingleLine: false |
||||
|
AllowShortFunctionsOnASingleLine: Empty |
||||
|
AllowShortIfStatementsOnASingleLine: Never |
||||
|
AllowShortLoopsOnASingleLine: false |
||||
|
BinPackArguments: false |
||||
|
BinPackParameters: false |
||||
|
ExperimentalAutoDetectBinPacking: true |
||||
|
BreakBeforeBraces: Custom |
||||
|
BraceWrapping: |
||||
|
AfterFunction: true |
||||
|
ColumnLimit: 100 |
||||
|
AlwaysBreakAfterDefinitionReturnType: None |
||||
|
AlwaysBreakAfterReturnType: None |
||||
|
PenaltyBreakBeforeFirstCallParameter: 0 |
||||
|
PenaltyReturnTypeOnItsOwnLine: 1000000 |
||||
|
PenaltyBreakAssignment: 1000000 |
||||
|
PenaltyExcessCharacter: 10 |
||||
|
IndentCaseLabels: true |
||||
|
IndentWidth: 4 |
||||
|
MaxEmptyLinesToKeep: 2 |
||||
|
NamespaceIndentation: All |
||||
|
SpaceAfterTemplateKeyword: false |
||||
|
AccessModifierOffset: -4 |
||||
|
AllowShortBlocksOnASingleLine: Always |
||||
|
IndentPPDirectives: BeforeHash |
||||
|
IndentExternBlock: Indent |
||||
|
Cpp11BracedListStyle: false |
@ -0,0 +1,4 @@ |
|||||
|
*.o |
||||
|
*.d |
||||
|
src/fvhex2bin |
||||
|
src/libfvhex2bin.a |
@ -0,0 +1,20 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2013-2017 Kimmo Kulovesi |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
||||
|
the Software, and to permit persons to whom the Software is furnished to do so, |
||||
|
subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
File diff suppressed because it is too large
Binary file not shown.
@ -0,0 +1,45 @@ |
|||||
|
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 |
||||
|
.DEFAULT_GOAL := all |
||||
|
|
||||
|
all: $(TARGET_EXE) |
||||
|
|
||||
|
lib: $(TARGET_LIB) |
||||
|
|
||||
|
$(TARGET_EXE): $(SRC_EXE) $(TARGET_LIB) |
||||
|
$(CXX) $(LDFLAGS) $(TARGET_LIB) $(STATIC_LINK_LIBS) $(SRC_EXE) -o $@ |
||||
|
|
||||
|
$(TARGET_LIB): $(OBJ_LIB) |
||||
|
$(AR) -cr $@ $^ |
||||
|
|
||||
|
clean: |
||||
|
rm $(OBJ_LIB) $(TARGET_LIB) $(TARGET_EXE) $(DEPENDS) |
||||
|
|
||||
|
|
@ -0,0 +1,194 @@ |
|||||
|
/*
|
||||
|
* 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 <stdbool.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <errno.h> |
||||
|
|
||||
|
#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; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
break; |
||||
|
case 'o': |
||||
|
if (--argc == 0) { |
||||
|
goto invalid_argument; |
||||
|
} |
||||
|
++argv; |
||||
|
if (!(outfile = fopen(*argv, "wb"))) { |
||||
|
goto argument_error; |
||||
|
} |
||||
|
break; |
||||
|
case 'v': |
||||
|
debug_enabled = 1; |
||||
|
break; |
||||
|
case 'h': |
||||
|
case '?': |
||||
|
arg = NULL; |
||||
|
goto usage; |
||||
|
default: |
||||
|
goto invalid_argument; |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
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 <address_offset>]|[-A])" |
||||
|
" [-o <out.bin>] [-i <in.hex>] [-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); |
||||
|
} |
||||
|
|
||||
|
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; |
||||
|
} |
@ -0,0 +1,191 @@ |
|||||
|
/*
|
||||
|
* kk_ihex.h: A simple library for reading and writing the Intel HEX |
||||
|
* or IHEX format. Intended mainly for embedded systems, and thus |
||||
|
* somewhat optimised for size at the expense of error handling and |
||||
|
* generality. |
||||
|
* |
||||
|
* USAGE |
||||
|
* ----- |
||||
|
* |
||||
|
* The library has been split into read and write parts, which use a |
||||
|
* common data structure (`struct ihex_state`), but each can be used |
||||
|
* independently. Include the header `kk_ihex_read.h` for reading, and/or |
||||
|
* the header `kk_ihex_write.h` for writing (and link with their respective |
||||
|
* object files). Both can be used simultaneously - this header defines |
||||
|
* the shared data structures and definitions. |
||||
|
* |
||||
|
* |
||||
|
* READING INTEL HEX DATA |
||||
|
* ---------------------- |
||||
|
* |
||||
|
* 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 the header |
||||
|
* `kk_ihex_read.h` for details and example implementation of `ihex_data_read`. |
||||
|
* |
||||
|
* The sequence to read data in IHEX format is: |
||||
|
* struct ihex_state 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 the header |
||||
|
* `kk_ihex_write.h` 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); |
||||
|
* |
||||
|
* 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 |
||||
|
* writing the first byte. |
||||
|
* |
||||
|
* Gaps in the data may be created by calling `ihex_write_at_address` with the |
||||
|
* 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). |
||||
|
* |
||||
|
* |
||||
|
* CONSERVING MEMORY |
||||
|
* ----------------- |
||||
|
* |
||||
|
* For memory-critical use, you can save additional memory by defining |
||||
|
* `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that |
||||
|
* this limit affects both reading and writing, so the resulting library |
||||
|
* will be unable to read lines with more than this number of data bytes. |
||||
|
* That said, I haven't encountered any IHEX files with more than 32 |
||||
|
* data bytes per line. For write only there is no reason to define the |
||||
|
* maximum as greater than the line length you'll actually be writing, |
||||
|
* e.g., 32 or 16. |
||||
|
* |
||||
|
* If the write functionality is only occasionally used, you can provide |
||||
|
* your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER` |
||||
|
* and providing a `char *ihex_write_buffer` which points to valid storage |
||||
|
* for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first |
||||
|
* call to any IHEX write function to until after the last. |
||||
|
* |
||||
|
* If you are doing both reading and writing, you can define the maximum |
||||
|
* output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will |
||||
|
* decrease the write buffer size, but `struct ihex_state` will still |
||||
|
* use the larger `IHEX_LINE_MAX_LENGTH` for its data storage. |
||||
|
* |
||||
|
* You can also save a few additional bytes by disabling support for |
||||
|
* segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the |
||||
|
* read and write modules need to be build with the same option, as the |
||||
|
* resulting data structures will not be compatible otherwise. To be honest, |
||||
|
* this is a fairly pointless optimisation. |
||||
|
* |
||||
|
* |
||||
|
* Copyright (c) 2013-2019 Kimmo Kulovesi, https://arkku.com/
|
||||
|
* Provided with absolutely no warranty, use at your own risk only. |
||||
|
* Use and distribute freely, mark modified copies as such. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef KK_IHEX_H |
||||
|
#define KK_IHEX_H |
||||
|
|
||||
|
#define KK_IHEX_VERSION "2019-08-07" |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
#ifdef IHEX_USE_STDBOOL |
||||
|
#include <stdbool.h> |
||||
|
typedef bool ihex_bool_t; |
||||
|
#else |
||||
|
typedef uint_fast8_t ihex_bool_t; |
||||
|
#endif |
||||
|
|
||||
|
typedef uint_least32_t ihex_address_t; |
||||
|
typedef uint_least16_t ihex_segment_t; |
||||
|
typedef int ihex_count_t; |
||||
|
|
||||
|
// 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.
|
||||
|
#ifndef IHEX_LINE_MAX_LENGTH |
||||
|
#define IHEX_LINE_MAX_LENGTH 255 |
||||
|
#endif |
||||
|
|
||||
|
enum ihex_flags { |
||||
|
IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow
|
||||
|
}; |
||||
|
typedef uint8_t ihex_flags_t; |
||||
|
|
||||
|
typedef struct ihex_state { |
||||
|
ihex_address_t address; |
||||
|
#ifndef IHEX_DISABLE_SEGMENTS |
||||
|
ihex_segment_t segment; |
||||
|
#endif |
||||
|
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 { |
||||
|
IHEX_DATA_RECORD, |
||||
|
IHEX_END_OF_FILE_RECORD, |
||||
|
IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD, |
||||
|
IHEX_START_SEGMENT_ADDRESS_RECORD, |
||||
|
IHEX_EXTENDED_LINEAR_ADDRESS_RECORD, |
||||
|
IHEX_START_LINEAR_ADDRESS_RECORD |
||||
|
}; |
||||
|
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)) |
||||
|
|
||||
|
#else // IHEX_DISABLE_SEGMENTS:
|
||||
|
|
||||
|
#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" |
||||
|
#endif |
||||
|
|
||||
|
// See kk_ihex_read.h and kk_ihex_write.h for function declarations!
|
||||
|
|
||||
|
#endif // !KK_IHEX_H
|
@ -0,0 +1,181 @@ |
|||||
|
/*
|
||||
|
* kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format. |
||||
|
* |
||||
|
* See the header `kk_ihex.h` for instructions. |
||||
|
* |
||||
|
* Copyright (c) 2013-2019 Kimmo Kulovesi, https://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_read.h" |
||||
|
|
||||
|
#define IHEX_START ':' |
||||
|
|
||||
|
#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) |
||||
|
|
||||
|
enum ihex_read_state { |
||||
|
READ_WAIT_FOR_START = 0, |
||||
|
READ_COUNT_HIGH = 1, |
||||
|
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 |
||||
|
}; |
||||
|
|
||||
|
#define IHEX_READ_RECORD_TYPE_MASK 0x07 |
||||
|
#define IHEX_READ_STATE_MASK 0x78 |
||||
|
#define IHEX_READ_STATE_OFFSET 3 |
||||
|
|
||||
|
void |
||||
|
ihex_begin_read (struct ihex_state * const ihex) { |
||||
|
ihex->address = 0; |
||||
|
#ifndef IHEX_DISABLE_SEGMENTS |
||||
|
ihex->segment = 0; |
||||
|
#endif |
||||
|
ihex->flags = 0; |
||||
|
ihex->line_length = 0; |
||||
|
ihex->length = 0; |
||||
|
} |
||||
|
|
||||
|
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) { |
||||
|
ihex_begin_read(ihex); |
||||
|
ihex->segment = segment; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
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) { |
||||
|
return; |
||||
|
} |
||||
|
{ |
||||
|
// compute and validate checksum
|
||||
|
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) { |
||||
|
sum += *r++; |
||||
|
} |
||||
|
sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum
|
||||
|
} |
||||
|
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); |
||||
|
#ifndef IHEX_DISABLE_SEGMENTS |
||||
|
} else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { |
||||
|
ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
|
ihex->length = 0; |
||||
|
ihex->flags = 0; |
||||
|
} |
||||
|
|
||||
|
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
|
||||
|
state >>= IHEX_READ_STATE_OFFSET; |
||||
|
|
||||
|
if (b >= '0' && b <= '9') { |
||||
|
b -= '0'; |
||||
|
} else if (b >= 'A' && b <= 'F') { |
||||
|
b -= 'A' - 10; |
||||
|
} else if (b >= 'a' && b <= 'f') { |
||||
|
b -= 'a' - 10; |
||||
|
} else if (b == IHEX_START) { |
||||
|
// sync to a new record at any state
|
||||
|
state = READ_COUNT_HIGH; |
||||
|
goto end_read; |
||||
|
} else { |
||||
|
// ignore unknown characters (e.g., extra whitespace)
|
||||
|
goto save_read_state; |
||||
|
} |
||||
|
|
||||
|
if (!(++state & 1)) { |
||||
|
// high nybble, store temporarily at end of data:
|
||||
|
b <<= 4; |
||||
|
ihex->data[len] = b; |
||||
|
} else { |
||||
|
// low nybble, combine with stored high nybble:
|
||||
|
b = (ihex->data[len] |= b); |
||||
|
// 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); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
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) { |
||||
|
while (count > 0) { |
||||
|
ihex_read_byte(ihex, *data++); |
||||
|
--count; |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,117 @@ |
|||||
|
/*
|
||||
|
* kk_ihex_read.h: A simple library for reading Intel HEX data. See |
||||
|
* the accompanying kk_ihex_write.h for IHEX write support. |
||||
|
* |
||||
|
* |
||||
|
* READING INTEL HEX DATA |
||||
|
* ---------------------- |
||||
|
* |
||||
|
* 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_begin_read(&ihex); |
||||
|
* ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); |
||||
|
* ihex_end_read(&ihex); |
||||
|
* |
||||
|
* |
||||
|
* CONSERVING MEMORY |
||||
|
* ----------------- |
||||
|
* |
||||
|
* For memory-critical use, you can save additional memory by defining |
||||
|
* `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that |
||||
|
* this limit affects both reading and writing, so the resulting library |
||||
|
* will be unable to read lines with more than this number of data bytes. |
||||
|
* That said, I haven't encountered any IHEX files with more than 32 |
||||
|
* data bytes per line. |
||||
|
* |
||||
|
* |
||||
|
* Copyright (c) 2013-2019 Kimmo Kulovesi, https://arkku.com/
|
||||
|
* Provided with absolutely no warranty, use at your own risk only. |
||||
|
* Use and distribute freely, mark modified copies as such. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef KK_IHEX_READ_H |
||||
|
#define KK_IHEX_READ_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
#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` (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 `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); |
||||
|
|
||||
|
// 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); |
||||
|
#endif |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
||||
|
#endif // !KK_IHEX_READ_H
|
Loading…
Reference in new issue