-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathintel_hex.c
324 lines (275 loc) · 9.17 KB
/
intel_hex.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/* Intel .hex file parser
* Alex Hirzel <[email protected]>
* May 27, 2012
*
* http://github.com/alhirzel/intel_hex_files
*/
#include "intel_hex.h"
/* States for the simple state machine used in 'slurp_next_intel_hex_record'.
* See the source code for this function for documentation on each. */
enum slurp_state {
SLURP_INIT,
SLURP_READ_COLON_OR_LINE_BREAK,
SLURP_READ_BYTE_COUNT,
SLURP_READ_ADDRESS,
SLURP_READ_RECORD_TYPE,
SLURP_VERIFY_ESA_ADDRESS_ZERO,
SLURP_VERIFY_ESA_BYTE_COUNT_TWO,
SLURP_READ_ESA_DATA,
SLURP_VERIFY_ESA_DATA_FORMAT_IS_PROPER_ADDRESS,
SLURP_VERIFY_SSA_ADDRESS_ZERO,
SLURP_VERIFY_SSA_BYTE_COUNT_FOUR,
SLURP_VERIFY_ELA_ADDRESS_ZERO,
SLURP_VERIFY_ELA_BYTE_COUNT_TWO,
SLURP_VERIFY_SLA_ADDRESS_ZERO,
SLURP_VERIFY_SLA_BYTE_COUNT_FOUR,
SLURP_READ_DATA,
SLURP_READ_CHECKSUM,
SLURP_VERIFY_CHECKSUM
};
/* Local utility functions. (See below for documentation.) */
static enum intel_hex_slurp_error slurp8bits(char (*)(void), uint8_t *, uint16_t *);
static enum intel_hex_slurp_error slurp16bits(char (*)(void), uint16_t *, uint16_t *);
static enum intel_hex_slurp_error slurp_bytes(int, char (*)(void), uint8_t (*)[], uint16_t *);
/* (See header for documentation.) */
enum intel_hex_slurp_error slurp_next_intel_hex_record(char (*slurp_char)(void), struct intel_hex_record *r) {
enum slurp_state state = SLURP_INIT;
enum intel_hex_slurp_error err = SLURP_ERROR_NONE;
uint16_t checksum = 0;
uint8_t checksum_read;
char colon;
/* State machine has two tracking variables:
*
* * state - next state to proceed to assuming no error condition
* * err - error condition triggered by previous state
*
* An error condition will stop the state machine and return the error.
* States should be written to trigger one type of error each. */
while (err == SLURP_ERROR_NONE) {
switch (state) {
/* Entry into state machine. (May be useful to other
* implementations. */
case SLURP_INIT:
state = SLURP_READ_COLON_OR_LINE_BREAK;
break;
/* Reads multiple line break characters sequentially followed by a
* colon character. This frames the ASCII record ahead of the
* remainder of the parsing process. */
case SLURP_READ_COLON_OR_LINE_BREAK:
colon = (*slurp_char)();
if (':' == colon) {
state = SLURP_READ_BYTE_COUNT;
} else if (('\r' == colon) || ('\n' == colon)) {
state = SLURP_READ_COLON_OR_LINE_BREAK;
} else {
err = SLURP_ERROR_PARSING;
}
break;
/* Reads two ASCII character byte count. This is validated after
* reading the record type. */
case SLURP_READ_BYTE_COUNT:
err = slurp8bits(slurp_char, &(r->byte_count), &checksum);
state = SLURP_READ_ADDRESS;
break;
/* Reads four ASCII character address. This is validated after
* reading the record type. */
case SLURP_READ_ADDRESS:
err = slurp16bits(slurp_char, &(r->address), &checksum);
state = SLURP_READ_RECORD_TYPE;
break;
/* Reads two ASCII character record type and branch to proper
* validation depending on the record type's requirements. */
case SLURP_READ_RECORD_TYPE:
err = slurp8bits(slurp_char, &(r->record_type), &checksum);
switch (r->record_type) {
case DATA_RECORD:
case EOF_RECORD: state = SLURP_READ_DATA; break;
case ESA_RECORD: state = SLURP_VERIFY_ESA_ADDRESS_ZERO; break;
case SSA_RECORD: state = SLURP_VERIFY_SSA_ADDRESS_ZERO; break;
case ELA_RECORD: state = SLURP_VERIFY_ELA_ADDRESS_ZERO; break;
case SLA_RECORD: state = SLURP_VERIFY_SLA_ADDRESS_ZERO; break;
}
break;
/* Only reached for ESA record type; throws error if address field
* is nonzero. */
case SLURP_VERIFY_ESA_ADDRESS_ZERO:
if (0 == r->address) {
state = SLURP_VERIFY_ESA_BYTE_COUNT_TWO;
} else {
err = SLURP_ERROR_ESA_ADDRESS_NOT_ZERO;
}
break;
/* Only reached for ESA record type; throws error if byte count
* field is not two. */
case SLURP_VERIFY_ESA_BYTE_COUNT_TWO:
if (2 == r->byte_count) {
state = SLURP_READ_ESA_DATA;
} else {
err = SLURP_ERROR_ESA_BYTE_COUNT_NOT_TWO;
}
break;
/* Only reached for ESA record type; a specific state to reach data
* for ESA is required to simplify structure of the state machine
* at the cost of some code duplication. */
case SLURP_READ_ESA_DATA:
err = slurp_bytes(2, slurp_char, &(r->data), &checksum);
state = SLURP_VERIFY_ESA_DATA_FORMAT_IS_PROPER_ADDRESS;
break;
/* Only reached for ESA record type; throws error if data field has
* a nonzero digit as the last digit. */
case SLURP_VERIFY_ESA_DATA_FORMAT_IS_PROPER_ADDRESS:
if (0 == (r->data[1] & 0x0F)) {
state = SLURP_READ_CHECKSUM;
} else {
err = SLURP_ERROR_ESA_DATA_FORMAT_INVALID;
}
break;
/* Only reached for SSA record type; throws error if address field
* is nonzero. */
case SLURP_VERIFY_SSA_ADDRESS_ZERO:
if (0 == r->address) {
state = SLURP_VERIFY_SSA_BYTE_COUNT_FOUR;
} else {
err = SLURP_ERROR_SSA_ADDRESS_NOT_ZERO;
}
break;
/* Only reached for SSA record type; throws error if byte count
* field is not four. */
case SLURP_VERIFY_SSA_BYTE_COUNT_FOUR:
if (4 == r->byte_count) {
state = SLURP_READ_DATA;
} else {
err = SLURP_ERROR_SSA_BYTE_COUNT_NOT_FOUR;
}
break;
/* Only reached for ELA record type; throws error if address field
* is nonzero. */
case SLURP_VERIFY_ELA_ADDRESS_ZERO:
if (0 == r->address) {
state = SLURP_VERIFY_ELA_BYTE_COUNT_TWO;
} else {
err = SLURP_ERROR_ELA_ADDRESS_NOT_ZERO;
}
break;
/* Only reached for ELA record type; throws error if byte count
* field is not two. */
case SLURP_VERIFY_ELA_BYTE_COUNT_TWO:
if (2 == r->byte_count) {
state = SLURP_READ_DATA;
} else {
err = SLURP_ERROR_ELA_BYTE_COUNT_NOT_TWO;
}
break;
/* Only reached for SLA record type; throws error if address field
* is nonzero. */
case SLURP_VERIFY_SLA_ADDRESS_ZERO:
if (0 == r->address) {
state = SLURP_VERIFY_SLA_BYTE_COUNT_FOUR;
} else {
err = SLURP_ERROR_SLA_ADDRESS_NOT_ZERO;
}
break;
/* Only reached for SLA record type; throws error if byte count
* field is not four. */
case SLURP_VERIFY_SLA_BYTE_COUNT_FOUR:
if (4 == r->byte_count) {
state = SLURP_READ_DATA;
} else {
err = SLURP_ERROR_SLA_BYTE_COUNT_NOT_FOUR;
}
break;
/* Reads pairs of ASCII characters according to the data length
* field. */
case SLURP_READ_DATA:
err = slurp_bytes(r->byte_count, slurp_char, &(r->data), &checksum);
state = SLURP_READ_CHECKSUM;
break;
/* Reads two ASCII character checksum field and adds it in to the
* rest of the checksum. */
case SLURP_READ_CHECKSUM:
err = slurp8bits(slurp_char, &checksum_read, &checksum);
state = SLURP_VERIFY_CHECKSUM;
break;
/* Verifies that the checksum including the final checksum byte is
* zero (this is the checksum condition for a valid record.) */
case SLURP_VERIFY_CHECKSUM:
checksum &= 0xFF;
if (0 == checksum) {
err = SLURP_ERROR_DONE;
} else {
err = SLURP_ERROR_INVALID_CHECKSUM;
}
break;
}
}
/* Return proper value in case of no error. */
if (SLURP_ERROR_DONE == err) {
err = SLURP_ERROR_NONE;
}
return err;
}
/* TODO */
static enum intel_hex_slurp_error slurp8bits(char (*slurp_char)(void), uint8_t *dest, uint16_t *checksum) {
char temp;
int i;
/* TODO */
*((uint8_t *) dest) = 0;
for (i = 4; i >= 0; i -= 4) {
switch ((*slurp_char)()) {
case '0': temp = 0x0; break;
case '1': temp = 0x1; break;
case '2': temp = 0x2; break;
case '3': temp = 0x3; break;
case '4': temp = 0x4; break;
case '5': temp = 0x5; break;
case '6': temp = 0x6; break;
case '7': temp = 0x7; break;
case '8': temp = 0x8; break;
case '9': temp = 0x9; break;
case 'A': temp = 0xA; break;
case 'a': temp = 0xA; break;
case 'B': temp = 0xB; break;
case 'b': temp = 0xB; break;
case 'C': temp = 0xC; break;
case 'c': temp = 0xC; break;
case 'D': temp = 0xD; break;
case 'd': temp = 0xD; break;
case 'E': temp = 0xE; break;
case 'e': temp = 0xE; break;
case 'F': temp = 0xF; break;
case 'f': temp = 0xF; break;
default: return SLURP_ERROR_NON_HEX_CHARACTER;
}
*((uint8_t *) dest) |= temp << i;
}
/* Don't forget to update the checksum! (This is the easiest place to do so.) */
*checksum += *dest;
return SLURP_ERROR_NONE;
}
/* Slurp two 8-bit values and combine them. */
static enum intel_hex_slurp_error slurp16bits(char (*slurp_char)(void), uint16_t *dest, uint16_t *checksum) {
uint8_t b1, b2;
enum intel_hex_slurp_error err;
err = slurp8bits(slurp_char, &b1, checksum);
if (SLURP_ERROR_NONE == err) {
err = slurp8bits(slurp_char, &b2, checksum);
if (SLURP_ERROR_NONE == err) {
*dest = b1;
*dest <<= 8;
*dest |= b2;
}
}
return SLURP_ERROR_NONE;
}
/* TODO */
static enum intel_hex_slurp_error slurp_bytes(int nbytes, char (*slurp_char)(void), uint8_t (*dest)[], uint16_t *checksum) {
enum intel_hex_slurp_error err = SLURP_ERROR_NONE;
int i;
for (i = 0; i < nbytes; i++) {
err = slurp8bits(slurp_char, &((*dest)[i]), checksum);
if (SLURP_ERROR_NONE != err) {
break;
}
}
return err;
}