2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
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.
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.
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.
19 package org.altusmetrum.altoslib_14;
22 import java.util.LinkedList;
23 import java.util.Arrays;
25 class HexFileInputStream extends PushbackInputStream {
28 public HexFileInputStream(FileInputStream o) {
29 super(new BufferedInputStream(o));
33 public int read() throws IOException {
40 public void unread(int c) throws IOException {
48 class HexRecord implements Comparable<Object> {
54 static final int NORMAL = 0;
55 static final int EOF = 1;
56 static final int EXTENDED_ADDRESS = 2;
70 boolean ishex(int c) {
71 if ('0' <= c && c <= '9')
73 if ('a' <= c && c <= 'f')
75 if ('A' <= c && c <= 'F')
80 boolean isspace(int c) {
90 if ('0' <= c && c <= '9')
92 if ('a' <= c && c <= 'f')
94 if ('A' <= c && c <= 'F')
99 public byte checksum() {
103 got += (address >> 8) & 0xff;
104 got += (address ) & 0xff;
106 for (int i = 0; i < data.length; i++)
108 return (byte) (-got);
111 public int compareTo(Object other) {
112 HexRecord o = (HexRecord) other;
114 long diff = address - o.address;
123 public String toString() {
124 return String.format("%04x: %02x (%d)", address, type, data.length);
127 public HexRecord(HexFileInputStream input) throws IOException, EOFException {
128 read_state state = read_state.marker;
134 while (state != read_state.done) {
135 int c = input.read();
136 if (c < 0 && state != read_state.white && state != read_state.marker)
137 throw new IOException(String.format("%d: Unexpected EOF", input.line));
142 if (c == EOF || c == -1)
143 throw new EOFException();
145 throw new IOException(String.format ("Missing ':' (got %x)", c));
146 state = read_state.length;
156 throw new IOException(String.format("Non-hex char '%c'", c));
157 hex = hex << 4 | fromhex(c);
164 data = new byte[(int) hex];
165 state = read_state.address;
170 state = read_state.type;
176 state = read_state.data;
178 state = read_state.checksum;
183 data[ndata] = (byte) hex;
186 if (ndata == data.length)
187 state = read_state.checksum;
190 checksum = (byte) hex;
191 state = read_state.newline;
199 if (c != '\n' && c != '\r')
200 throw new IOException("Missing newline");
201 state = read_state.white;
206 state = read_state.done;
213 got_checksum = checksum();
214 if (got_checksum != checksum)
215 throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
216 checksum, got_checksum));
220 public class AltosHexfile {
222 public long max_address;
224 LinkedList<AltosHexsym> symlist = new LinkedList<AltosHexsym>();
226 public byte get_byte(long a) {
227 return data[(int) (a - address)];
230 public int get_u8(long a) {
231 return ((int) get_byte(a)) & 0xff;
234 public int get_u16(long a) {
235 return get_u8(a) | (get_u8(a+1) << 8);
238 /* CC1111-based products have the romconfig stuff located
239 * at a fixed address; when the file we load has no symbols,
240 * assume it is one of those and set the symbols appropriately
242 final static int ao_romconfig_version_addr = 0xa0;
243 final static int ao_romconfig_check_addr = 0xa2;
244 final static int ao_serial_number_addr = 0xa4;
245 final static int ao_radio_cal_addr = 0xa6;
246 final static int ao_usb_descriptors_addr = 0xaa;
248 static AltosHexsym[] cc_symbols = {
249 new AltosHexsym("ao_romconfig_version", ao_romconfig_version_addr),
250 new AltosHexsym("ao_romconfig_check", ao_romconfig_check_addr),
251 new AltosHexsym("ao_serial_number", ao_serial_number_addr),
252 new AltosHexsym("ao_radio_cal", ao_radio_cal_addr),
253 new AltosHexsym("ao_usb_descriptors", ao_usb_descriptors_addr)
256 static final int AO_USB_DESC_DEVICE = 1;
257 static final int AO_USB_DESC_STRING = 3;
259 static final int AO_ROMCONFIG_VERSION_INDEX = 0;
260 static final int AO_ROMCONFIG_CHECK_INDEX = 1;
261 static final int AO_SERIAL_NUMBER_INDEX = 2;
262 static final int AO_RADIO_CAL_INDEX = 3;
263 static final int AO_USB_DESCRIPTORS_INDEX = 4;
265 private void add_cc_symbols() {
266 for (int i = 0; i < cc_symbols.length; i++)
267 symlist.add(cc_symbols[i]);
270 public void add_symbol(AltosHexsym symbol) {
274 /* Take symbols from another hexfile and duplicate them here */
275 public void add_symbols(AltosHexfile other) {
276 for (AltosHexsym symbol : other.symlist)
280 public AltosHexsym lookup_symbol(String name) {
281 if (symlist.isEmpty())
284 for (AltosHexsym symbol : symlist)
285 if (name.equals(symbol.name))
290 private long find_usb_descriptors() {
291 AltosHexsym usb_descriptors = lookup_symbol("ao_usb_descriptors");
294 if (usb_descriptors == null)
298 /* The address of this has moved depending on
299 * padding in the linker script. Look forward
300 * and backwards two bytes to see if we can find it
302 a = usb_descriptors.address;
304 if (get_u8(a) == 0x12 && get_u8(a+1) == AO_USB_DESC_DEVICE)
306 else if (get_u8(a+1) == 0x12 && get_u8(a+3) == AO_USB_DESC_DEVICE)
308 else if (get_u8(a-2) == 0x12 && get_u8(a-1) == AO_USB_DESC_DEVICE)
312 } catch (ArrayIndexOutOfBoundsException ae) {
317 public AltosUsbId find_usb_id() {
318 long a = find_usb_descriptors();
323 /* Walk the descriptors looking for the device */
324 while (get_u8(a+1) != AO_USB_DESC_DEVICE) {
325 int delta = get_u8(a);
327 if (delta == 0 || a >= max_address)
331 return new AltosUsbId(get_u16(a + 8),
335 public String find_usb_product() {
336 long a = find_usb_descriptors();
343 product_string = get_u8(a+15);
345 /* Walk the descriptors looking for the device */
348 if (get_u8(a+1) == AO_USB_DESC_STRING) {
350 if (num_strings == product_string + 1)
354 int delta = get_u8(a);
356 if (delta == 0 || a >= max_address)
360 int product_len = get_u8(a);
362 if (product_len <= 0)
367 for (int i = 0; i < product_len - 2; i += 2) {
368 int c = get_u16(a + 2 + i);
370 product += Character.toString((char) c);
374 System.out.printf("product %s\n", product);
379 private String make_string(byte[] data, int start, int length) {
381 for (int i = 0; i < length; i++)
382 s += (char) data[start + i];
386 public AltosHexfile(byte[] bytes, long offset) {
389 max_address = address + bytes.length;
392 public AltosHexfile(FileInputStream file) throws IOException {
393 HexFileInputStream input = new HexFileInputStream(file);
394 LinkedList<HexRecord> record_list = new LinkedList<HexRecord>();
395 boolean done = false;
399 HexRecord record = new HexRecord(input);
401 record_list.add(record);
402 } catch (EOFException eof) {
407 long extended_addr = 0;
411 for (HexRecord record : record_list) {
413 switch (record.type) {
415 addr = extended_addr + record.address;
416 long r_bound = addr + record.data.length;
417 if (!set || addr < base)
419 if (!set || r_bound > bound)
426 if (record.data.length != 2)
427 throw new IOException("invalid extended segment address record");
428 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
431 if (record.data.length != 2)
432 throw new IOException("invalid extended segment address record");
433 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
436 String name = make_string(record.data, 0, record.data.length);
437 addr = extended_addr + record.address;
438 AltosHexsym s = new AltosHexsym(name, addr);
442 throw new IOException ("invalid hex record type");
446 if (!set || base >= bound)
447 throw new IOException("invalid hex file");
449 if (bound - base > 4 * 1024 * 1024)
450 throw new IOException("hex file too large");
452 data = new byte[(int) (bound - base)];
455 Arrays.fill(data, (byte) 0xff);
457 /* Paint the records into the new array */
458 for (HexRecord record : record_list) {
459 switch (record.type) {
461 long addr = extended_addr + record.address;
462 long r_bound = addr + record.data.length;
463 for (int j = 0; j < record.data.length; j++)
464 data[(int) (addr - base) + j] = record.data[j];
469 if (record.data.length != 2)
470 throw new IOException("invalid extended segment address record");
471 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
474 if (record.data.length != 2)
475 throw new IOException("invalid extended segment address record");
476 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
481 throw new IOException ("invalid hex record type");