//
// srecord - manipulate eprom load files
// Copyright (C) 1998-2003, 2005-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
srec_input_file_intel::srec_input_file_intel(const string &a_file_name) :
srec_input_file(a_file_name),
data_record_count(0),
garbage_warning(false),
seen_some_input(false),
termination_seen(false),
mode(linear),
address_base(0),
pushback(0),
end_seen(false)
{
}
srec_input_file_intel::~srec_input_file_intel()
{
if (pushback)
delete pushback;
}
int
srec_input_file_intel::read_inner(srec_record &record)
{
if (pushback)
{
record = *pushback;
delete pushback;
pushback = 0;
return 1;
}
//
// keep reading lines until something returnable comes along
//
for (;;)
{
//
// grab the first character of the line
//
int c = get_char();
//
// end of file means we are done
//
if (c < 0)
return 0;
//
// quietly ignore blank lines
//
if (c == '\n')
continue;
//
// If it doesn't start with a colon, it's a garbage line.
// Warn, and then ignore it.
//
if (c != ':')
{
if (!garbage_warning)
{
warning("ignoring garbage lines");
garbage_warning = true;
}
for (;;)
{
c = get_char();
if (c < 0)
return 0;
if (c == '\n')
break;
}
continue;
}
//
// Looks like a real Intel-hex line.
//
unsigned char buffer[255+5];
checksum_reset();
buffer[0] = get_byte();
buffer[1] = get_byte();
buffer[2] = get_byte();
buffer[3] = get_byte();
for (int j = 0; j <= buffer[0]; ++j)
buffer[4 + j] = get_byte();
if (use_checksums())
{
int n = checksum_get();
if (n != 0)
fatal_error("checksum mismatch (%02X != 00)", n);
}
if (get_char() != '\n')
fatal_error("end-of-line expected");
srec_record::address_t address_field =
srec_record::decode_big_endian(buffer + 1, 2);
srec_record::type_t type = srec_record::type_unknown;
switch (buffer[3])
{
case 0:
//
// data
//
if (mode == linear)
{
//
// linear addressing model
//
if
(
(
(long long)address_base + address_field
<
((long long)1 << 32)
)
&&
(
(long long)address_base + address_field + buffer[0]
>
((long long)1 << 32)
)
)
{
int split =
((long long)1 << 32) - address_base - address_field;
pushback =
new srec_record
(
srec_record::type_data,
0L,
buffer + 4 + split,
buffer[0] - split
);
buffer[0] = split;
}
}
else
{
//
// segmented addressing model
//
if (address_field + buffer[0] > (1L << 16))
{
int split = (1L << 16) - address_field;
pushback =
new srec_record
(
srec_record::type_data,
address_base + ((address_field + split) & 0xFFFF),
buffer + 4 + split,
buffer[0] - split
);
buffer[0] = split;
}
}
type = srec_record::type_data;
break;
case 1:
//
// end-of-file record
//
if (buffer[0] != 0)
fatal_error("length field must be zero");
if (address_field != 0)
fatal_error("address field must be zero");
end_seen = true;
seek_to_end();
return 0;
case 2:
//
// extended segment address record
//
// Set the base address and addressing mode,
// and then loop for another record, this one
// isn't visable to the caller.
//
if (buffer[0] != 2)
fatal_error("length field must be 2");
if (address_field != 0)
fatal_error("address field must be zero");
address_field =
srec_record::decode_big_endian(buffer + 4, 2);
address_base = address_field << 4;
mode = segmented;
continue;
case 3:
//
// start segment address record
//
if (buffer[0] != 4)
fatal_error("length field must be 4");
if (address_field != 0)
fatal_error("address field must be zero");
address_field =
srec_record::decode_big_endian(buffer + 4, 2) * 16 +
srec_record::decode_big_endian(buffer + 6, 2);
record =
srec_record
(
srec_record::type_start_address,
address_field,
0,
0
);
return 1;
case 4:
//
// extended linear address record
//
// Set the base address and addressing mode,
// and then loop for another record, this one
// isn't visable to the caller.
//
if (buffer[0] != 2)
fatal_error("length field must be 2");
if (address_field != 0)
fatal_error("address field must be zero");
address_field =
srec_record::decode_big_endian(buffer + 4, 2);
address_base = address_field << 16;
mode = linear;
continue;
case 5:
//
// start linear address record
//
if (buffer[0] != 4)
fatal_error("length field must be 4");
if (address_field != 0)
fatal_error("address field must be zero");
address_field = srec_record::decode_big_endian(buffer + 4, 4);
record =
srec_record
(
srec_record::type_start_address,
address_field,
0,
0
);
return 1;
}
//
// data record or unknown
//
record =
srec_record
(
type,
address_base + address_field,
buffer + 4,
buffer[0]
);
return 1;
}
}
int
srec_input_file_intel::read(srec_record &record)
{
for (;;)
{
if (!read_inner(record))
{
if (!seen_some_input && garbage_warning)
fatal_error("file contains no data");
if (data_record_count <= 0)
fatal_error("file contains no data");
if (!termination_seen)
{
//
// Eric Weddington: ''From looking at the "Hexadecimal
// Object File Format Specification" from Intel,
// Revision A, January 6, 1988, the Extended Segment
// Address Record (0x02) and the Extended Linear Address
// Record (0x04) are both optional. The respective
// addresses default to 0 until either type of record is
// encounterd.''
//
termination_seen = true;
#if 0
//
// We could synthesize a start address, but that means
// the input and output don't agree when a round-trip
// occurs.
//
record = srec_record(srec_record::type_start_address, 0, 0, 0);
return 1;
#endif
}
if (!end_seen)
{
warning("no end-of-file record");
end_seen = true;
}
return 0;
}
seen_some_input = true;
switch (record.get_type())
{
case srec_record::type_unknown:
fatal_error("record type not recognised");
break;
default:
// impossible
continue;
case srec_record::type_data:
++data_record_count;
if (record.get_length() == 0)
{
warning("empty data record ignored");
continue;
}
break;
case srec_record::type_start_address:
if (termination_seen)
warning("redundant start address record");
termination_seen = true;
break;
}
break;
}
return 1;
}
const char *
srec_input_file_intel::get_file_format_name()
const
{
return "Intel Hexadecimal (MCS-86)";
}