ao-tools: Create general elf and hex library routines
[fw/altos] / ao-tools / lib / ao-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 <stdarg.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "ao-hex.h"
25
26 struct ao_hex_input {
27         FILE    *file;
28         int     line;
29         char    *name;
30 };
31
32 enum ao_hex_read_state {
33         read_marker,
34         read_length,
35         read_address,
36         read_type,
37         read_data,
38         read_checksum,
39         read_newline,
40         read_white,
41         read_done,
42 };
43
44
45 static void
46 ao_hex_error(struct ao_hex_input *input, char *format, ...)
47 {
48         va_list ap;
49
50         va_start(ap, format);
51         fprintf(stderr, "Hex error %s:%d: ", input->name, input->line);
52         vfprintf(stderr, format, ap);
53         fprintf(stderr, "\n");
54         va_end(ap);
55 }
56
57 static void
58 ao_hex_free(struct ao_hex_record *record)
59 {
60         if (!record) return;
61         free(record);
62 }
63
64 static struct ao_hex_record *
65 ao_hex_alloc(uint8_t length)
66 {
67         struct ao_hex_record *record;
68
69         record = calloc(1, sizeof(struct ao_hex_record) + length);
70         record->length = length;
71         return record;
72 }
73
74 static int
75 ishex(char c)
76 {
77         return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
78 }
79
80 static int
81 fromhex(char c)
82 {
83         if (isdigit(c))
84                 return c - '0';
85         if ('a' <= c && c <= 'f')
86                 return c - 'a' + 10;
87         if ('A' <= c && c <= 'F')
88                 return c - 'A' + 10;
89         abort();
90         return 0;
91 }
92
93 static uint8_t
94 ao_hex_checksum(struct ao_hex_record *record)
95 {
96         uint8_t checksum = 0;
97         int i;
98
99         checksum += record->length;
100         checksum += record->address >> 8;
101         checksum += record->address & 0xff;
102         checksum += record->type;
103         for (i = 0; i < record->length; i++)
104                 checksum += record->data[i];
105         return -checksum;
106 }
107
108 static struct ao_hex_record *
109 ao_hex_read_record(struct ao_hex_input *input)
110 {
111         struct ao_hex_record *record = NULL;
112         enum ao_hex_read_state state = read_marker;
113         char c;
114         int nhexbytes;
115         uint32_t hex;
116         uint32_t ndata;
117         uint8_t checksum;
118
119         while (state != read_done) {
120                 c = getc(input->file);
121                 if (c == EOF && state != read_white) {
122                         ao_hex_error(input, "Unexpected EOF");
123                         goto bail;
124                 }
125                 if (c == ' ')
126                         continue;
127                 if (c == '\n')
128                         input->line++;
129                 switch (state) {
130                 case read_marker:
131                         if (c != ':') {
132                                 ao_hex_error(input, "Missing ':'");
133                                 goto bail;
134                         }
135                         state = read_length;
136                         nhexbytes = 2;
137                         hex = 0;
138                         break;
139                 case read_length:
140                 case read_address:
141                 case read_type:
142                 case read_data:
143                 case read_checksum:
144                         if (!ishex(c)) {
145                                 ao_hex_error(input, "Non-hex char '%c'",
146                                                 c);
147                                 goto bail;
148                         }
149                         hex = hex << 4 | fromhex(c);
150                         --nhexbytes;
151                         if (nhexbytes != 0)
152                                 break;
153
154                         switch (state) {
155                         case read_length:
156                                 record = ao_hex_alloc(hex);
157                                 if (!record) {
158                                         ao_hex_error(input, "Out of memory");
159                                         goto bail;
160                                 }
161                                 state = read_address;
162                                 nhexbytes = 4;
163                                 break;
164                         case read_address:
165                                 record->address = hex;
166                                 state = read_type;
167                                 nhexbytes = 2;
168                                 break;
169                         case read_type:
170                                 record->type = hex;
171                                 state = read_data;
172                                 nhexbytes = 2;
173                                 ndata = 0;
174                                 break;
175                         case read_data:
176                                 record->data[ndata] = hex;
177                                 ndata++;
178                                 nhexbytes = 2;
179                                 break;
180                         case read_checksum:
181                                 record->checksum = hex;
182                                 state = read_newline;
183                                 break;
184                         default:
185                                 break;
186                         }
187                         if (state == read_data)
188                                 if (ndata == record->length) {
189                                         nhexbytes = 2;
190                                         state = read_checksum;
191                                 }
192                         hex = 0;
193                         break;
194                 case read_newline:
195                         if (c != '\n' && c != '\r') {
196                                 ao_hex_error(input, "Missing newline");
197                                 goto bail;
198                         }
199                         state = read_white;
200                         break;
201                 case read_white:
202                         if (!isspace(c)) {
203                                 if (c == '\n')
204                                         input->line--;
205                                 if (c != EOF)
206                                         ungetc(c, input->file);
207                                 state = read_done;
208                         }
209                         break;
210                 case read_done:
211                         break;
212                 }
213         }
214         checksum = ao_hex_checksum(record);
215         if (checksum != record->checksum) {
216                 ao_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n",
217                                 record->checksum, checksum);
218                 goto bail;
219         }
220         return record;
221
222 bail:
223         ao_hex_free(record);
224         return NULL;
225 }
226
227 void
228 ao_hex_file_free(struct ao_hex_file *hex)
229 {
230         int     i;
231
232         if (!hex)
233                 return;
234         for (i = 0; i < hex->nrecord; i++)
235                 ao_hex_free(hex->records[i]);
236         free(hex);
237 }
238
239 struct ao_hex_file *
240 ao_hex_file_read(FILE *file, char *name)
241 {
242         struct ao_hex_input input;
243         struct ao_hex_file      *hex = NULL, *newhex;
244         struct ao_hex_record *record;
245         int srecord = 1;
246         int done = 0;
247
248         hex = calloc(sizeof (struct ao_hex_file) + sizeof (struct ao_hex_record *), 1);
249         input.name = name;
250         input.line = 1;
251         input.file = file;
252         while (!done) {
253                 record = ao_hex_read_record(&input);
254                 if (!record)
255                         goto bail;
256                 if (hex->nrecord == srecord) {
257                         srecord *= 2;
258                         newhex = realloc(hex,
259                                          sizeof (struct ao_hex_file) +
260                                          srecord * sizeof (struct ao_hex_record *));
261                         if (!newhex)
262                                 goto bail;
263                         hex = newhex;
264                 }
265                 hex->records[hex->nrecord++] = record;
266                 if (record->type == AO_HEX_RECORD_EOF)
267                         done = 1;
268         }
269         return hex;
270
271 bail:
272         ao_hex_file_free(hex);
273         return NULL;
274 }
275
276 struct ao_hex_image *
277 ao_hex_image_create(struct ao_hex_file *hex)
278 {
279         struct ao_hex_image *image;
280         struct ao_hex_record *record;
281         int i;
282         uint32_t addr;
283         uint32_t base, bound;
284         uint32_t offset;
285         uint32_t extended_addr;
286
287         int length;
288
289         base = 0xffffffff;
290         bound = 0x0;
291         extended_addr = 0;
292         for (i = 0; i < hex->nrecord; i++) {
293                 uint32_t r_bound;
294                 record = hex->records[i];
295                 switch (record->type) {
296                 case 0:
297                         addr = extended_addr + record->address;
298                         r_bound = addr + record->length;
299                         if (addr < base)
300                                 base = addr;
301                         if (r_bound > bound)
302                                 bound = r_bound;
303                         break;
304                 case 1:
305                         break;
306                 case 2:
307                         if (record->length != 2)
308                                 return NULL;
309                         extended_addr = ((record->data[0] << 8) | record->data[1]) << 4;
310                         break;
311                 case 4:
312                         if (record->length != 2)
313                                 return NULL;
314                         extended_addr = ((record->data[0] << 8) | record->data[1]) << 16;
315                         break;
316                 }
317
318         }
319         length = bound - base;
320         image = calloc(sizeof(struct ao_hex_image) + length, 1);
321         if (!image)
322                 return NULL;
323         image->address = base;
324         image->length = length;
325         memset(image->data, 0xff, length);
326         extended_addr = 0;
327         for (i = 0; i < hex->nrecord; i++) {
328                 record = hex->records[i];
329                 switch (record->type) {
330                 case 0:
331                         addr = extended_addr + record->address;
332                         offset = addr - base;
333                         memcpy(image->data + offset, record->data, record->length);
334                         break;
335                 case 1:
336                         break;
337                 case 2:
338                         extended_addr = ((record->data[0] << 8) | record->data[1]) << 4;
339                         break;
340                 case 4:
341                         extended_addr = ((record->data[0] << 8) | record->data[1]) << 16;
342                         break;
343                 }
344         }
345         return image;
346 }
347
348 void
349 ao_hex_image_free(struct ao_hex_image *image)
350 {
351         free(image);
352 }
353
354 int
355 ao_hex_image_equal(struct ao_hex_image *a, struct ao_hex_image *b)
356 {
357         if (a->length != b->length)
358                 return 0;
359         if (memcmp(a->data, b->data, a->length) != 0)
360                 return 0;
361         return 1;
362 }
363
364 struct ao_hex_image *
365 ao_hex_load(char *filename)
366 {
367         FILE *file;
368         struct ao_hex_file      *hex_file;
369         struct ao_hex_image *hex_image;
370
371         file = fopen (filename, "r");
372         if (!file)
373                 return 0;
374         
375         hex_file = ao_hex_file_read(file, filename);
376         fclose(file);
377         if (!hex_file)
378                 return 0;
379         hex_image = ao_hex_image_create(hex_file);
380         if (!hex_image)
381                 return 0;
382         ao_hex_file_free(hex_file);
383         return hex_image;
384 }