1 //
2 // srecord - Manipulate EPROM load files
3 // Copyright (C) 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
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public 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 <cassert>
20 
21 #include <srecord/arglex/tool.h>
22 #include <srecord/input/file/ppx.h>
23 #include <srecord/record.h>
24 
25 
~input_file_ppx()26 srecord::input_file_ppx::~input_file_ppx()
27 {
28 }
29 
30 
input_file_ppx(const std::string & filename)31 srecord::input_file_ppx::input_file_ppx(const std::string &filename) :
32     input_file(filename),
33     state(0),
34     token(token_eof),
35     token_value(0),
36     address(0),
37     data_seen(false),
38     dsum(0),
39     buffer_length(0)
40 {
41 }
42 
43 
44 srecord::input_file_ppx::pointer
create(const std::string & filename)45 srecord::input_file_ppx::create(const std::string &filename)
46 {
47     return pointer(new input_file_ppx(filename));
48 }
49 
50 
51 void
get_next_token(void)52 srecord::input_file_ppx::get_next_token(void)
53 {
54     for (;;)
55     {
56         int sc = get_char();
57         if (sc < 0)
58         {
59             token = token_eof;
60             return;
61         }
62         unsigned char c = sc;
63         switch (c)
64         {
65         case '*':
66             token = token_star;
67             return;
68 
69         case '$':
70             token = token_end;
71             return;
72 
73         case 'S':
74             token = token_sum;
75             return;
76 
77         case ' ':
78         case '\t':
79         case '\f':
80         case '\r':
81         case '\v':
82         case '\n':
83             break;
84 
85         case '0': case '1': case '2': case '3': case '4':
86         case '5': case '6': case '7': case '8': case '9':
87         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
88         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
89             {
90                 token_value = get_nibble_value(c);
91                 int ndigits = 1;
92                 for (;;)
93                 {
94                     int sc = get_char();
95                     if (sc < 0)
96                         break;
97                     c = sc;
98                     switch (c)
99                     {
100                     case '0': case '1': case '2': case '3': case '4':
101                     case '5': case '6': case '7': case '8': case '9':
102                     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
103                     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
104                         token_value = (token_value << 4) | get_nibble_value(c);
105                         ++ndigits;
106                         continue;
107 
108                     default:
109                         get_char_undo(c);
110                         break;
111                     }
112                     break;
113                 }
114                 if (ndigits > 2)
115                 {
116                     token = token_address;
117                     return;
118                 }
119                 token = token_byte;
120                 return;
121             }
122 
123         default:
124             syntax_error();
125             break;
126         }
127     }
128 }
129 
130 
131 void
syntax_error(void)132 srecord::input_file_ppx::syntax_error(void)
133 {
134     fatal_error("syntax error");
135 }
136 
137 
138 bool
read(record & result)139 srecord::input_file_ppx::read(record &result)
140 {
141     //
142     // file
143     //     : STAR lines end
144     //     ;
145     // lines
146     //     : line
147     //     | lines line
148     //     ;
149     // line
150     //     : ADDRESS bytes
151     //     ;
152     // bytes
153     //     : BYTE
154     //     | bytes BYTE
155     //     ;
156     // end
157     //     : END
158     //     | END SUM ADDRESS
159     //     ;
160     //
161     for (;;)
162     {
163         switch (state)
164         {
165         case 0:
166             get_next_token();
167             // file: . ASTERISK lines end;
168             switch (token)
169             {
170             case token_star:
171                 get_next_token();
172                 state = 1;
173                 break;
174 
175             default:
176                 syntax_error();
177             }
178             break;
179 
180         case 1:
181             // file: ASTERISK . lines end;
182             // file: ASTERISK lines . end;
183             // lines: . line
184             // lines: lines . line
185             // lines: lines line .
186             // line: . ADDRESS bytes
187             // end: . END
188             // end: . END SUM ADDRESS
189             switch (token)
190             {
191             case token_address:
192                 if (address != token_value)
193                     assert(buffer_length == 0);
194                 address = token_value;
195                 get_next_token();
196                 state = 3;
197                 break;
198 
199             case token_end:
200                 get_next_token();
201                 state = 2;
202                 break;
203 
204             default:
205                 syntax_error();
206             }
207             break;
208 
209         case 2:
210             // end: END .
211             // end: END . SUM ADDRESS
212             switch (token)
213             {
214             case token_eof:
215                 state = 5;
216                 break;
217 
218             case token_sum:
219                 get_next_token();
220                 state = 4;
221                 break;
222 
223             default:
224                 syntax_error();
225             }
226             break;
227 
228         case 3:
229             // line: ADDRESS . bytes
230             // line: ADDRESS bytes .
231             // bytes: . BYTE
232             // bytes: bytes . BYTE
233             // bytes: bytes BYTE .
234             switch (token)
235             {
236             case token_address:
237             case token_end:
238                 state = 1;
239 
240                 if (buffer_length)
241                 {
242                     return_data_record:
243                     result =
244                         record
245                         (
246                             record::type_data,
247                             address - buffer_length,
248                             buffer,
249                             buffer_length
250                         );
251                     buffer_length = 0;
252                     return true;
253                 }
254                 break;
255 
256             case token_byte:
257                 dsum += token_value;
258                 buffer[buffer_length++] = token_value;
259                 ++address;
260                 get_next_token();
261                 data_seen = true;
262 
263                 if (buffer_length >= sizeof(buffer))
264                     goto return_data_record;
265                 break;
266 
267             default:
268                 syntax_error();
269             }
270             break;
271 
272         case 4:
273             // end: END SUM . ADDRESS
274             switch (token)
275             {
276             case token_address:
277                 if (use_checksums() && dsum != token_value)
278                 {
279                     fatal_error
280                     (
281                         "checksum mismatch (calculated 0x%04X, given 0x%04X)",
282                         dsum,
283                         token_value
284                     );
285                 }
286                 get_next_token();
287                 state = 5;
288                 break;
289 
290             default:
291                 syntax_error();
292             }
293             break;
294 
295         case 5:
296             // file: ASTERISK lines end .
297             switch (token)
298             {
299             case token_eof:
300                 if (!data_seen)
301                     fatal_error("no data seen");
302                 return false;
303 
304             default:
305                 syntax_error();
306             }
307             break;
308         }
309     }
310 }
311 
312 
313 const char *
get_file_format_name(void) const314 srecord::input_file_ppx::get_file_format_name(void)
315     const
316 {
317     return "Stag Prom Programmer hexadecimal (PPX)";
318 }
319 
320 
321 int
format_option_number(void) const322 srecord::input_file_ppx::format_option_number(void)
323     const
324 {
325     return arglex_tool::token_ppx;
326 }
327 
328 
329 // vim: set ts=8 sw=4 et :
330