1 | // Scintilla source code edit control |
2 | /** @file LexHex.cxx |
3 | ** Lexers for Motorola S-Record, Intel HEX and Tektronix extended HEX. |
4 | ** |
5 | ** Written by Markus Heidelberg |
6 | **/ |
7 | // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> |
8 | // The License.txt file describes the conditions under which this software may be distributed. |
9 | |
10 | /* |
11 | * Motorola S-Record |
12 | * =============================== |
13 | * |
14 | * Each record (line) is built as follows: |
15 | * |
16 | * field digits states |
17 | * |
18 | * +----------+ |
19 | * | start | 1 ('S') SCE_HEX_RECSTART |
20 | * +----------+ |
21 | * | type | 1 SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN) |
22 | * +----------+ |
23 | * | count | 2 SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG |
24 | * +----------+ |
25 | * | address | 4/6/8 SCE_HEX_NOADDRESS, SCE_HEX_DATAADDRESS, SCE_HEX_RECCOUNT, SCE_HEX_STARTADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN) |
26 | * +----------+ |
27 | * | data | 0..504/502/500 SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN, SCE_HEX_DATA_EMPTY, (SCE_HEX_DATA_UNKNOWN) |
28 | * +----------+ |
29 | * | checksum | 2 SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG |
30 | * +----------+ |
31 | * |
32 | * |
33 | * Intel HEX |
34 | * =============================== |
35 | * |
36 | * Each record (line) is built as follows: |
37 | * |
38 | * field digits states |
39 | * |
40 | * +----------+ |
41 | * | start | 1 (':') SCE_HEX_RECSTART |
42 | * +----------+ |
43 | * | count | 2 SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG |
44 | * +----------+ |
45 | * | address | 4 SCE_HEX_NOADDRESS, SCE_HEX_DATAADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN) |
46 | * +----------+ |
47 | * | type | 2 SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN) |
48 | * +----------+ |
49 | * | data | 0..510 SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN, SCE_HEX_DATA_EMPTY, SCE_HEX_EXTENDEDADDRESS, SCE_HEX_STARTADDRESS, (SCE_HEX_DATA_UNKNOWN) |
50 | * +----------+ |
51 | * | checksum | 2 SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG |
52 | * +----------+ |
53 | * |
54 | * |
55 | * Folding: |
56 | * |
57 | * Data records (type 0x00), which follow an extended address record (type |
58 | * 0x02 or 0x04), can be folded. The extended address record is the fold |
59 | * point at fold level 0, the corresponding data records are set to level 1. |
60 | * |
61 | * Any record, which is not a data record, sets the fold level back to 0. |
62 | * Any line, which is not a record (blank lines and lines starting with a |
63 | * character other than ':'), leaves the fold level unchanged. |
64 | * |
65 | * |
66 | * Tektronix extended HEX |
67 | * =============================== |
68 | * |
69 | * Each record (line) is built as follows: |
70 | * |
71 | * field digits states |
72 | * |
73 | * +----------+ |
74 | * | start | 1 ('%') SCE_HEX_RECSTART |
75 | * +----------+ |
76 | * | length | 2 SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG |
77 | * +----------+ |
78 | * | type | 1 SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN) |
79 | * +----------+ |
80 | * | checksum | 2 SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG |
81 | * +----------+ |
82 | * | address | 9 SCE_HEX_DATAADDRESS, SCE_HEX_STARTADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN) |
83 | * +----------+ |
84 | * | data | 0..241 SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN |
85 | * +----------+ |
86 | * |
87 | * |
88 | * General notes for all lexers |
89 | * =============================== |
90 | * |
91 | * - Depending on where the helper functions are invoked, some of them have to |
92 | * read beyond the current position. In case of malformed data (record too |
93 | * short), it has to be ensured that this either does not have bad influence |
94 | * or will be captured deliberately. |
95 | * |
96 | * - States in parentheses in the upper format descriptions indicate that they |
97 | * should not appear in a valid hex file. |
98 | * |
99 | * - State SCE_HEX_GARBAGE means garbage data after the intended end of the |
100 | * record, the line is too long then. This state is used in all lexers. |
101 | */ |
102 | |
103 | #include <stdlib.h> |
104 | #include <string.h> |
105 | #include <stdio.h> |
106 | #include <stdarg.h> |
107 | #include <assert.h> |
108 | #include <ctype.h> |
109 | |
110 | #include <string> |
111 | #include <string_view> |
112 | |
113 | #include "ILexer.h" |
114 | #include "Scintilla.h" |
115 | #include "SciLexer.h" |
116 | |
117 | #include "WordList.h" |
118 | #include "LexAccessor.h" |
119 | #include "Accessor.h" |
120 | #include "StyleContext.h" |
121 | #include "CharacterSet.h" |
122 | #include "LexerModule.h" |
123 | |
124 | using namespace Lexilla; |
125 | |
126 | // prototypes for general helper functions |
127 | static inline bool IsNewline(const int ch); |
128 | static int GetHexaNibble(char hd); |
129 | static int GetHexaChar(char hd1, char hd2); |
130 | static int GetHexaChar(Sci_PositionU pos, Accessor &styler); |
131 | static bool ForwardWithinLine(StyleContext &sc, Sci_Position nb = 1); |
132 | static bool PosInSameRecord(Sci_PositionU pos1, Sci_PositionU pos2, Accessor &styler); |
133 | static Sci_Position CountByteCount(Sci_PositionU startPos, Sci_Position uncountedDigits, Accessor &styler); |
134 | static int CalcChecksum(Sci_PositionU startPos, Sci_Position cnt, bool twosCompl, Accessor &styler); |
135 | |
136 | // prototypes for file format specific helper functions |
137 | static Sci_PositionU GetSrecRecStartPosition(Sci_PositionU pos, Accessor &styler); |
138 | static int GetSrecByteCount(Sci_PositionU recStartPos, Accessor &styler); |
139 | static Sci_Position CountSrecByteCount(Sci_PositionU recStartPos, Accessor &styler); |
140 | static int GetSrecAddressFieldSize(Sci_PositionU recStartPos, Accessor &styler); |
141 | static int GetSrecAddressFieldType(Sci_PositionU recStartPos, Accessor &styler); |
142 | static int GetSrecDataFieldType(Sci_PositionU recStartPos, Accessor &styler); |
143 | static Sci_Position GetSrecRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler); |
144 | static int GetSrecChecksum(Sci_PositionU recStartPos, Accessor &styler); |
145 | static int CalcSrecChecksum(Sci_PositionU recStartPos, Accessor &styler); |
146 | |
147 | static Sci_PositionU GetIHexRecStartPosition(Sci_PositionU pos, Accessor &styler); |
148 | static int GetIHexByteCount(Sci_PositionU recStartPos, Accessor &styler); |
149 | static Sci_Position CountIHexByteCount(Sci_PositionU recStartPos, Accessor &styler); |
150 | static int GetIHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler); |
151 | static int GetIHexDataFieldType(Sci_PositionU recStartPos, Accessor &styler); |
152 | static int GetIHexRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler); |
153 | static int GetIHexChecksum(Sci_PositionU recStartPos, Accessor &styler); |
154 | static int CalcIHexChecksum(Sci_PositionU recStartPos, Accessor &styler); |
155 | |
156 | static int GetTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler); |
157 | static Sci_Position CountTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler); |
158 | static int GetTEHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler); |
159 | static int GetTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler); |
160 | static int CalcTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler); |
161 | |
162 | static inline bool IsNewline(const int ch) |
163 | { |
164 | return (ch == '\n' || ch == '\r'); |
165 | } |
166 | |
167 | static int GetHexaNibble(char hd) |
168 | { |
169 | int hexValue = 0; |
170 | |
171 | if (hd >= '0' && hd <= '9') { |
172 | hexValue += hd - '0'; |
173 | } else if (hd >= 'A' && hd <= 'F') { |
174 | hexValue += hd - 'A' + 10; |
175 | } else if (hd >= 'a' && hd <= 'f') { |
176 | hexValue += hd - 'a' + 10; |
177 | } else { |
178 | return -1; |
179 | } |
180 | |
181 | return hexValue; |
182 | } |
183 | |
184 | static int GetHexaChar(char hd1, char hd2) |
185 | { |
186 | int hexValue = 0; |
187 | |
188 | if (hd1 >= '0' && hd1 <= '9') { |
189 | hexValue += 16 * (hd1 - '0'); |
190 | } else if (hd1 >= 'A' && hd1 <= 'F') { |
191 | hexValue += 16 * (hd1 - 'A' + 10); |
192 | } else if (hd1 >= 'a' && hd1 <= 'f') { |
193 | hexValue += 16 * (hd1 - 'a' + 10); |
194 | } else { |
195 | return -1; |
196 | } |
197 | |
198 | if (hd2 >= '0' && hd2 <= '9') { |
199 | hexValue += hd2 - '0'; |
200 | } else if (hd2 >= 'A' && hd2 <= 'F') { |
201 | hexValue += hd2 - 'A' + 10; |
202 | } else if (hd2 >= 'a' && hd2 <= 'f') { |
203 | hexValue += hd2 - 'a' + 10; |
204 | } else { |
205 | return -1; |
206 | } |
207 | |
208 | return hexValue; |
209 | } |
210 | |
211 | static int GetHexaChar(Sci_PositionU pos, Accessor &styler) |
212 | { |
213 | char highNibble, lowNibble; |
214 | |
215 | highNibble = styler.SafeGetCharAt(pos); |
216 | lowNibble = styler.SafeGetCharAt(pos + 1); |
217 | |
218 | return GetHexaChar(highNibble, lowNibble); |
219 | } |
220 | |
221 | // Forward <nb> characters, but abort (and return false) if hitting the line |
222 | // end. Return true if forwarding within the line was possible. |
223 | // Avoids influence on highlighting of the subsequent line if the current line |
224 | // is malformed (too short). |
225 | static bool ForwardWithinLine(StyleContext &sc, Sci_Position nb) |
226 | { |
227 | for (Sci_Position i = 0; i < nb; i++) { |
228 | if (sc.atLineEnd) { |
229 | // line is too short |
230 | sc.SetState(SCE_HEX_DEFAULT); |
231 | sc.Forward(); |
232 | return false; |
233 | } else { |
234 | sc.Forward(); |
235 | } |
236 | } |
237 | |
238 | return true; |
239 | } |
240 | |
241 | // Checks whether the given positions are in the same record. |
242 | static bool PosInSameRecord(Sci_PositionU pos1, Sci_PositionU pos2, Accessor &styler) |
243 | { |
244 | return styler.GetLine(pos1) == styler.GetLine(pos2); |
245 | } |
246 | |
247 | // Count the number of digit pairs from <startPos> till end of record, ignoring |
248 | // <uncountedDigits> digits. |
249 | // If the record is too short, a negative count may be returned. |
250 | static Sci_Position CountByteCount(Sci_PositionU startPos, Sci_Position uncountedDigits, Accessor &styler) |
251 | { |
252 | Sci_Position cnt; |
253 | Sci_PositionU pos; |
254 | |
255 | pos = startPos; |
256 | |
257 | while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) { |
258 | pos++; |
259 | } |
260 | |
261 | // number of digits in this line minus number of digits of uncounted fields |
262 | cnt = static_cast<Sci_Position>(pos - startPos) - uncountedDigits; |
263 | |
264 | // Prepare round up if odd (digit pair incomplete), this way the byte |
265 | // count is considered to be valid if the checksum is incomplete. |
266 | if (cnt >= 0) { |
267 | cnt++; |
268 | } |
269 | |
270 | // digit pairs |
271 | cnt /= 2; |
272 | |
273 | return cnt; |
274 | } |
275 | |
276 | // Calculate the checksum of the record. |
277 | // <startPos> is the position of the first character of the starting digit |
278 | // pair, <cnt> is the number of digit pairs. |
279 | static int CalcChecksum(Sci_PositionU startPos, Sci_Position cnt, bool twosCompl, Accessor &styler) |
280 | { |
281 | int cs = 0; |
282 | |
283 | for (Sci_PositionU pos = startPos; pos < startPos + cnt; pos += 2) { |
284 | int val = GetHexaChar(pos, styler); |
285 | |
286 | if (val < 0) { |
287 | return val; |
288 | } |
289 | |
290 | // overflow does not matter |
291 | cs += val; |
292 | } |
293 | |
294 | if (twosCompl) { |
295 | // low byte of two's complement |
296 | return -cs & 0xFF; |
297 | } else { |
298 | // low byte of one's complement |
299 | return ~cs & 0xFF; |
300 | } |
301 | } |
302 | |
303 | // Get the position of the record "start" field (first character in line) in |
304 | // the record around position <pos>. |
305 | static Sci_PositionU GetSrecRecStartPosition(Sci_PositionU pos, Accessor &styler) |
306 | { |
307 | while (styler.SafeGetCharAt(pos) != 'S') { |
308 | pos--; |
309 | } |
310 | |
311 | return pos; |
312 | } |
313 | |
314 | // Get the value of the "byte count" field, it counts the number of bytes in |
315 | // the subsequent fields ("address", "data" and "checksum" fields). |
316 | static int GetSrecByteCount(Sci_PositionU recStartPos, Accessor &styler) |
317 | { |
318 | int val; |
319 | |
320 | val = GetHexaChar(recStartPos + 2, styler); |
321 | if (val < 0) { |
322 | val = 0; |
323 | } |
324 | |
325 | return val; |
326 | } |
327 | |
328 | // Count the number of digit pairs for the "address", "data" and "checksum" |
329 | // fields in this record. Has to be equal to the "byte count" field value. |
330 | // If the record is too short, a negative count may be returned. |
331 | static Sci_Position CountSrecByteCount(Sci_PositionU recStartPos, Accessor &styler) |
332 | { |
333 | return CountByteCount(recStartPos, 4, styler); |
334 | } |
335 | |
336 | // Get the size of the "address" field. |
337 | static int GetSrecAddressFieldSize(Sci_PositionU recStartPos, Accessor &styler) |
338 | { |
339 | switch (styler.SafeGetCharAt(recStartPos + 1)) { |
340 | case '0': |
341 | case '1': |
342 | case '5': |
343 | case '9': |
344 | return 2; // 16 bit |
345 | |
346 | case '2': |
347 | case '6': |
348 | case '8': |
349 | return 3; // 24 bit |
350 | |
351 | case '3': |
352 | case '7': |
353 | return 4; // 32 bit |
354 | |
355 | default: |
356 | return 0; |
357 | } |
358 | } |
359 | |
360 | // Get the type of the "address" field content. |
361 | static int GetSrecAddressFieldType(Sci_PositionU recStartPos, Accessor &styler) |
362 | { |
363 | switch (styler.SafeGetCharAt(recStartPos + 1)) { |
364 | case '0': |
365 | return SCE_HEX_NOADDRESS; |
366 | |
367 | case '1': |
368 | case '2': |
369 | case '3': |
370 | return SCE_HEX_DATAADDRESS; |
371 | |
372 | case '5': |
373 | case '6': |
374 | return SCE_HEX_RECCOUNT; |
375 | |
376 | case '7': |
377 | case '8': |
378 | case '9': |
379 | return SCE_HEX_STARTADDRESS; |
380 | |
381 | default: // handle possible format extension in the future |
382 | return SCE_HEX_ADDRESSFIELD_UNKNOWN; |
383 | } |
384 | } |
385 | |
386 | // Get the type of the "data" field content. |
387 | static int GetSrecDataFieldType(Sci_PositionU recStartPos, Accessor &styler) |
388 | { |
389 | switch (styler.SafeGetCharAt(recStartPos + 1)) { |
390 | case '0': |
391 | case '1': |
392 | case '2': |
393 | case '3': |
394 | return SCE_HEX_DATA_ODD; |
395 | |
396 | case '5': |
397 | case '6': |
398 | case '7': |
399 | case '8': |
400 | case '9': |
401 | return SCE_HEX_DATA_EMPTY; |
402 | |
403 | default: // handle possible format extension in the future |
404 | return SCE_HEX_DATA_UNKNOWN; |
405 | } |
406 | } |
407 | |
408 | // Get the required size of the "data" field. Useless for block header and |
409 | // ordinary data records (type S0, S1, S2, S3), return the value calculated |
410 | // from the "byte count" and "address field" size in this case. |
411 | static Sci_Position GetSrecRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler) |
412 | { |
413 | switch (styler.SafeGetCharAt(recStartPos + 1)) { |
414 | case '5': |
415 | case '6': |
416 | case '7': |
417 | case '8': |
418 | case '9': |
419 | return 0; |
420 | |
421 | default: |
422 | return GetSrecByteCount(recStartPos, styler) |
423 | - GetSrecAddressFieldSize(recStartPos, styler) |
424 | - 1; // -1 for checksum field |
425 | } |
426 | } |
427 | |
428 | // Get the value of the "checksum" field. |
429 | static int GetSrecChecksum(Sci_PositionU recStartPos, Accessor &styler) |
430 | { |
431 | int byteCount; |
432 | |
433 | byteCount = GetSrecByteCount(recStartPos, styler); |
434 | |
435 | return GetHexaChar(recStartPos + 2 + byteCount * 2, styler); |
436 | } |
437 | |
438 | // Calculate the checksum of the record. |
439 | static int CalcSrecChecksum(Sci_PositionU recStartPos, Accessor &styler) |
440 | { |
441 | Sci_Position byteCount; |
442 | |
443 | byteCount = GetSrecByteCount(recStartPos, styler); |
444 | |
445 | // sum over "byte count", "address" and "data" fields (6..510 digits) |
446 | return CalcChecksum(recStartPos + 2, byteCount * 2, false, styler); |
447 | } |
448 | |
449 | // Get the position of the record "start" field (first character in line) in |
450 | // the record around position <pos>. |
451 | static Sci_PositionU GetIHexRecStartPosition(Sci_PositionU pos, Accessor &styler) |
452 | { |
453 | while (styler.SafeGetCharAt(pos) != ':') { |
454 | pos--; |
455 | } |
456 | |
457 | return pos; |
458 | } |
459 | |
460 | // Get the value of the "byte count" field, it counts the number of bytes in |
461 | // the "data" field. |
462 | static int GetIHexByteCount(Sci_PositionU recStartPos, Accessor &styler) |
463 | { |
464 | int val; |
465 | |
466 | val = GetHexaChar(recStartPos + 1, styler); |
467 | if (val < 0) { |
468 | val = 0; |
469 | } |
470 | |
471 | return val; |
472 | } |
473 | |
474 | // Count the number of digit pairs for the "data" field in this record. Has to |
475 | // be equal to the "byte count" field value. |
476 | // If the record is too short, a negative count may be returned. |
477 | static Sci_Position CountIHexByteCount(Sci_PositionU recStartPos, Accessor &styler) |
478 | { |
479 | return CountByteCount(recStartPos, 11, styler); |
480 | } |
481 | |
482 | // Get the type of the "address" field content. |
483 | static int GetIHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler) |
484 | { |
485 | if (!PosInSameRecord(recStartPos, recStartPos + 7, styler)) { |
486 | // malformed (record too short) |
487 | // type cannot be determined |
488 | return SCE_HEX_ADDRESSFIELD_UNKNOWN; |
489 | } |
490 | |
491 | switch (GetHexaChar(recStartPos + 7, styler)) { |
492 | case 0x00: |
493 | return SCE_HEX_DATAADDRESS; |
494 | |
495 | case 0x01: |
496 | case 0x02: |
497 | case 0x03: |
498 | case 0x04: |
499 | case 0x05: |
500 | return SCE_HEX_NOADDRESS; |
501 | |
502 | default: // handle possible format extension in the future |
503 | return SCE_HEX_ADDRESSFIELD_UNKNOWN; |
504 | } |
505 | } |
506 | |
507 | // Get the type of the "data" field content. |
508 | static int GetIHexDataFieldType(Sci_PositionU recStartPos, Accessor &styler) |
509 | { |
510 | switch (GetHexaChar(recStartPos + 7, styler)) { |
511 | case 0x00: |
512 | return SCE_HEX_DATA_ODD; |
513 | |
514 | case 0x01: |
515 | return SCE_HEX_DATA_EMPTY; |
516 | |
517 | case 0x02: |
518 | case 0x04: |
519 | return SCE_HEX_EXTENDEDADDRESS; |
520 | |
521 | case 0x03: |
522 | case 0x05: |
523 | return SCE_HEX_STARTADDRESS; |
524 | |
525 | default: // handle possible format extension in the future |
526 | return SCE_HEX_DATA_UNKNOWN; |
527 | } |
528 | } |
529 | |
530 | // Get the required size of the "data" field. Useless for an ordinary data |
531 | // record (type 00), return the "byte count" in this case. |
532 | static int GetIHexRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler) |
533 | { |
534 | switch (GetHexaChar(recStartPos + 7, styler)) { |
535 | case 0x01: |
536 | return 0; |
537 | |
538 | case 0x02: |
539 | case 0x04: |
540 | return 2; |
541 | |
542 | case 0x03: |
543 | case 0x05: |
544 | return 4; |
545 | |
546 | default: |
547 | return GetIHexByteCount(recStartPos, styler); |
548 | } |
549 | } |
550 | |
551 | // Get the value of the "checksum" field. |
552 | static int GetIHexChecksum(Sci_PositionU recStartPos, Accessor &styler) |
553 | { |
554 | int byteCount; |
555 | |
556 | byteCount = GetIHexByteCount(recStartPos, styler); |
557 | |
558 | return GetHexaChar(recStartPos + 9 + byteCount * 2, styler); |
559 | } |
560 | |
561 | // Calculate the checksum of the record. |
562 | static int CalcIHexChecksum(Sci_PositionU recStartPos, Accessor &styler) |
563 | { |
564 | int byteCount; |
565 | |
566 | byteCount = GetIHexByteCount(recStartPos, styler); |
567 | |
568 | // sum over "byte count", "address", "type" and "data" fields (8..518 digits) |
569 | return CalcChecksum(recStartPos + 1, 8 + byteCount * 2, true, styler); |
570 | } |
571 | |
572 | |
573 | // Get the value of the "record length" field, it counts the number of digits in |
574 | // the record excluding the percent. |
575 | static int GetTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler) |
576 | { |
577 | int val = GetHexaChar(recStartPos + 1, styler); |
578 | if (val < 0) |
579 | val = 0; |
580 | |
581 | return val; |
582 | } |
583 | |
584 | // Count the number of digits in this record. Has to |
585 | // be equal to the "record length" field value. |
586 | static Sci_Position CountTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler) |
587 | { |
588 | Sci_PositionU pos; |
589 | |
590 | pos = recStartPos+1; |
591 | |
592 | while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) { |
593 | pos++; |
594 | } |
595 | |
596 | return static_cast<Sci_Position>(pos - (recStartPos+1)); |
597 | } |
598 | |
599 | // Get the type of the "address" field content. |
600 | static int GetTEHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler) |
601 | { |
602 | switch (styler.SafeGetCharAt(recStartPos + 3)) { |
603 | case '6': |
604 | return SCE_HEX_DATAADDRESS; |
605 | |
606 | case '8': |
607 | return SCE_HEX_STARTADDRESS; |
608 | |
609 | default: // handle possible format extension in the future |
610 | return SCE_HEX_ADDRESSFIELD_UNKNOWN; |
611 | } |
612 | } |
613 | |
614 | // Get the value of the "checksum" field. |
615 | static int GetTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler) |
616 | { |
617 | return GetHexaChar(recStartPos+4, styler); |
618 | } |
619 | |
620 | // Calculate the checksum of the record (excluding the checksum field). |
621 | static int CalcTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler) |
622 | { |
623 | Sci_PositionU pos = recStartPos +1; |
624 | Sci_PositionU length = GetTEHexDigitCount(recStartPos, styler); |
625 | |
626 | int cs = GetHexaNibble(styler.SafeGetCharAt(pos++));//length |
627 | cs += GetHexaNibble(styler.SafeGetCharAt(pos++));//length |
628 | |
629 | cs += GetHexaNibble(styler.SafeGetCharAt(pos++));//type |
630 | |
631 | pos += 2;// jump over CS field |
632 | |
633 | for (; pos <= recStartPos + length; ++pos) { |
634 | int val = GetHexaNibble(styler.SafeGetCharAt(pos)); |
635 | |
636 | if (val < 0) { |
637 | return val; |
638 | } |
639 | |
640 | // overflow does not matter |
641 | cs += val; |
642 | } |
643 | |
644 | // low byte |
645 | return cs & 0xFF; |
646 | |
647 | } |
648 | |
649 | static void ColouriseSrecDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) |
650 | { |
651 | StyleContext sc(startPos, length, initStyle, styler); |
652 | |
653 | while (sc.More()) { |
654 | Sci_PositionU recStartPos; |
655 | Sci_Position reqByteCount; |
656 | Sci_Position dataFieldSize; |
657 | int byteCount, addrFieldSize, addrFieldType, dataFieldType; |
658 | int cs1, cs2; |
659 | |
660 | switch (sc.state) { |
661 | case SCE_HEX_DEFAULT: |
662 | if (sc.atLineStart && sc.Match('S')) { |
663 | sc.SetState(SCE_HEX_RECSTART); |
664 | } |
665 | ForwardWithinLine(sc); |
666 | break; |
667 | |
668 | case SCE_HEX_RECSTART: |
669 | recStartPos = sc.currentPos - 1; |
670 | addrFieldType = GetSrecAddressFieldType(recStartPos, styler); |
671 | |
672 | if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) { |
673 | sc.SetState(SCE_HEX_RECTYPE_UNKNOWN); |
674 | } else { |
675 | sc.SetState(SCE_HEX_RECTYPE); |
676 | } |
677 | |
678 | ForwardWithinLine(sc); |
679 | break; |
680 | |
681 | case SCE_HEX_RECTYPE: |
682 | case SCE_HEX_RECTYPE_UNKNOWN: |
683 | recStartPos = sc.currentPos - 2; |
684 | byteCount = GetSrecByteCount(recStartPos, styler); |
685 | reqByteCount = GetSrecAddressFieldSize(recStartPos, styler) |
686 | + GetSrecRequiredDataFieldSize(recStartPos, styler) |
687 | + 1; // +1 for checksum field |
688 | |
689 | if (byteCount == CountSrecByteCount(recStartPos, styler) |
690 | && byteCount == reqByteCount) { |
691 | sc.SetState(SCE_HEX_BYTECOUNT); |
692 | } else { |
693 | sc.SetState(SCE_HEX_BYTECOUNT_WRONG); |
694 | } |
695 | |
696 | ForwardWithinLine(sc, 2); |
697 | break; |
698 | |
699 | case SCE_HEX_BYTECOUNT: |
700 | case SCE_HEX_BYTECOUNT_WRONG: |
701 | recStartPos = sc.currentPos - 4; |
702 | addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler); |
703 | addrFieldType = GetSrecAddressFieldType(recStartPos, styler); |
704 | |
705 | sc.SetState(addrFieldType); |
706 | ForwardWithinLine(sc, addrFieldSize * 2); |
707 | break; |
708 | |
709 | case SCE_HEX_NOADDRESS: |
710 | case SCE_HEX_DATAADDRESS: |
711 | case SCE_HEX_RECCOUNT: |
712 | case SCE_HEX_STARTADDRESS: |
713 | case SCE_HEX_ADDRESSFIELD_UNKNOWN: |
714 | recStartPos = GetSrecRecStartPosition(sc.currentPos, styler); |
715 | dataFieldType = GetSrecDataFieldType(recStartPos, styler); |
716 | |
717 | // Using the required size here if possible has the effect that the |
718 | // checksum is highlighted at a fixed position after this field for |
719 | // specific record types, independent on the "byte count" value. |
720 | dataFieldSize = GetSrecRequiredDataFieldSize(recStartPos, styler); |
721 | |
722 | sc.SetState(dataFieldType); |
723 | |
724 | if (dataFieldType == SCE_HEX_DATA_ODD) { |
725 | for (int i = 0; i < dataFieldSize * 2; i++) { |
726 | if ((i & 0x3) == 0) { |
727 | sc.SetState(SCE_HEX_DATA_ODD); |
728 | } else if ((i & 0x3) == 2) { |
729 | sc.SetState(SCE_HEX_DATA_EVEN); |
730 | } |
731 | |
732 | if (!ForwardWithinLine(sc)) { |
733 | break; |
734 | } |
735 | } |
736 | } else { |
737 | ForwardWithinLine(sc, dataFieldSize * 2); |
738 | } |
739 | break; |
740 | |
741 | case SCE_HEX_DATA_ODD: |
742 | case SCE_HEX_DATA_EVEN: |
743 | case SCE_HEX_DATA_EMPTY: |
744 | case SCE_HEX_DATA_UNKNOWN: |
745 | recStartPos = GetSrecRecStartPosition(sc.currentPos, styler); |
746 | cs1 = CalcSrecChecksum(recStartPos, styler); |
747 | cs2 = GetSrecChecksum(recStartPos, styler); |
748 | |
749 | if (cs1 != cs2 || cs1 < 0 || cs2 < 0) { |
750 | sc.SetState(SCE_HEX_CHECKSUM_WRONG); |
751 | } else { |
752 | sc.SetState(SCE_HEX_CHECKSUM); |
753 | } |
754 | |
755 | ForwardWithinLine(sc, 2); |
756 | break; |
757 | |
758 | case SCE_HEX_CHECKSUM: |
759 | case SCE_HEX_CHECKSUM_WRONG: |
760 | case SCE_HEX_GARBAGE: |
761 | // record finished or line too long |
762 | sc.SetState(SCE_HEX_GARBAGE); |
763 | ForwardWithinLine(sc); |
764 | break; |
765 | |
766 | default: |
767 | // prevent endless loop in faulty state |
768 | sc.SetState(SCE_HEX_DEFAULT); |
769 | break; |
770 | } |
771 | } |
772 | sc.Complete(); |
773 | } |
774 | |
775 | static void ColouriseIHexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) |
776 | { |
777 | StyleContext sc(startPos, length, initStyle, styler); |
778 | |
779 | while (sc.More()) { |
780 | Sci_PositionU recStartPos; |
781 | int byteCount, addrFieldType, dataFieldSize, dataFieldType; |
782 | int cs1, cs2; |
783 | |
784 | switch (sc.state) { |
785 | case SCE_HEX_DEFAULT: |
786 | if (sc.atLineStart && sc.Match(':')) { |
787 | sc.SetState(SCE_HEX_RECSTART); |
788 | } |
789 | ForwardWithinLine(sc); |
790 | break; |
791 | |
792 | case SCE_HEX_RECSTART: |
793 | recStartPos = sc.currentPos - 1; |
794 | byteCount = GetIHexByteCount(recStartPos, styler); |
795 | dataFieldSize = GetIHexRequiredDataFieldSize(recStartPos, styler); |
796 | |
797 | if (byteCount == CountIHexByteCount(recStartPos, styler) |
798 | && byteCount == dataFieldSize) { |
799 | sc.SetState(SCE_HEX_BYTECOUNT); |
800 | } else { |
801 | sc.SetState(SCE_HEX_BYTECOUNT_WRONG); |
802 | } |
803 | |
804 | ForwardWithinLine(sc, 2); |
805 | break; |
806 | |
807 | case SCE_HEX_BYTECOUNT: |
808 | case SCE_HEX_BYTECOUNT_WRONG: |
809 | recStartPos = sc.currentPos - 3; |
810 | addrFieldType = GetIHexAddressFieldType(recStartPos, styler); |
811 | |
812 | sc.SetState(addrFieldType); |
813 | ForwardWithinLine(sc, 4); |
814 | break; |
815 | |
816 | case SCE_HEX_NOADDRESS: |
817 | case SCE_HEX_DATAADDRESS: |
818 | case SCE_HEX_ADDRESSFIELD_UNKNOWN: |
819 | recStartPos = sc.currentPos - 7; |
820 | addrFieldType = GetIHexAddressFieldType(recStartPos, styler); |
821 | |
822 | if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) { |
823 | sc.SetState(SCE_HEX_RECTYPE_UNKNOWN); |
824 | } else { |
825 | sc.SetState(SCE_HEX_RECTYPE); |
826 | } |
827 | |
828 | ForwardWithinLine(sc, 2); |
829 | break; |
830 | |
831 | case SCE_HEX_RECTYPE: |
832 | case SCE_HEX_RECTYPE_UNKNOWN: |
833 | recStartPos = sc.currentPos - 9; |
834 | dataFieldType = GetIHexDataFieldType(recStartPos, styler); |
835 | |
836 | // Using the required size here if possible has the effect that the |
837 | // checksum is highlighted at a fixed position after this field for |
838 | // specific record types, independent on the "byte count" value. |
839 | dataFieldSize = GetIHexRequiredDataFieldSize(recStartPos, styler); |
840 | |
841 | sc.SetState(dataFieldType); |
842 | |
843 | if (dataFieldType == SCE_HEX_DATA_ODD) { |
844 | for (int i = 0; i < dataFieldSize * 2; i++) { |
845 | if ((i & 0x3) == 0) { |
846 | sc.SetState(SCE_HEX_DATA_ODD); |
847 | } else if ((i & 0x3) == 2) { |
848 | sc.SetState(SCE_HEX_DATA_EVEN); |
849 | } |
850 | |
851 | if (!ForwardWithinLine(sc)) { |
852 | break; |
853 | } |
854 | } |
855 | } else { |
856 | ForwardWithinLine(sc, dataFieldSize * 2); |
857 | } |
858 | break; |
859 | |
860 | case SCE_HEX_DATA_ODD: |
861 | case SCE_HEX_DATA_EVEN: |
862 | case SCE_HEX_DATA_EMPTY: |
863 | case SCE_HEX_EXTENDEDADDRESS: |
864 | case SCE_HEX_STARTADDRESS: |
865 | case SCE_HEX_DATA_UNKNOWN: |
866 | recStartPos = GetIHexRecStartPosition(sc.currentPos, styler); |
867 | cs1 = CalcIHexChecksum(recStartPos, styler); |
868 | cs2 = GetIHexChecksum(recStartPos, styler); |
869 | |
870 | if (cs1 != cs2 || cs1 < 0 || cs2 < 0) { |
871 | sc.SetState(SCE_HEX_CHECKSUM_WRONG); |
872 | } else { |
873 | sc.SetState(SCE_HEX_CHECKSUM); |
874 | } |
875 | |
876 | ForwardWithinLine(sc, 2); |
877 | break; |
878 | |
879 | case SCE_HEX_CHECKSUM: |
880 | case SCE_HEX_CHECKSUM_WRONG: |
881 | case SCE_HEX_GARBAGE: |
882 | // record finished or line too long |
883 | sc.SetState(SCE_HEX_GARBAGE); |
884 | ForwardWithinLine(sc); |
885 | break; |
886 | |
887 | default: |
888 | // prevent endless loop in faulty state |
889 | sc.SetState(SCE_HEX_DEFAULT); |
890 | break; |
891 | } |
892 | } |
893 | sc.Complete(); |
894 | } |
895 | |
896 | static void FoldIHexDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) |
897 | { |
898 | Sci_PositionU endPos = startPos + length; |
899 | |
900 | Sci_Position lineCurrent = styler.GetLine(startPos); |
901 | int levelCurrent = SC_FOLDLEVELBASE; |
902 | if (lineCurrent > 0) |
903 | levelCurrent = styler.LevelAt(lineCurrent - 1); |
904 | |
905 | Sci_PositionU lineStartNext = styler.LineStart(lineCurrent + 1); |
906 | int levelNext = SC_FOLDLEVELBASE; // default if no specific line found |
907 | |
908 | for (Sci_PositionU i = startPos; i < endPos; i++) { |
909 | bool atEOL = i == (lineStartNext - 1); |
910 | int style = styler.StyleAt(i); |
911 | |
912 | // search for specific lines |
913 | if (style == SCE_HEX_EXTENDEDADDRESS) { |
914 | // extended addres record |
915 | levelNext = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG; |
916 | } else if (style == SCE_HEX_DATAADDRESS |
917 | || (style == SCE_HEX_DEFAULT |
918 | && i == (Sci_PositionU)styler.LineStart(lineCurrent))) { |
919 | // data record or no record start code at all |
920 | if (levelCurrent & SC_FOLDLEVELHEADERFLAG) { |
921 | levelNext = SC_FOLDLEVELBASE + 1; |
922 | } else { |
923 | // continue level 0 or 1, no fold point |
924 | levelNext = levelCurrent; |
925 | } |
926 | } |
927 | |
928 | if (atEOL || (i == endPos - 1)) { |
929 | styler.SetLevel(lineCurrent, levelNext); |
930 | |
931 | lineCurrent++; |
932 | lineStartNext = styler.LineStart(lineCurrent + 1); |
933 | levelCurrent = levelNext; |
934 | levelNext = SC_FOLDLEVELBASE; |
935 | } |
936 | } |
937 | } |
938 | |
939 | static void ColouriseTEHexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) |
940 | { |
941 | StyleContext sc(startPos, length, initStyle, styler); |
942 | |
943 | while (sc.More()) { |
944 | Sci_PositionU recStartPos; |
945 | int digitCount, addrFieldType; |
946 | int cs1, cs2; |
947 | |
948 | switch (sc.state) { |
949 | case SCE_HEX_DEFAULT: |
950 | if (sc.atLineStart && sc.Match('%')) { |
951 | sc.SetState(SCE_HEX_RECSTART); |
952 | } |
953 | ForwardWithinLine(sc); |
954 | break; |
955 | |
956 | case SCE_HEX_RECSTART: |
957 | |
958 | recStartPos = sc.currentPos - 1; |
959 | |
960 | if (GetTEHexDigitCount(recStartPos, styler) == CountTEHexDigitCount(recStartPos, styler)) { |
961 | sc.SetState(SCE_HEX_BYTECOUNT); |
962 | } else { |
963 | sc.SetState(SCE_HEX_BYTECOUNT_WRONG); |
964 | } |
965 | |
966 | ForwardWithinLine(sc, 2); |
967 | break; |
968 | |
969 | case SCE_HEX_BYTECOUNT: |
970 | case SCE_HEX_BYTECOUNT_WRONG: |
971 | recStartPos = sc.currentPos - 3; |
972 | addrFieldType = GetTEHexAddressFieldType(recStartPos, styler); |
973 | |
974 | if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) { |
975 | sc.SetState(SCE_HEX_RECTYPE_UNKNOWN); |
976 | } else { |
977 | sc.SetState(SCE_HEX_RECTYPE); |
978 | } |
979 | |
980 | ForwardWithinLine(sc); |
981 | break; |
982 | |
983 | case SCE_HEX_RECTYPE: |
984 | case SCE_HEX_RECTYPE_UNKNOWN: |
985 | recStartPos = sc.currentPos - 4; |
986 | cs1 = CalcTEHexChecksum(recStartPos, styler); |
987 | cs2 = GetTEHexChecksum(recStartPos, styler); |
988 | |
989 | if (cs1 != cs2 || cs1 < 0 || cs2 < 0) { |
990 | sc.SetState(SCE_HEX_CHECKSUM_WRONG); |
991 | } else { |
992 | sc.SetState(SCE_HEX_CHECKSUM); |
993 | } |
994 | |
995 | ForwardWithinLine(sc, 2); |
996 | break; |
997 | |
998 | |
999 | case SCE_HEX_CHECKSUM: |
1000 | case SCE_HEX_CHECKSUM_WRONG: |
1001 | recStartPos = sc.currentPos - 6; |
1002 | addrFieldType = GetTEHexAddressFieldType(recStartPos, styler); |
1003 | |
1004 | sc.SetState(addrFieldType); |
1005 | ForwardWithinLine(sc, 9); |
1006 | break; |
1007 | |
1008 | case SCE_HEX_DATAADDRESS: |
1009 | case SCE_HEX_STARTADDRESS: |
1010 | case SCE_HEX_ADDRESSFIELD_UNKNOWN: |
1011 | recStartPos = sc.currentPos - 15; |
1012 | digitCount = GetTEHexDigitCount(recStartPos, styler) - 14; |
1013 | |
1014 | sc.SetState(SCE_HEX_DATA_ODD); |
1015 | |
1016 | for (int i = 0; i < digitCount; i++) { |
1017 | if ((i & 0x3) == 0) { |
1018 | sc.SetState(SCE_HEX_DATA_ODD); |
1019 | } else if ((i & 0x3) == 2) { |
1020 | sc.SetState(SCE_HEX_DATA_EVEN); |
1021 | } |
1022 | |
1023 | if (!ForwardWithinLine(sc)) { |
1024 | break; |
1025 | } |
1026 | } |
1027 | break; |
1028 | |
1029 | case SCE_HEX_DATA_ODD: |
1030 | case SCE_HEX_DATA_EVEN: |
1031 | case SCE_HEX_GARBAGE: |
1032 | // record finished or line too long |
1033 | sc.SetState(SCE_HEX_GARBAGE); |
1034 | ForwardWithinLine(sc); |
1035 | break; |
1036 | |
1037 | default: |
1038 | // prevent endless loop in faulty state |
1039 | sc.SetState(SCE_HEX_DEFAULT); |
1040 | break; |
1041 | } |
1042 | } |
1043 | sc.Complete(); |
1044 | } |
1045 | |
1046 | LexerModule lmSrec(SCLEX_SREC, ColouriseSrecDoc, "srec" , 0, NULL); |
1047 | LexerModule lmIHex(SCLEX_IHEX, ColouriseIHexDoc, "ihex" , FoldIHexDoc, NULL); |
1048 | LexerModule lmTEHex(SCLEX_TEHEX, ColouriseTEHexDoc, "tehex" , 0, NULL); |
1049 | |