altoslib: Hook up idle monitoring data again
[fw/altos] / altoslib / AltosHexfile.java
1 /*
2  * Copyright © 2010 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 package org.altusmetrum.altoslib_11;
20
21 import java.io.*;
22 import java.util.LinkedList;
23 import java.util.Arrays;
24
25 class HexFileInputStream extends PushbackInputStream {
26         public int line;
27
28         public HexFileInputStream(FileInputStream o) {
29                 super(new BufferedInputStream(o));
30                 line = 1;
31         }
32
33         public int read() throws IOException {
34                 int     c = super.read();
35                 if (c == '\n')
36                         line++;
37                 return c;
38         }
39
40         public void unread(int c) throws IOException {
41                 if (c == '\n')
42                         line--;
43                 if (c != -1)
44                         super.unread(c);
45         }
46 }
47
48 class HexRecord implements Comparable<Object> {
49         public int      address;
50         public int      type;
51         public byte     checksum;
52         public byte[]   data;
53
54         static final int NORMAL = 0;
55         static final int EOF = 1;
56         static final int EXTENDED_ADDRESS = 2;
57
58         enum read_state {
59                 marker,
60                 length,
61                 address,
62                 type,
63                 data,
64                 checksum,
65                 newline,
66                 white,
67                 done,
68         }
69
70         boolean ishex(int c) {
71                 if ('0' <= c && c <= '9')
72                         return true;
73                 if ('a' <= c && c <= 'f')
74                         return true;
75                 if ('A' <= c && c <= 'F')
76                         return true;
77                 return false;
78         }
79
80         boolean isspace(int c) {
81                 switch (c) {
82                 case ' ':
83                 case '\t':
84                         return true;
85                 }
86                 return false;
87         }
88
89         int fromhex(int c) {
90                 if ('0' <= c && c <= '9')
91                         return c - '0';
92                 if ('a' <= c && c <= 'f')
93                         return c - 'a' + 10;
94                 if ('A' <= c && c <= 'F')
95                         return c - 'A' + 10;
96                 return -1;
97         }
98
99         public byte checksum() {
100                 byte    got = 0;
101
102                 got += data.length;
103                 got += (address >> 8) & 0xff;
104                 got += (address     ) & 0xff;
105                 got += type;
106                 for (int i = 0; i < data.length; i++)
107                         got += data[i];
108                 return (byte) (-got);
109         }
110
111         public int compareTo(Object other) {
112                 HexRecord       o = (HexRecord) other;
113                 return address - o.address;
114         }
115
116         public String toString() {
117                 return String.format("%04x: %02x (%d)", address, type, data.length);
118         }
119
120         public HexRecord(HexFileInputStream input) throws IOException, EOFException {
121                 read_state      state = read_state.marker;
122                 int             nhexbytes = 0;
123                 int             hex = 0;
124                 int             ndata = 0;
125                 byte            got_checksum;
126
127                 while (state != read_state.done) {
128                         int c = input.read();
129                         if (c < 0 && state != read_state.white && state != read_state.marker)
130                                 throw new IOException(String.format("%d: Unexpected EOF", input.line));
131                         if (c == ' ')
132                                 continue;
133                         switch (state) {
134                         case marker:
135                                 if (c == EOF || c == -1)
136                                         throw new EOFException();
137                                 if (c != ':')
138                                         throw new IOException(String.format ("Missing ':' (got %x)", c));
139                                 state = read_state.length;
140                                 nhexbytes = 2;
141                                 hex = 0;
142                                 break;
143                         case length:
144                         case address:
145                         case type:
146                         case data:
147                         case checksum:
148                                 if(!ishex(c))
149                                         throw new IOException(String.format("Non-hex char '%c'", c));
150                                 hex = hex << 4 | fromhex(c);
151                                 --nhexbytes;
152                                 if (nhexbytes != 0)
153                                         break;
154
155                                 switch (state) {
156                                 case length:
157                                         data = new byte[hex];
158                                         state = read_state.address;
159                                         nhexbytes = 4;
160                                         break;
161                                 case address:
162                                         address = hex;
163                                         state = read_state.type;
164                                         nhexbytes = 2;
165                                         break;
166                                 case type:
167                                         type = hex;
168                                         if (data.length > 0)
169                                                 state = read_state.data;
170                                         else
171                                                 state = read_state.checksum;
172                                         nhexbytes = 2;
173                                         ndata = 0;
174                                         break;
175                                 case data:
176                                         data[ndata] = (byte) hex;
177                                         ndata++;
178                                         nhexbytes = 2;
179                                         if (ndata == data.length)
180                                                 state = read_state.checksum;
181                                         break;
182                                 case checksum:
183                                         checksum = (byte) hex;
184                                         state = read_state.newline;
185                                         break;
186                                 default:
187                                         break;
188                                 }
189                                 hex = 0;
190                                 break;
191                         case newline:
192                                 if (c != '\n' && c != '\r')
193                                         throw new IOException("Missing newline");
194                                 state = read_state.white;
195                                 break;
196                         case white:
197                                 if (!isspace(c)) {
198                                         input.unread(c);
199                                         state = read_state.done;
200                                 }
201                                 break;
202                         case done:
203                                 break;
204                         }
205                 }
206                 got_checksum = checksum();
207                 if (got_checksum != checksum)
208                         throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
209                                                             checksum, got_checksum));
210         }
211 }
212
213 public class AltosHexfile {
214         public int              address;
215         public byte[]           data;
216         LinkedList<AltosHexsym> symlist = new LinkedList<AltosHexsym>();
217
218         public byte get_byte(int a) {
219                 return data[a - address];
220         }
221
222         /* CC1111-based products have the romconfig stuff located
223          * at a fixed address; when the file we load has no symbols,
224          * assume it is one of those and set the symbols appropriately
225          */
226         final static int ao_romconfig_version_addr = 0xa0;
227         final static int ao_romconfig_check_addr = 0xa2;
228         final static int ao_serial_number_addr = 0xa4;
229         final static int ao_radio_cal_addr = 0xa6;
230         final static int ao_usb_descriptors_addr = 0xaa;
231
232         static AltosHexsym[] cc_symbols = {
233                 new AltosHexsym("ao_romconfig_version", ao_romconfig_version_addr),
234                 new AltosHexsym("ao_romconfig_check", ao_romconfig_check_addr),
235                 new AltosHexsym("ao_serial_number", ao_serial_number_addr),
236                 new AltosHexsym("ao_radio_cal", ao_radio_cal_addr),
237                 new AltosHexsym("ao_usb_descriptors", ao_usb_descriptors_addr)
238         };
239
240         private void add_cc_symbols() {
241                 for (int i = 0; i < cc_symbols.length; i++)
242                         symlist.add(cc_symbols[i]);
243         }
244
245         public void add_symbol(AltosHexsym symbol) {
246                 symlist.add(symbol);
247         }
248
249         /* Take symbols from another hexfile and duplicate them here */
250         public void add_symbols(AltosHexfile other) {
251                 for (AltosHexsym symbol : other.symlist)
252                         symlist.add(symbol);
253         }
254
255         public AltosHexsym lookup_symbol(String name) {
256                 if (symlist.isEmpty())
257                         add_cc_symbols();
258
259                 for (AltosHexsym symbol : symlist)
260                         if (name.equals(symbol.name))
261                                 return symbol;
262                 return null;
263         }
264
265         private String make_string(byte[] data, int start, int length) {
266                 String s = "";
267                 for (int i = 0; i < length; i++)
268                         s += (char) data[start + i];
269                 return s;
270         }
271
272         public AltosHexfile(byte[] bytes, int offset) {
273                 data = bytes;
274                 address = offset;
275         }
276
277         public AltosHexfile(FileInputStream file) throws IOException {
278                 HexFileInputStream      input = new HexFileInputStream(file);
279                 LinkedList<HexRecord>   record_list = new LinkedList<HexRecord>();
280                 boolean                 done = false;
281
282                 while (!done) {
283                         try {
284                                 HexRecord       record = new HexRecord(input);
285
286                                 record_list.add(record);
287                         } catch (EOFException eof) {
288                                 done = true;
289                         }
290                 }
291
292                 long    extended_addr = 0;
293                 long    base = 0;
294                 long    bound = 0;
295                 boolean set = false;
296                 for (HexRecord record : record_list) {
297                         long addr;
298                         switch (record.type) {
299                         case 0:
300                                 addr = extended_addr + record.address;
301                                 long r_bound = addr + record.data.length;
302                                 if (!set || addr < base)
303                                         base = addr;
304                                 if (!set || r_bound > bound)
305                                         bound = r_bound;
306                                 set = true;
307                                 break;
308                         case 1:
309                                 break;
310                         case 2:
311                                 if (record.data.length != 2)
312                                         throw new IOException("invalid extended segment address record");
313                                 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
314                                 break;
315                         case 4:
316                                 if (record.data.length != 2)
317                                         throw new IOException("invalid extended segment address record");
318                                 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
319                                 break;
320                         case 0xfe:
321                                 String name = make_string(record.data, 0, record.data.length);
322                                 addr = extended_addr + record.address;
323                                 AltosHexsym s = new AltosHexsym(name, addr);
324                                 symlist.add(s);
325                                 break;
326                         default:
327                                 throw new IOException ("invalid hex record type");
328                         }
329                 }
330
331                 if (!set || base >= bound)
332                         throw new IOException("invalid hex file");
333
334                 if (bound - base > 4 * 1024 * 1024)
335                         throw new IOException("hex file too large");
336
337                 data = new byte[(int) (bound - base)];
338                 address = (int) base;
339                 Arrays.fill(data, (byte) 0xff);
340
341                 /* Paint the records into the new array */
342                 for (HexRecord record : record_list) {
343                         switch (record.type) {
344                         case 0:
345                                 long addr = extended_addr + record.address;
346                                 long r_bound = addr + record.data.length;
347                                 for (int j = 0; j < record.data.length; j++)
348                                         data[(int) (addr - base) + j] = record.data[j];
349                                 break;
350                         case 1:
351                                 break;
352                         case 2:
353                                 if (record.data.length != 2)
354                                         throw new IOException("invalid extended segment address record");
355                                 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
356                                 break;
357                         case 4:
358                                 if (record.data.length != 2)
359                                         throw new IOException("invalid extended segment address record");
360                                 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
361                                 break;
362                         case 0xfe:
363                                 break;
364                         default:
365                                 throw new IOException ("invalid hex record type");
366                         }
367                 }
368         }
369 }