-
Notifications
You must be signed in to change notification settings - Fork 36
/
kk_ihex_read.c
181 lines (167 loc) · 5.29 KB
/
kk_ihex_read.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* 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;
}
}