Add manual pages for remaining commands.
[fw/altos] / ao-tools / lib / 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 == ' ')
123                         continue;
124                 if (c == '\n')
125                         input->line++;
126                 switch (state) {
127                 case read_marker:
128                         if (c != ':') {
129                                 ccdbg_hex_error(input, "Missing ':'");
130                                 goto bail;
131                         }
132                         state = read_length;
133                         nhexbytes = 2;
134                         hex = 0;
135                         break;
136                 case read_length:
137                 case read_address:
138                 case read_type:
139                 case read_data:
140                 case read_checksum:
141                         if (!ishex(c)) {
142                                 ccdbg_hex_error(input, "Non-hex char '%c'",
143                                                 c);
144                                 goto bail;
145                         }
146                         hex = hex << 4 | fromhex(c);
147                         --nhexbytes;
148                         if (nhexbytes != 0)
149                                 break;
150
151                         switch (state) {
152                         case read_length:
153                                 record = ccdbg_hex_alloc(hex);
154                                 if (!record) {
155                                         ccdbg_hex_error(input, "Out of memory");
156                                         goto bail;
157                                 }
158                                 state = read_address;
159                                 nhexbytes = 4;
160                                 break;
161                         case read_address:
162                                 record->address = hex;
163                                 state = read_type;
164                                 nhexbytes = 2;
165                                 break;
166                         case read_type:
167                                 record->type = hex;
168                                 state = read_data;
169                                 nhexbytes = 2;
170                                 ndata = 0;
171                                 break;
172                         case read_data:
173                                 record->data[ndata] = hex;
174                                 ndata++;
175                                 nhexbytes = 2;
176                                 break;
177                         case read_checksum:
178                                 record->checksum = hex;
179                                 state = read_newline;
180                                 break;
181                         default:
182                                 break;
183                         }
184                         if (state == read_data)
185                                 if (ndata == record->length) {
186                                         nhexbytes = 2;
187                                         state = read_checksum;
188                                 }
189                         hex = 0;
190                         break;
191                 case read_newline:
192                         if (c != '\n' && c != '\r') {
193                                 ccdbg_hex_error(input, "Missing newline");
194                                 goto bail;
195                         }
196                         state = read_white;
197                         break;
198                 case read_white:
199                         if (!isspace(c)) {
200                                 if (c == '\n')
201                                         input->line--;
202                                 if (c != EOF)
203                                         ungetc(c, input->file);
204                                 state = read_done;
205                         }
206                         break;
207                 case read_done:
208                         break;
209                 }
210         }
211         checksum = ccdbg_hex_checksum(record);
212         if (checksum != record->checksum) {
213                 ccdbg_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n",
214                                 record->checksum, checksum);
215                 goto bail;
216         }
217         return record;
218
219 bail:
220         ccdbg_hex_free(record);
221         return NULL;
222 }
223
224 void
225 ccdbg_hex_file_free(struct hex_file *hex)
226 {
227         int     i;
228
229         if (!hex)
230                 return;
231         for (i = 0; i < hex->nrecord; i++)
232                 ccdbg_hex_free(hex->records[i]);
233         free(hex);
234 }
235
236 static int
237 ccdbg_hex_record_compar(const void *av, const void *bv)
238 {
239         const struct hex_record *a = *(struct hex_record **) av;
240         const struct hex_record *b = *(struct hex_record **) bv;
241
242         return (int) a->address - (int) b->address;
243 }
244
245 struct hex_file *
246 ccdbg_hex_file_read(FILE *file, char *name)
247 {
248         struct hex_input input;
249         struct hex_file *hex = NULL, *newhex;
250         struct hex_record *record;
251         int srecord = 1;
252         int done = 0;
253
254         hex = calloc(sizeof (struct hex_file) + sizeof (struct hex_record *), 1);
255         input.name = name;
256         input.line = 1;
257         input.file = file;
258         while (!done) {
259                 record = ccdbg_hex_read_record(&input);
260                 if (!record)
261                         goto bail;
262                 if (hex->nrecord == srecord) {
263                         srecord *= 2;
264                         newhex = realloc(hex,
265                                          sizeof (struct hex_file) +
266                                          srecord * sizeof (struct hex_record *));
267                         if (!newhex)
268                                 goto bail;
269                         hex = newhex;
270                 }
271                 hex->records[hex->nrecord++] = record;
272                 if (record->type == HEX_RECORD_EOF)
273                         done = 1;
274         }
275         /*
276          * Sort them into increasing addresses, except for EOF
277          */
278         qsort(hex->records, hex->nrecord - 1, sizeof (struct hex_record *),
279               ccdbg_hex_record_compar);
280         return hex;
281
282 bail:
283         ccdbg_hex_file_free(hex);
284         return NULL;
285 }
286
287 struct hex_image *
288 ccdbg_hex_image_create(struct hex_file *hex)
289 {
290         struct hex_image *image;
291         struct hex_record *first, *last, *record;
292         int i;
293         uint32_t base, bound;
294         uint32_t offset;
295         int length;
296
297         first = hex->records[0];
298         last = hex->records[hex->nrecord - 2];  /* skip EOF */
299         base = (uint32_t) first->address;
300         bound = (uint32_t) last->address + (uint32_t) last->length;
301         length = bound - base;
302         image = calloc(sizeof(struct hex_image) + length, 1);
303         if (!image)
304                 return NULL;
305         image->address = base;
306         image->length = length;
307         memset(image->data, 0xff, length);
308         for (i = 0; i < hex->nrecord - 1; i++) {
309                 record = hex->records[i];
310                 offset = record->address - base;
311                 memcpy(image->data + offset, record->data, record->length);
312         }
313         return image;
314 }
315
316 void
317 ccdbg_hex_image_free(struct hex_image *image)
318 {
319         free(image);
320 }
321
322 int
323 ccdbg_hex_image_equal(struct hex_image *a, struct hex_image *b)
324 {
325         if (a->length != b->length)
326                 return 0;
327         if (memcmp(a->data, b->data, a->length) != 0)
328                 return 0;
329         return 1;
330 }