faster
[fw/altos] / ccdbg-hex.c
1 /*
2  * Copyright © 2008 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ccdbg.h"
20 #include <stdarg.h>
21 #include <ctype.h>
22
23 struct hex_input {
24         FILE    *file;
25         int     line;
26         char    *name;
27 };
28
29 enum hex_read_state {
30         read_marker,
31         read_length,
32         read_address,
33         read_type,
34         read_data,
35         read_checksum,
36         read_newline,
37         read_white,
38         read_done,
39 };
40
41
42 static void
43 ccdbg_hex_error(struct hex_input *input, char *format, ...)
44 {
45         va_list ap;
46
47         va_start(ap, format);
48         fprintf(stderr, "Hex error %s:%d: ", input->name, input->line);
49         vfprintf(stderr, format, ap);
50         fprintf(stderr, "\n");
51         va_end(ap);
52 }
53
54 static void
55 ccdbg_hex_free(struct hex_record *record)
56 {
57         if (!record) return;
58         free(record);
59 }
60
61 static struct hex_record *
62 ccdbg_hex_alloc(uint8_t length)
63 {
64         struct hex_record *record;
65
66         record = calloc(1, sizeof(struct hex_record) + length);
67         record->length = length;
68         return record;
69 }
70
71 static int
72 ishex(char c)
73 {
74         return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
75 }
76
77 static int
78 fromhex(char c)
79 {
80         if (isdigit(c))
81                 return c - '0';
82         if ('a' <= c && c <= 'f')
83                 return c - 'a' + 10;
84         if ('A' <= c && c <= 'F')
85                 return c - 'A' + 10;
86         abort();
87         return 0;
88 }
89
90 static uint8_t
91 ccdbg_hex_checksum(struct hex_record *record)
92 {
93         uint8_t checksum = 0;
94         int i;
95
96         checksum += record->length;
97         checksum += record->address >> 8;
98         checksum += record->address & 0xff;
99         checksum += record->type;
100         for (i = 0; i < record->length; i++)
101                 checksum += record->data[i];
102         return -checksum;
103 }
104
105 static struct hex_record *
106 ccdbg_hex_read_record(struct hex_input *input)
107 {
108         struct hex_record *record = NULL;
109         enum hex_read_state state = read_marker;
110         char c;
111         int nhexbytes;
112         uint32_t hex;
113         uint32_t ndata;
114         uint8_t checksum;
115
116         while (state != read_done) {
117                 c = getc(input->file);
118                 if (c == EOF && state != read_white) {
119                         ccdbg_hex_error(input, "Unexpected EOF");
120                         goto bail;
121                 }
122                 if (c == '\n')
123                         input->line++;
124                 switch (state) {
125                 case read_marker:
126                         if (c != ':') {
127                                 ccdbg_hex_error(input, "Missing ':'");
128                                 goto bail;
129                         }
130                         state = read_length;
131                         nhexbytes = 2;
132                         hex = 0;
133                         break;
134                 case read_length:
135                 case read_address:
136                 case read_type:
137                 case read_data:
138                 case read_checksum:
139                         if (!ishex(c)) {
140                                 ccdbg_hex_error(input, "Non-hex char '%c'",
141                                                 c);
142                                 goto bail;
143                         }
144                         hex = hex << 4 | fromhex(c);
145                         --nhexbytes;
146                         if (nhexbytes != 0)
147                                 break;
148                         
149                         switch (state) {
150                         case read_length:
151                                 record = ccdbg_hex_alloc(hex);
152                                 if (!record) {
153                                         ccdbg_hex_error(input, "Out of memory");
154                                         goto bail;
155                                 }
156                                 state = read_address;
157                                 nhexbytes = 4;
158                                 break;
159                         case read_address:
160                                 record->address = hex;
161                                 state = read_type;
162                                 nhexbytes = 2;
163                                 break;
164                         case read_type:
165                                 record->type = hex;
166                                 state = read_data;
167                                 nhexbytes = 2;
168                                 ndata = 0;
169                                 break;
170                         case read_data:
171                                 record->data[ndata] = hex;
172                                 ndata++;
173                                 nhexbytes = 2;
174                                 break;
175                         case read_checksum:
176                                 record->checksum = hex;
177                                 state = read_newline;
178                                 break;
179                         default:
180                                 break;
181                         }
182                         if (state == read_data)
183                                 if (ndata == record->length) {
184                                         nhexbytes = 2;
185                                         state = read_checksum;
186                                 }
187                         hex = 0;
188                         break;
189                 case read_newline:
190                         if (c != '\n' && c != '\r') {
191                                 ccdbg_hex_error(input, "Missing newline");
192                                 goto bail;
193                         }
194                         state = read_white;
195                         break;
196                 case read_white:
197                         if (!isspace(c)) {
198                                 if (c == '\n')
199                                         input->line--;
200                                 if (c != EOF)
201                                         ungetc(c, input->file);
202                                 state = read_done;
203                         }
204                         break;
205                 case read_done:
206                         break;
207                 }
208         }
209         checksum = ccdbg_hex_checksum(record);
210         if (checksum != record->checksum) {
211                 ccdbg_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n",
212                                 record->checksum, checksum);
213                 goto bail;
214         }
215         return record;
216
217 bail:
218         ccdbg_hex_free(record);
219         return NULL;
220 }
221
222 void
223 ccdbg_hex_file_free(struct hex_file *hex)
224 {
225         int     i;
226
227         if (!hex)
228                 return;
229         for (i = 0; i < hex->nrecord; i++)
230                 ccdbg_hex_free(hex->records[i]);
231         free (hex);
232 }
233
234 static int
235 ccdbg_hex_record_compar(const void *av, const void *bv)
236 {
237         const struct hex_record *a = *(struct hex_record **) av;
238         const struct hex_record *b = *(struct hex_record **) bv;
239
240         return (int) a->address - (int) b->address;
241 }
242
243 struct hex_file *
244 ccdbg_hex_file_read(FILE *file, char *name)
245 {
246         struct hex_input input;
247         struct hex_file *hex = NULL, *newhex;
248         struct hex_record *record;
249         int srecord = 1;
250         int done = 0;
251
252         hex = calloc (1, sizeof (struct hex_file) + sizeof (struct hex_record *));
253         input.name = name;
254         input.line = 1;
255         input.file = file;
256         while (!done) {
257                 record = ccdbg_hex_read_record(&input);
258                 if (!record)
259                         goto bail;
260                 if (hex->nrecord == srecord) {
261                         srecord *= 2;
262                         newhex = realloc(hex, 
263                                          sizeof (struct hex_file) +
264                                          srecord * sizeof (struct hex_record *));
265                         if (!newhex)
266                                 goto bail;
267                         hex = newhex;
268                 }
269                 hex->records[hex->nrecord++] = record;
270                 if (record->type == HEX_RECORD_EOF)
271                         done = 1;
272         }
273         /*
274          * Sort them into increasing addresses, except for EOF
275          */
276         qsort(hex->records, hex->nrecord - 1, sizeof (struct hex_record *),
277               ccdbg_hex_record_compar);
278         return hex;
279
280 bail:
281         ccdbg_hex_file_free(hex);
282         return NULL;
283 }
284