//
// srecord - manipulate eprom load files
// Copyright (C) 2000-2007 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
#include
#include
#include
#include
//
// The default number of bits is 32.
// If you change this, you must also change the following files:
// lib/srec/output/file/vmem.cc
// man/man1/srec_cat.1
//
#define DEFAULT_MEM_WIDTH 32
//
// Calculate log2(bytes_per_word)
//
// For example...
// Return Num Num
// Value Bytes Bits
// 0 1 8
// 1 2 16
// 2 4 32
// 3 8 64
// 4 16 128
//
static unsigned
calc_width_shift(int x)
{
//
// The user could be giving a number of bytes.
// (But the range is limited to those values not easily confused
// with a number of bits.)
//
if (x == 1)
return 0;
if (x == 2)
return 1;
if (x == 4)
return 2;
//
// Number of bits.
//
if (x == 8)
return 0;
if (x == 16)
return 1;
if (x == 32)
return 2;
if (x == 64)
return 3;
if (x == 128)
return 4;
//
// The default number of bits is 32.
// If you change this, you must also change the following files:
// man/man1/srec_cat.1
// lib/srec/arglex_output.cc
//
return 2;
}
static unsigned
calc_width_mask(int x)
{
return ((1uL << calc_width_shift(x)) - 1uL);
}
srec_output_file_vmem::srec_output_file_vmem(const std::string &a_file_name) :
srec_output_file(a_file_name),
bytes_per_word(4),
address(0),
column(0),
pref_block_size(4 * bytes_per_word),
width_shift(calc_width_shift(8 * bytes_per_word)),
width_mask(calc_width_mask(8 * bytes_per_word))
{
line_length_set(80);
}
void
srec_output_file_vmem::command_line(srec_arglex *cmdln)
{
if (cmdln->token_cur() == arglex::token_number)
{
int n = cmdln->value_number();
cmdln->token_next();
width_shift = calc_width_shift(n);
bytes_per_word = 1u << width_shift;
width_mask = calc_width_mask(n);
//
// Recalculate the preferred block size
// for an 80 column line.
//
line_length_set(80);
}
}
srec_output_file_vmem::~srec_output_file_vmem()
{
if (column)
put_char('\n');
}
void
srec_output_file_vmem::write(const srec_record &record)
{
switch (record.get_type())
{
case srec_record::type_header:
// emit header records as comments in the file
if (!data_only_flag && record.get_length() > 0)
{
put_string("/* ");
if (record.get_address() != 0)
put_stringf("%08lX: ", record.get_address());
const unsigned char *cp = record.get_data();
const unsigned char *ep = cp + record.get_length();
while (cp < ep)
{
unsigned char c = *cp++;
if (c == '\n')
put_stringf("\n * ");
else if (isprint(c) || isspace(c))
put_char(c);
else
put_stringf("\\%o", c);
// make sure we don't end the comment
if (c == '*' && cp < ep && *cp == '/')
put_char(' ');
}
put_string(" */\n");
}
break;
case srec_record::type_data:
//
// make sure the data is aligned properly
//
if
(
(record.get_address() & width_mask)
||
(record.get_length() & width_mask)
)
fatal_alignment_error(1u << width_shift);
//
// If we need to advance the address, it has to be at the start
// of a line.
//
if (address != record.get_address())
{
if (column)
{
put_char('\n');
column = 0;
}
address = record.get_address();
}
//
// emit the data bytes
//
for (int j = 0; j < record.get_length(); j += bytes_per_word)
{
//
// Each line starts with an address.
// The addresses are actually divided by the memory width,
// rather than being byte adddresses.
//
// The presence of the @ character would seem to imply this
// is optional. It would be easy to figure out that an
// address is an address, and not a data byte.
//
if (column == 0)
put_stringf("@%08lX", address >> width_shift);
//
// Put a space between each word
//
put_char(' ');
//
// emit the bytes of the word
//
for (unsigned k = 0; k < bytes_per_word; ++k)
{
//
// Write the byte and crank the address.
//
put_byte(record.get_data(j + k));
++address;
//
// Crank the column.
// If the line is too long, finish it.
//
++column;
if (column >= pref_block_size)
{
put_char('\n');
column = 0;
}
}
}
break;
case srec_record::type_data_count:
case srec_record::type_start_address:
// ignore
break;
case srec_record::type_unknown:
fatal_error("can't write unknown record type");
}
}
void
srec_output_file_vmem::line_length_set(int linlen)
{
int nwords = (linlen - 9) / (bytes_per_word * 2 + 1);
int max_words = srec_record::max_data_length >> width_shift;
if (nwords > max_words)
nwords = max_words;
if (nwords < 1)
nwords = 1;
pref_block_size = nwords * bytes_per_word;
}
void
srec_output_file_vmem::address_length_set(int)
{
// ignore
}
int
srec_output_file_vmem::preferred_block_size_get()
const
{
return pref_block_size;
}
const char *
srec_output_file_vmem::format_name()
const
{
return "VMem";
}