Reverted package name to 'altosui' from 'AltosUI'
[fw/altos] / altosui / 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; version 2 of the License.
7  *
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.
12  *
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.
16  */
17
18 package altosui;
19
20 import java.lang.*;
21 import java.io.*;
22 import java.util.concurrent.LinkedBlockingQueue;
23 import java.util.LinkedList;
24 import java.util.Iterator;
25 import java.util.Arrays;
26 import org.altusmetrum.AltosLib.*;
27
28 class HexFileInputStream extends PushbackInputStream {
29         public int line;
30
31         public HexFileInputStream(FileInputStream o) {
32                 super(new BufferedInputStream(o));
33                 line = 1;
34         }
35
36         public int read() throws IOException {
37                 int     c = super.read();
38                 if (c == '\n')
39                         line++;
40                 return c;
41         }
42
43         public void unread(int c) throws IOException {
44                 if (c == '\n')
45                         line--;
46                 if (c != -1)
47                         super.unread(c);
48         }
49 }
50
51 class HexRecord implements Comparable {
52         public int      address;
53         public int      type;
54         public byte     checksum;
55         public byte[]   data;
56
57         static final int NORMAL = 0;
58         static final int EOF = 1;
59         static final int EXTENDED_ADDRESS = 2;
60
61         enum read_state {
62                 marker,
63                 length,
64                 address,
65                 type,
66                 data,
67                 checksum,
68                 newline,
69                 white,
70                 done,
71         }
72
73         boolean ishex(int c) {
74                 if ('0' <= c && c <= '9')
75                         return true;
76                 if ('a' <= c && c <= 'f')
77                         return true;
78                 if ('A' <= c && c <= 'F')
79                         return true;
80                 return false;
81         }
82
83         boolean isspace(int c) {
84                 switch (c) {
85                 case ' ':
86                 case '\t':
87                         return true;
88                 }
89                 return false;
90         }
91
92         int fromhex(int c) {
93                 if ('0' <= c && c <= '9')
94                         return c - '0';
95                 if ('a' <= c && c <= 'f')
96                         return c - 'a' + 10;
97                 if ('A' <= c && c <= 'F')
98                         return c - 'A' + 10;
99                 return -1;
100         }
101
102         public byte checksum() {
103                 byte    got = 0;
104
105                 got += data.length;
106                 got += (address >> 8) & 0xff;
107                 got += (address     ) & 0xff;
108                 got += type;
109                 for (int i = 0; i < data.length; i++)
110                         got += data[i];
111                 return (byte) (-got);
112         }
113
114         public int compareTo(Object other) {
115                 HexRecord       o = (HexRecord) other;
116                 return address - o.address;
117         }
118
119         public String toString() {
120                 return String.format("%04x: %02x (%d)", address, type, data.length);
121         }
122
123         public HexRecord(HexFileInputStream input) throws IOException {
124                 read_state      state = read_state.marker;
125                 int             nhexbytes = 0;
126                 int             hex = 0;
127                 int             ndata = 0;
128                 byte            got_checksum;
129
130                 while (state != read_state.done) {
131                         int c = input.read();
132                         if (c < 0 && state != read_state.white)
133                                 throw new IOException(String.format("%d: Unexpected EOF", input.line));
134                         if (c == ' ')
135                                 continue;
136                         switch (state) {
137                         case marker:
138                                 if (c != ':')
139                                         throw new IOException("Missing ':'");
140                                 state = read_state.length;
141                                 nhexbytes = 2;
142                                 hex = 0;
143                                 break;
144                         case length:
145                         case address:
146                         case type:
147                         case data:
148                         case checksum:
149                                 if(!ishex(c))
150                                         throw new IOException(String.format("Non-hex char '%c'", c));
151                                 hex = hex << 4 | fromhex(c);
152                                 --nhexbytes;
153                                 if (nhexbytes != 0)
154                                         break;
155
156                                 switch (state) {
157                                 case length:
158                                         data = new byte[hex];
159                                         state = read_state.address;
160                                         nhexbytes = 4;
161                                         break;
162                                 case address:
163                                         address = hex;
164                                         state = read_state.type;
165                                         nhexbytes = 2;
166                                         break;
167                                 case type:
168                                         type = hex;
169                                         if (data.length > 0)
170                                                 state = read_state.data;
171                                         else
172                                                 state = read_state.checksum;
173                                         nhexbytes = 2;
174                                         ndata = 0;
175                                         break;
176                                 case data:
177                                         data[ndata] = (byte) hex;
178                                         ndata++;
179                                         nhexbytes = 2;
180                                         if (ndata == data.length)
181                                                 state = read_state.checksum;
182                                         break;
183                                 case checksum:
184                                         checksum = (byte) hex;
185                                         state = read_state.newline;
186                                         break;
187                                 default:
188                                         break;
189                                 }
190                                 hex = 0;
191                                 break;
192                         case newline:
193                                 if (c != '\n' && c != '\r')
194                                         throw new IOException("Missing newline");
195                                 state = read_state.white;
196                                 break;
197                         case white:
198                                 if (!isspace(c)) {
199                                         input.unread(c);
200                                         state = read_state.done;
201                                 }
202                                 break;
203                         case done:
204                                 break;
205                         }
206                 }
207                 got_checksum = checksum();
208                 if (got_checksum != checksum)
209                         throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
210                                                             checksum, got_checksum));
211         }
212 }
213
214 public class AltosHexfile {
215         public int      address;
216         public byte[]   data;
217
218         public byte get_byte(int a) {
219                 return data[a - address];
220         }
221
222         public AltosHexfile(FileInputStream file) throws IOException {
223                 HexFileInputStream      input = new HexFileInputStream(file);
224                 LinkedList<HexRecord>   record_list = new LinkedList<HexRecord>();
225                 boolean                 done = false;
226
227                 while (!done) {
228                         HexRecord       record = new HexRecord(input);
229
230                         if (record.type == HexRecord.EOF)
231                                 done = true;
232                         else
233                                 record_list.add(record);
234                 }
235                 HexRecord[] records  = record_list.toArray(new HexRecord[0]);
236                 Arrays.sort(records);
237                 if (records.length > 0) {
238                         int     base = records[0].address;
239                         int     bound = records[records.length-1].address +
240                                 records[records.length-1].data.length;
241
242                         data = new byte[bound - base];
243                         address = base;
244                         Arrays.fill(data, (byte) 0xff);
245
246                         /* Paint the records into the new array */
247                         for (int i = 0; i < records.length; i++) {
248                                 for (int j = 0; j < records[i].data.length; j++)
249                                         data[records[i].address - base + j] = records[i].data[j];
250                         }
251                 }
252         }
253 }