diff --git a/kk_ihex.h b/kk_ihex.h index c3ee633..bff1576 100644 --- a/kk_ihex.h +++ b/kk_ihex.h @@ -135,11 +135,12 @@ enum ihex_record_type { IHEX_START_LINEAR_ADDRESS_RECORD }; +#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). // -#ifndef IHEX_DISABLE_SEGMENTS #define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) // // Note that segmented addressing with this macro is not strictly adherent to @@ -160,6 +161,23 @@ enum ihex_record_type { #endif +// The newline string (appended to every output line, e.g., "\r\n") +#ifndef IHEX_NEWLINE_STRING +#define IHEX_NEWLINE_STRING "\n" +#endif + +// Define this as 1 to catch truncated lines when reading (they will report +// a checksum error or at the very least insufficient length). The default +// is to ignore truncated lines - this allows for somewhat smaller and faster +// code, and the only potential risk is when there is an error in the +// transmission that only cuts some lines short while not causing any +// checksum errors in non-truncated lines. That scenario seems unlikely, +// and in any case one should use a better checksum (e.g., SHA1/MD5) over +// the whole data if integrity is critical. +#ifndef IHEX_CATCH_TRUNCATED_LINES +#define IHEX_CATCH_TRUNCATED_LINES 0 +#endif + // See kk_ihex_read.h and kk_ihex_write.h for function declarations! #endif // !KK_IHEX_H diff --git a/kk_ihex_read.c b/kk_ihex_read.c index 9e03552..5f3f516 100644 --- a/kk_ihex_read.c +++ b/kk_ihex_read.c @@ -15,8 +15,8 @@ #define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) enum ihex_read_state { - READ_WAIT_FOR_START, - READ_COUNT_HIGH, + READ_WAIT_FOR_START = 0, + READ_COUNT_HIGH = 1, READ_COUNT_LOW, READ_ADDRESS_MSB_HIGH, READ_ADDRESS_MSB_LOW, @@ -95,71 +95,71 @@ ihex_read_byte (struct ihex_state *ihex, int b) { 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 != READ_WAIT_FOR_START) { + if (b != IHEX_START) { 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 == '\n' || b == '\r' || b == '\0') { - ihex_end_read(ihex); - return; } else { + // ignore extra characters goto save_read_state; } - if ((state & 1) == (READ_COUNT_HIGH & 1)) { - // states are arranged alternating high/low nybble - b <<= 4; - } - switch (state) { - case READ_COUNT_HIGH: - ihex->line_length = b; - break; - case READ_COUNT_LOW: - b = ihex->line_length | b; - ihex->line_length = b; - if (b > IHEX_LINE_MAX_LENGTH) { - ihex_end_read(ihex); - return; - } - break; - case READ_ADDRESS_MSB_HIGH: - // clear the low 16 bits of address - ihex->address = (ihex->address & ADDRESS_HIGH_MASK); - case READ_ADDRESS_MSB_LOW: - b <<= 8; - case READ_ADDRESS_LSB_HIGH: - case READ_ADDRESS_LSB_LOW: - ihex->address |= b; - break; - case READ_RECORD_TYPE_HIGH: - ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK); - case READ_RECORD_TYPE_LOW: - if (b > IHEX_READ_RECORD_TYPE_MASK) { - // ignore unknown record type - return; // state has been zeroed - } - ihex->flags |= (b & IHEX_READ_RECORD_TYPE_MASK); - break; - case READ_DATA_HIGH: - ihex->data[ihex->length] = b; - break; - case READ_DATA_LOW: { + { unsigned int len = ihex->length; - ihex->data[len] |= b; - if (len < ihex->line_length) { - ihex->length = len + 1; - state = READ_DATA_HIGH; - goto save_read_state; + if (state & 1) { + // high nybble, store temporarily at end of data: + ihex->data[len] = b << 4; + } else { + // low nybble, combine with stored high nybble: + b = (ihex->data[len] |= b); + switch (state >> 1) { + case (READ_COUNT_LOW >> 1): + // data length + ihex->line_length = b; + if (b > IHEX_LINE_MAX_LENGTH) { + ihex_end_read(ihex); + return; + } + break; + case (READ_ADDRESS_MSB_LOW >> 1): + // high byte of 16-bit address + ihex->address = (ihex->address & ADDRESS_HIGH_MASK); + b <<= 8; + case (READ_ADDRESS_LSB_LOW >> 1): + // low byte of 16-bit address + ihex->address |= b; + break; + case (READ_RECORD_TYPE_LOW >> 1): + // record type + if (b > IHEX_READ_RECORD_TYPE_MASK) { + // skip non-standard 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; // back to READ_DATA_HIGH + goto save_read_state; + } + // end of line (last "data" byte is checksum) + ihex_end_read(ihex); + default: + return; + } + } } - ihex_end_read(ihex); + ++state; } - default: - return; // state has been zeroed - } - ++state; - } else if (b == IHEX_START) { + } else { + // start of record +#if IHEX_CATCH_TRUNCATED_LINES != 0 + ihex_end_read(ihex); +#endif state = READ_COUNT_HIGH; } save_read_state: diff --git a/kk_ihex_write.h b/kk_ihex_write.h index 624cea1..fabe9dc 100644 --- a/kk_ihex_write.h +++ b/kk_ihex_write.h @@ -76,11 +76,6 @@ #define IHEX_MAX_OUTPUT_LINE_LENGTH IHEX_LINE_MAX_LENGTH #endif -// The newline string (appended to every output line) -#ifndef IHEX_NEWLINE_STRING -#define IHEX_NEWLINE_STRING "\n" -#endif - // Initialise the structure `ihex` for writing void ihex_init(struct ihex_state * const ihex);