1 //
2 // srecord - manipulate eprom load files
3 // Copyright (C) 2000-2010 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it 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
8 // (at 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
13 // GNU Lesser 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
17 // <http://www.gnu.org/licenses/>.
18 //
19 
20 #include <cctype>
21 
22 #include <srecord/arglex/tool.h>
23 #include <srecord/output/file/vmem.h>
24 #include <srecord/record.h>
25 
26 
~output_file_vmem()27 srecord::output_file_vmem::~output_file_vmem()
28 {
29     if (column)
30         put_char('\n');
31 }
32 
33 
34 //
35 // The default number of bits is 32.
36 // If you change this, you must also change the following files:
37 //     lib/srec/output/file/vmem.cc
38 //     man/man1/srecord::cat.1
39 //
40 #define DEFAULT_MEM_WIDTH 32
41 
42 
43 //
44 // Calculate log2(bytes_per_word)
45 //
46 // For example...
47 // Return  Num    Num
48 // Value   Bytes  Bits
49 //   0      1       8
50 //   1      2      16
51 //   2      4      32
52 //   3      8      64
53 //   4     16     128
54 //
55 static unsigned
calc_width_shift(int x)56 calc_width_shift(int x)
57 {
58     //
59     // The user could be giving a number of bytes.
60     // (But the range is limited to those values not easily confused
61     // with a number of bits.)
62     //
63     if (x == 1)
64         return 0;
65     if (x == 2)
66         return 1;
67     if (x == 4)
68         return 2;
69 
70     //
71     // Number of bits.
72     //
73     if (x == 8)
74         return 0;
75     if (x == 16)
76         return 1;
77     if (x == 32)
78         return 2;
79     if (x == 64)
80         return 3;
81     if (x == 128)
82         return 4;
83 
84     //
85     // The default number of bits is 32.
86     // If you change this, you must also change the following files:
87     //     man/man1/srecord::cat.1
88     //     lib/srec/arglex_output.cc
89     //
90     return 2;
91 }
92 
93 
94 static unsigned
calc_width_mask(int x)95 calc_width_mask(int x)
96 {
97     return ((1uL << calc_width_shift(x)) - 1uL);
98 }
99 
100 
output_file_vmem(const std::string & a_file_name)101 srecord::output_file_vmem::output_file_vmem(const std::string &a_file_name) :
102     srecord::output_file(a_file_name),
103     bytes_per_word(4),
104     address(0),
105     column(0),
106     pref_block_size(4 * bytes_per_word),
107     width_shift(calc_width_shift(8 * bytes_per_word)),
108     width_mask(calc_width_mask(8 * bytes_per_word))
109 {
110     line_length_set(80);
111 }
112 
113 
114 srecord::output::pointer
create(const std::string & a_file_name)115 srecord::output_file_vmem::create(const std::string &a_file_name)
116 {
117     return pointer(new srecord::output_file_vmem(a_file_name));
118 }
119 
120 
121 void
command_line(srecord::arglex_tool * cmdln)122 srecord::output_file_vmem::command_line(srecord::arglex_tool *cmdln)
123 {
124     if (cmdln->token_cur() == arglex::token_number)
125     {
126         int n = cmdln->value_number();
127         cmdln->token_next();
128 
129         width_shift = calc_width_shift(n);
130         bytes_per_word = 1u << width_shift;
131         width_mask = calc_width_mask(n);
132 
133         //
134         // Recalculate the preferred block size
135         // for an 80 column line.
136         //
137         line_length_set(80);
138     }
139 }
140 
141 
142 void
write(const srecord::record & record)143 srecord::output_file_vmem::write(const srecord::record &record)
144 {
145     switch (record.get_type())
146     {
147     case srecord::record::type_header:
148         // emit header records as comments in the file
149         if (enable_header_flag && record.get_length() > 0)
150         {
151             put_string("/* ");
152             if (record.get_address() != 0)
153             {
154                 unsigned long addr = record.get_address();
155                 put_stringf("%08lX: ", addr);
156             }
157             const unsigned char *cp = record.get_data();
158             const unsigned char *ep = cp + record.get_length();
159             while (cp < ep)
160             {
161                 unsigned char c = *cp++;
162                 if (c == '\n')
163                     put_stringf("\n * ");
164                 else if (isprint(c) || isspace(c))
165                     put_char(c);
166                 else
167                     put_stringf("\\%o", c);
168                 // make sure we don't end the comment
169                 if (c == '*' && cp < ep && *cp == '/')
170                     put_char(' ');
171             }
172             put_string(" */\n");
173         }
174         if (!enable_optional_address_flag)
175             address = (unsigned long)-1L;
176         break;
177 
178     case srecord::record::type_data:
179         //
180         // make sure the data is aligned properly
181         //
182         if
183         (
184             (record.get_address() & width_mask)
185         ||
186             (record.get_length() & width_mask)
187         )
188             fatal_alignment_error(1u << width_shift);
189 
190         //
191         // If we need to advance the address, it has to be at the start
192         // of a line.
193         //
194         if (address != record.get_address())
195         {
196             if (column)
197             {
198                 put_char('\n');
199                 column = 0;
200             }
201             address = record.get_address();
202         }
203 
204         //
205         // emit the data bytes
206         //
207         for (size_t j = 0; j < record.get_length(); j += bytes_per_word)
208         {
209             //
210             // Each line starts with an address.
211             // The addresses are actually divided by the memory width,
212             // rather than being byte adddresses.
213             //
214             // The presence of the @ character would seem to imply this
215             // is optional.  It would be easy to figure out that an
216             // address is an address, and not a data byte.
217             //
218             if (column == 0)
219                 put_stringf("@%08lX", address >> width_shift);
220 
221             //
222             // Put a space between each word
223             //
224             put_char(' ');
225 
226             //
227             // emit the bytes of the word
228             //
229             for (unsigned k = 0; k < bytes_per_word; ++k)
230             {
231                 //
232                 // Write the byte and crank the address.
233                 //
234                 put_byte(record.get_data(j + k));
235                 ++address;
236 
237                 //
238                 // Crank the column.
239                 // If the line is too long, finish it.
240                 //
241                 ++column;
242                 if (column >= pref_block_size)
243                 {
244                     put_char('\n');
245                     column = 0;
246                 }
247             }
248         }
249         break;
250 
251     case srecord::record::type_data_count:
252     case srecord::record::type_execution_start_address:
253         // ignore
254         break;
255 
256     case srecord::record::type_unknown:
257         fatal_error("can't write unknown record type");
258     }
259 }
260 
261 
262 void
line_length_set(int linlen)263 srecord::output_file_vmem::line_length_set(int linlen)
264 {
265     int nwords = (linlen - 9) / (bytes_per_word * 2 + 1);
266     int max_words = srecord::record::max_data_length >> width_shift;
267     if (nwords > max_words)
268         nwords = max_words;
269     if (nwords < 1)
270         nwords = 1;
271     pref_block_size = nwords * bytes_per_word;
272 }
273 
274 
275 void
address_length_set(int)276 srecord::output_file_vmem::address_length_set(int)
277 {
278     // ignore
279 }
280 
281 
282 bool
preferred_block_size_set(int nbytes)283 srecord::output_file_vmem::preferred_block_size_set(int nbytes)
284 {
285     if (nbytes < 1 || nbytes > record::max_data_length)
286         return false;
287     if (bytes_per_word > 1 && 0 != (nbytes % bytes_per_word))
288         return false;
289     pref_block_size = nbytes;
290     return true;
291 }
292 
293 
294 int
preferred_block_size_get() const295 srecord::output_file_vmem::preferred_block_size_get()
296     const
297 {
298     return pref_block_size;
299 }
300 
301 
302 const char *
format_name() const303 srecord::output_file_vmem::format_name()
304     const
305 {
306     return "VMem";
307 }
308