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