Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[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
27 class HexFileInputStream extends PushbackInputStream {
28         public int line;
29
30         public HexFileInputStream(FileInputStream o) {
31                 super(new BufferedInputStream(o));
32                 line = 1;
33         }
34
35         public int read() throws IOException {
36                 int     c = super.read();
37                 if (c == '\n')
38                         line++;
39                 return c;
40         }
41
42         public void unread(int c) throws IOException {
43                 if (c == '\n')
44                         line--;
45                 if (c != -1)
46                         super.unread(c);
47         }
48 }
49
50 class HexRecord implements Comparable {
51         public int      address;
52         public int      type;
53         public byte     checksum;
54         public byte[]   data;
55
56         static final int NORMAL = 0;
57         static final int EOF = 1;
58         static final int EXTENDED_ADDRESS = 2;
59
60         enum read_state {
61                 marker,
62                 length,
63                 address,
64                 type,
65                 data,
66                 checksum,
67                 newline,
68                 white,
69                 done,
70         }
71
72         boolean ishex(int c) {
73                 if ('0' <= c && c <= '9')
74                         return true;
75                 if ('a' <= c && c <= 'f')
76                         return true;
77                 if ('A' <= c && c <= 'F')
78                         return true;
79                 return false;
80         }
81
82         boolean isspace(int c) {
83                 switch (c) {
84                 case ' ':
85                 case '\t':
86                         return true;
87                 }
88                 return false;
89         }
90
91         int fromhex(int c) {
92                 if ('0' <= c && c <= '9')
93                         return c - '0';
94                 if ('a' <= c && c <= 'f')
95                         return c - 'a' + 10;
96                 if ('A' <= c && c <= 'F')
97                         return c - 'A' + 10;
98                 return -1;
99         }
100
101         public byte checksum() {
102                 byte    got = 0;
103
104                 got += data.length;
105                 got += (address >> 8) & 0xff;
106                 got += (address     ) & 0xff;
107                 got += type;
108                 for (int i = 0; i < data.length; i++)
109                         got += data[i];
110                 return (byte) (-got);
111         }
112
113         public int compareTo(Object other) {
114                 HexRecord       o = (HexRecord) other;
115                 return address - o.address;
116         }
117
118         public String toString() {
119                 return String.format("%04x: %02x (%d)", address, type, data.length);
120         }
121
122         public HexRecord(HexFileInputStream input) throws IOException {
123                 read_state      state = read_state.marker;
124                 int             nhexbytes = 0;
125                 int             hex = 0;
126                 int             ndata = 0;
127                 byte            got_checksum;
128
129                 while (state != read_state.done) {
130                         int c = input.read();
131                         if (c < 0 && state != read_state.white)
132                                 throw new IOException(String.format("%d: Unexpected EOF", input.line));
133                         if (c == ' ')
134                                 continue;
135                         switch (state) {
136                         case marker:
137                                 if (c != ':')
138                                         throw new IOException("Missing ':'");
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
217         public byte get_byte(int a) {
218                 return data[a - address];
219         }
220
221         public AltosHexfile(FileInputStream file) throws IOException {
222                 HexFileInputStream      input = new HexFileInputStream(file);
223                 LinkedList<HexRecord>   record_list = new LinkedList<HexRecord>();
224                 boolean                 done = false;
225
226                 while (!done) {
227                         HexRecord       record = new HexRecord(input);
228
229                         if (record.type == HexRecord.EOF)
230                                 done = true;
231                         else
232                                 record_list.add(record);
233                 }
234                 HexRecord[] records  = record_list.toArray(new HexRecord[0]);
235                 Arrays.sort(records);
236                 if (records.length > 0) {
237                         int     base = records[0].address;
238                         int     bound = records[records.length-1].address +
239                                 records[records.length-1].data.length;
240
241                         data = new byte[bound - base];
242                         address = base;
243                         Arrays.fill(data, (byte) 0xff);
244
245                         /* Paint the records into the new array */
246                         for (int i = 0; i < records.length; i++) {
247                                 for (int j = 0; j < records[i].data.length; j++)
248                                         data[records[i].address - base + j] = records[i].data[j];
249                         }
250                 }
251         }
252 }