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