1 //
2 // srecord - manipulate eprom load files
3 // Copyright (C) 2001-2003, 2005-2008, 2010, 2011, 2013 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at your
8 // option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
13 // License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include <cstring>
20 
21 #include <srecord/arglex/tool.h>
22 #include <srecord/input/file/fastload.h>
23 #include <srecord/record.h>
24 
25 
~input_file_fastload()26 srecord::input_file_fastload::~input_file_fastload()
27 {
28 }
29 
30 
input_file_fastload(const std::string & a_file_name)31 srecord::input_file_fastload::input_file_fastload(
32     const std::string &a_file_name
33 ) :
34     srecord::input_file(a_file_name),
35     seen_some_input(false),
36     address(0)
37 {
38 }
39 
40 
41 srecord::input_file::pointer
create(const std::string & a_file_name)42 srecord::input_file_fastload::create(const std::string &a_file_name)
43 {
44     return pointer(new srecord::input_file_fastload(a_file_name));
45 }
46 
47 
48 int
get_digit(void)49 srecord::input_file_fastload::get_digit(void)
50 {
51     int c = get_char();
52     switch (c)
53     {
54     case 'A': return 0;
55     case 'B': return 1;
56     case 'C': return 2;
57     case 'D': return 3;
58     case 'E': return 4;
59     case 'F': return 5;
60     case 'G': return 6;
61     case 'H': return 7;
62     case 'I': return 8;
63     case 'J': return 9;
64     case 'K': return 10;
65     case 'L': return 11;
66     case 'M': return 12;
67     case 'N': return 13;
68     case 'O': return 14;
69     case 'P': return 15;
70     case 'Q': return 16;
71     case 'R': return 17;
72     case 'S': return 18;
73     case 'T': return 19;
74     case 'U': return 20;
75     case 'V': return 21;
76     case 'W': return 22;
77     case 'X': return 23;
78     case 'Y': return 24;
79     case 'Z': return 25;
80     case 'a': return 26;
81     case 'b': return 27;
82     case 'c': return 28;
83     case 'd': return 29;
84     case 'e': return 30;
85     case 'f': return 31;
86     case 'g': return 32;
87     case 'h': return 33;
88     case 'i': return 34;
89     case 'j': return 35;
90     case 'k': return 36;
91     case 'l': return 37;
92     case 'm': return 38;
93     case 'n': return 39;
94     case 'o': return 40;
95     case 'p': return 41;
96     case 'q': return 42;
97     case 'r': return 43;
98     case 's': return 44;
99     case 't': return 45;
100     case 'u': return 46;
101     case 'v': return 47;
102     case 'w': return 48;
103     case 'x': return 49;
104     case 'y': return 50;
105     case 'z': return 51;
106     case '0': return 52;
107     case '1': return 53;
108     case '2': return 54;
109     case '3': return 55;
110     case '4': return 56;
111     case '5': return 57;
112     case '6': return 58;
113     case '7': return 59;
114     case '8': return 60;
115     case '9': return 61;
116     case ',': return 62;
117     case '.': return 63;
118     }
119     get_char_undo(c);
120     return -1;
121 }
122 
123 
124 unsigned long
get_number(int min_digits,int max_digits)125 srecord::input_file_fastload::get_number(int min_digits, int max_digits)
126 {
127     unsigned long result = 0;
128     for (int ndigits = 0; ndigits < max_digits; ++ndigits)
129     {
130         int c = get_digit();
131         if (c < 0)
132         {
133             if (ndigits < min_digits)
134             {
135                 fatal_error("base-64 number expected (%d < %d)",
136                     ndigits, min_digits);
137             }
138             break;
139         }
140         result = (result << 6) | c;
141     }
142     return result;
143 }
144 
145 
146 void
expect_white_space(void)147 srecord::input_file_fastload::expect_white_space(void)
148 {
149     switch (peek_char())
150     {
151     case -1:
152     case ' ':
153     case '\t':
154     case '\r':
155     case '\n':
156     case '/':
157         break;
158 
159     default:
160         fatal_error("white space expected");
161     }
162 }
163 
164 
165 bool
read_inner(srecord::record & record)166 srecord::input_file_fastload::read_inner(srecord::record &record)
167 {
168     unsigned long n;
169     unsigned char data[srecord::record::max_data_length];
170     unsigned long data_address = address;
171     srecord::record::type_t type;
172     int data_length = 0;
173     unsigned char the_byte;
174     for (;;)
175     {
176         switch (peek_char())
177         {
178         case -1:
179             return false;
180 
181         case ' ':
182         case '\t':
183         case '\n':
184         case '\r':
185             get_char();
186             break;
187 
188         case '/':
189             if (data_length > 0)
190             {
191                 got_a_record:
192                 record =
193                     srecord::record
194                     (
195                         srecord::record::type_data,
196                         data_address,
197                         data,
198                         data_length
199                     );
200                 return true;
201             }
202             get_char();
203             switch (get_char())
204             {
205             case 'A':
206                 address = get_number(1, 6);
207                 expect_white_space();
208                 data_address = address;
209                 break;
210 
211             case 'B':
212                 the_byte = get_number(1, 6);
213                 data[data_length++] = the_byte;
214                 checksum_add(the_byte);
215                 expect_white_space();
216                 address++;
217                 // assert(data_length == 1);
218                 break;
219 
220             case 'C':
221                 n = get_number(1, 6);
222                 if (use_checksums())
223                 {
224                     int csum = checksum_get16();
225                     if ((int)n != csum)
226                     {
227                         fatal_error("checksum mismatch (%04X != %04X)",
228                             (int)n, csum);
229                     }
230                 }
231                 expect_white_space();
232                 break;
233 
234             case 'E':
235                 get_number(1, 6);
236                 seek_to_end();
237                 type = srecord::record::type_execution_start_address;
238                 record = srecord::record(type, address, 0, 0);
239                 return true;
240 
241             case 'K':
242                 get_number(1, 6);
243                 expect_white_space();
244                 checksum_reset();
245                 break;
246 
247             case 'S':
248                 // Ignore symbols
249                 for (;;)
250                 {
251                     int c = get_char();
252                     if (c < 0)
253                         fatal_error("end-of-input in symbol");
254                     if (c == ',')
255                         break;
256                 }
257                 get_number(1, 6);
258                 expect_white_space();
259                 break;
260 
261             case 'Z':
262                 n = get_number(1, 6);
263                 expect_white_space();
264                 if (n >= srecord::record::max_data_length)
265                     fatal_error("clearing too many bytes (%lu)", n);
266                 memset(data, 0, n);
267                 type = srecord::record::type_data;
268                 record = srecord::record(type, address, data, n);
269                 address += n;
270                 return true;
271 
272             default:
273                 fatal_error("unknown command");
274             }
275             break;
276 
277         default:
278             if (data_length + 3 > srecord::record::max_data_length)
279                 goto got_a_record;
280             n = get_number(4, 4);
281             the_byte = n >> 16;
282             data[data_length++] = the_byte;
283             checksum_add(the_byte);
284             the_byte = n >> 8;
285             data[data_length++] = the_byte;
286             checksum_add(the_byte);
287             the_byte = n;
288             data[data_length++] = the_byte;
289             checksum_add(the_byte);
290             address += 3;
291             break;
292         }
293     }
294 }
295 
296 
297 bool
read(srecord::record & record)298 srecord::input_file_fastload::read(srecord::record &record)
299 {
300     if (!read_inner(record))
301     {
302         if (!seen_some_input)
303             fatal_error("file contains no data");
304         return false;
305     }
306     seen_some_input = true;
307     return true;
308 }
309 
310 
311 const char *
get_file_format_name(void) const312 srecord::input_file_fastload::get_file_format_name(void)
313     const
314 {
315     return "LSI Logic Fast Load";
316 }
317 
318 
319 int
format_option_number(void) const320 srecord::input_file_fastload::format_option_number(void)
321     const
322 {
323     return arglex_tool::token_fast_load;
324 }
325 
326 
327 // vim: set ts=8 sw=4 et :
328