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; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 package org.altusmetrum.altoslib_9;
21 import java.util.LinkedList;
22 import java.util.Arrays;
24 class HexFileInputStream extends PushbackInputStream {
27 public HexFileInputStream(FileInputStream o) {
28 super(new BufferedInputStream(o));
32 public int read() throws IOException {
39 public void unread(int c) throws IOException {
47 class HexRecord implements Comparable<Object> {
53 static final int NORMAL = 0;
54 static final int EOF = 1;
55 static final int EXTENDED_ADDRESS = 2;
69 boolean ishex(int c) {
70 if ('0' <= c && c <= '9')
72 if ('a' <= c && c <= 'f')
74 if ('A' <= c && c <= 'F')
79 boolean isspace(int c) {
89 if ('0' <= c && c <= '9')
91 if ('a' <= c && c <= 'f')
93 if ('A' <= c && c <= 'F')
98 public byte checksum() {
102 got += (address >> 8) & 0xff;
103 got += (address ) & 0xff;
105 for (int i = 0; i < data.length; i++)
107 return (byte) (-got);
110 public int compareTo(Object other) {
111 HexRecord o = (HexRecord) other;
112 return address - o.address;
115 public String toString() {
116 return String.format("%04x: %02x (%d)", address, type, data.length);
119 public HexRecord(HexFileInputStream input) throws IOException, EOFException {
120 read_state state = read_state.marker;
126 while (state != read_state.done) {
127 int c = input.read();
128 if (c < 0 && state != read_state.white && state != read_state.marker)
129 throw new IOException(String.format("%d: Unexpected EOF", input.line));
134 if (c == EOF || c == -1)
135 throw new EOFException();
137 throw new IOException(String.format ("Missing ':' (got %x)", c));
138 state = read_state.length;
148 throw new IOException(String.format("Non-hex char '%c'", c));
149 hex = hex << 4 | fromhex(c);
156 data = new byte[hex];
157 state = read_state.address;
162 state = read_state.type;
168 state = read_state.data;
170 state = read_state.checksum;
175 data[ndata] = (byte) hex;
178 if (ndata == data.length)
179 state = read_state.checksum;
182 checksum = (byte) hex;
183 state = read_state.newline;
191 if (c != '\n' && c != '\r')
192 throw new IOException("Missing newline");
193 state = read_state.white;
198 state = read_state.done;
205 got_checksum = checksum();
206 if (got_checksum != checksum)
207 throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
208 checksum, got_checksum));
212 public class AltosHexfile {
215 LinkedList<AltosHexsym> symlist = new LinkedList<AltosHexsym>();
217 public byte get_byte(int a) {
218 return data[a - address];
221 /* CC1111-based products have the romconfig stuff located
222 * at a fixed address; when the file we load has no symbols,
223 * assume it is one of those and set the symbols appropriately
225 final static int ao_romconfig_version_addr = 0xa0;
226 final static int ao_romconfig_check_addr = 0xa2;
227 final static int ao_serial_number_addr = 0xa4;
228 final static int ao_radio_cal_addr = 0xa6;
229 final static int ao_usb_descriptors_addr = 0xaa;
231 static AltosHexsym[] cc_symbols = {
232 new AltosHexsym("ao_romconfig_version", ao_romconfig_version_addr),
233 new AltosHexsym("ao_romconfig_check", ao_romconfig_check_addr),
234 new AltosHexsym("ao_serial_number", ao_serial_number_addr),
235 new AltosHexsym("ao_radio_cal", ao_radio_cal_addr),
236 new AltosHexsym("ao_usb_descriptors", ao_usb_descriptors_addr)
239 private void add_cc_symbols() {
240 for (int i = 0; i < cc_symbols.length; i++)
241 symlist.add(cc_symbols[i]);
244 public void add_symbol(AltosHexsym symbol) {
248 /* Take symbols from another hexfile and duplicate them here */
249 public void add_symbols(AltosHexfile other) {
250 for (AltosHexsym symbol : other.symlist)
254 public AltosHexsym lookup_symbol(String name) {
255 if (symlist.isEmpty())
258 for (AltosHexsym symbol : symlist)
259 if (name.equals(symbol.name))
264 private String make_string(byte[] data, int start, int length) {
266 for (int i = 0; i < length; i++)
267 s += (char) data[start + i];
271 public AltosHexfile(byte[] bytes, int offset) {
276 public AltosHexfile(FileInputStream file) throws IOException {
277 HexFileInputStream input = new HexFileInputStream(file);
278 LinkedList<HexRecord> record_list = new LinkedList<HexRecord>();
279 boolean done = false;
283 HexRecord record = new HexRecord(input);
285 record_list.add(record);
286 } catch (EOFException eof) {
291 long extended_addr = 0;
295 for (HexRecord record : record_list) {
297 switch (record.type) {
299 addr = extended_addr + record.address;
300 long r_bound = addr + record.data.length;
301 if (!set || addr < base)
303 if (!set || r_bound > bound)
310 if (record.data.length != 2)
311 throw new IOException("invalid extended segment address record");
312 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
315 if (record.data.length != 2)
316 throw new IOException("invalid extended segment address record");
317 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
320 String name = make_string(record.data, 0, record.data.length);
321 addr = extended_addr + record.address;
322 AltosHexsym s = new AltosHexsym(name, addr);
326 throw new IOException ("invalid hex record type");
330 if (!set || base >= bound)
331 throw new IOException("invalid hex file");
333 if (bound - base > 4 * 1024 * 1024)
334 throw new IOException("hex file too large");
336 data = new byte[(int) (bound - base)];
337 address = (int) base;
338 Arrays.fill(data, (byte) 0xff);
340 /* Paint the records into the new array */
341 for (HexRecord record : record_list) {
342 switch (record.type) {
344 long addr = extended_addr + record.address;
345 long r_bound = addr + record.data.length;
346 for (int j = 0; j < record.data.length; j++)
347 data[(int) (addr - base) + j] = record.data[j];
352 if (record.data.length != 2)
353 throw new IOException("invalid extended segment address record");
354 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
357 if (record.data.length != 2)
358 throw new IOException("invalid extended segment address record");
359 extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
364 throw new IOException ("invalid hex record type");