//
// srecord - manipulate eprom load files
// Copyright (C) 2003, 2006, 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
#include // for sprintf
srec_output_file_asm::srec_output_file_asm(const string &filename) :
srec_output_file(filename),
prefix("eprom"),
taddr(0),
column(0),
current_address(0),
line_length(75),
org_warn(false),
output_word(false),
dot_style(false),
section_style(false),
hex_style(false)
{
}
void
srec_output_file_asm::command_line(srec_arglex *cmdln)
{
if (cmdln->token_cur() == arglex::token_string)
{
prefix = cmdln->value_string();
cmdln->token_next();
}
for (;;)
{
switch (cmdln->token_cur())
{
case srec_arglex::token_a430:
cmdln->token_next();
// Generate "IAR assembler compiler compliant" output.
section_style = true;
hex_style = true;
break;
case srec_arglex::token_cl430:
cmdln->token_next();
// Generate "Code Composer Essential compliant" output.
dot_style = true;
section_style = true;
hex_style = true;
break;
case srec_arglex::token_style_dot:
cmdln->token_next();
dot_style = true;
break;
case srec_arglex::token_style_hexadecimal:
cmdln->token_next();
hex_style = true;
break;
case srec_arglex::token_style_hexadecimal_not:
cmdln->token_next();
hex_style = false;
break;
case srec_arglex::token_style_section:
cmdln->token_next();
section_style = true;
break;
case srec_arglex::token_output_word:
cmdln->token_next();
output_word = true;
break;
default:
return;
}
}
}
void
srec_output_file_asm::emit_byte(int n)
{
char buffer[8];
if (hex_style)
sprintf(buffer, "0x%2.2X", (unsigned char)n);
else
sprintf(buffer, "%u", (unsigned char)n);
int len = strlen(buffer);
if (column && (column + 1 + len) > line_length)
{
put_char('\n');
column = 0;
}
if (!column)
{
if (dot_style)
{
put_string(" .byte ");
column = 16;
}
else
{
put_string(" DB ");
column = 16;
}
}
else
{
put_char(',');
++column;
}
put_string(buffer);
column += len;
++current_address;
}
void
srec_output_file_asm::emit_word(unsigned int n)
{
char buffer[16];
if (hex_style)
snprintf(buffer, sizeof(buffer), "0x%4.4X", (unsigned short)n);
else
snprintf(buffer, sizeof(buffer), "%u", (unsigned short)n);
int len = strlen(buffer);
if (column && (column + 1 + len) > line_length)
{
put_char('\n');
column = 0;
}
if (!column)
{
if (dot_style)
{
put_string(" .short ");
column = 20;
}
else
{
put_string(" DW ");
column = 16;
}
}
else
{
put_char(',');
++column;
}
put_string(buffer);
column += len;
current_address += 2;
}
srec_output_file_asm::~srec_output_file_asm()
{
if (!section_style && range.empty())
{
if (output_word)
emit_word(0xFFFF);
else
emit_byte(0xFF);
}
if (column)
{
put_char('\n');
column = 0;
}
//
// write out sections data
//
if (section_style)
{
// address
put_char('\n');
if (dot_style)
put_stringf(" .global %s_address\n", prefix.c_str());
else
put_stringf(" PUBLIC %s_address\n", prefix.c_str());
put_stringf("%s_address\n", prefix.c_str());
interval x = range;
while (!x.empty())
{
interval x2 = x;
x2.first_interval_only();
x -= x2;
char buffer[20];
if (hex_style)
snprintf(buffer, sizeof(buffer), "0x%8.8lX", x2.get_lowest());
else
snprintf(buffer, sizeof(buffer), "%lu", x2.get_lowest());
long len = strlen(buffer);
if (column && column + len + 2 > line_length)
{
put_char('\n');
column = 0;
}
if (column == 0)
{
if (dot_style)
put_stringf(" .long ");
else
put_stringf(" DL ");
column = 16;
}
else
{
put_stringf(", ");
column += 2;
}
put_string(buffer);
column += len;
}
if (column)
{
put_char('\n');
column = 0;
}
// length_of_sections
put_char('\n');
if (dot_style)
{
put_stringf
(
" .global %s_length_of_sections\n",
prefix.c_str()
);
}
else
{
put_stringf
(
" PUBLIC %s_length_of_sections\n",
prefix.c_str()
);
}
put_stringf("%s_length_of_sections\n", prefix.c_str());
long nsections = 0;
x = range;
while (!x.empty())
{
interval x2 = x;
x2.first_interval_only();
x -= x2;
++nsections;
unsigned long slen = x2.get_highest() - x2.get_lowest();
if (output_word)
slen /= 2;
char buffer[30];
if (hex_style)
snprintf(buffer, sizeof(buffer), "0x%8.8lX", slen);
else
snprintf(buffer, sizeof(buffer), "%lu", slen);
long len = strlen(buffer);
if (column && column + len + 2 > line_length)
{
put_char('\n');
column = 0;
}
if (column == 0)
{
if (dot_style)
put_stringf(" .long ");
else
put_stringf(" DL ");
column = 16;
}
else
{
put_stringf(", ");
column += 2;
}
put_string(buffer);
column += len;
}
if (column)
{
put_char('\n');
column = 0;
}
// sections
put_char('\n');
if (dot_style)
put_stringf(" .global %s_sections\n", prefix.c_str());
else
put_stringf(" PUBLIC %s_sections\n", prefix.c_str());
put_stringf("%s_sections\n", prefix.c_str());
if (dot_style)
put_string(" .long ");
else
put_string(" DL ");
if (hex_style)
put_stringf("0x%4.4lX\n", nsections);
else
put_stringf("%lu\n", nsections);
}
if (!data_only_flag)
{
put_stringf("; upper bound = 0x%4.4lX\n", range.get_highest());
put_stringf("; lower bound = 0x%4.4lX\n", range.get_lowest());
}
unsigned long len = range.get_highest() - range.get_lowest();
put_stringf("; length = 0x%4.4lX\n", len);
if (section_style)
{
if (dot_style)
put_stringf(" .end\n");
else
put_stringf(" END\n");
}
}
void
srec_output_file_asm::write(const srec_record & record)
{
switch (record.get_type())
{
default:
// ignore
break;
case srec_record::type_header:
// emit header records as comments in the file
{
bool bol = true;
const unsigned char *cp = record.get_data();
const unsigned char *ep = cp + record.get_length();
while (cp < ep)
{
int c = *cp++;
if (c == '\n')
{
put_char(c);
bol = true;
continue;
}
if (bol)
put_string("; ");
if (isprint(c))
put_char(c);
bol = false;
}
if (!bol)
put_char('\n');
}
break;
case srec_record::type_data:
//
// emit the data prelude, if we have not done so already
//
if (section_style && range.empty())
{
if (dot_style)
{
put_stringf(" .global %s\n", prefix.c_str());
put_stringf(" .text\n");
put_stringf("%s\n", prefix.c_str());
}
else
{
put_stringf(" PUBLIC %s\n", prefix.c_str());
put_stringf(" RSEG CODE\n");
put_stringf("%s\n", prefix.c_str());
}
}
if (current_address != record.get_address())
{
current_address = record.get_address();
if (!section_style)
{
if (column)
{
put_char('\n');
column = 0;
}
const char *org = dot_style ? ".org" : "ORG";
if (range.empty())
{
put_stringf
(
"; To avoid this next %s directive, use the "
"--offset -0x%lX filter.\n",
org,
current_address
);
}
else if (!org_warn)
{
org_warn = true;
put_stringf
(
"; To avoid this next %s directive, use the "
"--fill filter.\n",
org
);
}
put_stringf(" %-7s %lu\n", org, current_address);
}
}
if (output_word)
{
int len = record.get_length();
if (len & 1)
fatal_alignment_error(2);
range += interval(record.get_address(), record.get_address() + len);
//
// No attempt is made to align the data on even byte
// boundaries, use the --fill --range-pad filter for that.
//
for (int j = 0; j < len; j += 2)
{
unsigned char n1 = record.get_data(j);
unsigned char n2 = record.get_data(j + 1);
// little-endian
unsigned short n = n1 + (n2 << 8);
emit_word(n);
}
}
else
{
range +=
interval
(
record.get_address(),
record.get_address() + record.get_length()
);
for (int j = 0; j < record.get_length(); ++j)
{
emit_byte(record.get_data(j));
}
}
break;
case srec_record::type_start_address:
taddr = record.get_address();
if (!data_only_flag)
{
if (column)
{
put_char('\n');
column = 0;
}
put_stringf("; start addr = 0x%4.4lX\n", taddr);
}
break;
}
}
void
srec_output_file_asm::line_length_set(int n)
{
line_length = n;
}
void
srec_output_file_asm::address_length_set(int)
{
// ignore
}
int
srec_output_file_asm::preferred_block_size_get()
const
{
//
// Use the largest we can get.
//
if (output_word)
return (srec_record::max_data_length & ~1);
return srec_record::max_data_length;
}
const char *
srec_output_file_asm::format_name()
const
{
return (output_word ? "Assembler (16-bit)" : "Assembler (8-bit)");
}