You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

564 lines
14 KiB

/* Routines for reading/writing Intel INHX8M and INHX32 files
Copyright 2002 Brandon Fosdick (BSD License)
*/
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include "intelhex.h"
#include <string>
namespace intelhex
{
#define INH32M_HEADER ":020000040000FA"
// Array access operator
value_type& hex_data::operator[](address_type address)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while (i != blocks.rend())
{
if (i->first <= address)
{
// Use the block if address is interior or adjacent to the block
if ((address - i->first) <= i->second.size())
return i->second[address - i->first];
break;
}
++i;
}
return blocks[address][0];
}
// Return the value at address, or _fill if not set
value_type hex_data::get(address_type address)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while (i != blocks.rend())
{
if (i->first <= address)
{
// Use the block if address is interior to the block
if ((address - i->first) < i->second.size())
return i->second[address - i->first];
break;
}
++i;
}
return _fill;
}
// Set the value at address or create a new element using value
void hex_data::set(address_type address, value_type value)
{
if (value == fill()) // Handle fill values
{
erase(address); // If the address is already set, erase it
return;
}
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while (i != blocks.rend())
{
if (i->first <= address)
{
// Use the block if address is interior or adjacent to the block
const address_type index = address - i->first;
if (index < i->second.size())
{
i->second[index] = value;
return;
}
else if (index == i->second.size())
{
i->second.push_back(value);
return;
}
break;
}
++i;
}
blocks[address].push_back(value); // Otherwise create a new block
}
// Merge adjacent blocks
void hex_data::compact()
{
iterator previous = blocks.begin();
iterator i = previous;
for (++i; i != blocks.end(); ++i)
{
if ((previous->first + previous->second.size()) == i->first)
{
previous->second.insert(previous->second.end(), i->second.begin(), i->second.end());
blocks.erase(i);
i = previous;
}
previous = i;
}
}
// Delete all allocated memory
void hex_data::clear()
{
_fill = 0;
format = HEX_FORMAT_INHX8M;
linear_addr_rec = false;
segment_addr_rec = false;
blocks.clear();
}
// Erase a single element at the given address
void hex_data::erase(address_type address)
{
for (iterator i = blocks.begin(); i != blocks.end(); ++i)
{
// The blocks are sorted, so if the byte to be deleted is
// before the block it must be a blank address that's either
// before the first block or after any previous blocks.
if (address < i->first)
break;
// Ignore the block if address is past the end of the block
const address_type ope = i->first + i->second.size();
if (address >= ope)
continue;
// address is now guaranteed to be >= i->first and < ope
// Copy trailing portion of the old block to a new block
if ((ope - address) > 1)
{
const address_type index = address - i->first + 1;
blocks[address + 1].assign(i->second.begin() + index, i->second.end());
}
// Truncate or delete old block
const address_type size = address - i->first;
if (size)
i->second.resize(size);
else
blocks.erase(i);
break;
}
}
// Erase [first, last]
void hex_data::erase(address_type first, address_type last)
{
if (first > last)
std::swap(first, last);
for (iterator i = blocks.begin(); (i != blocks.end()) && (first <= last); ++i)
{
const address_type ope = i->first + i->second.size();
if (first >= ope) // Ignore all blocks with addresses < first
continue;
// The blocks are sorted, so if the first byte to be deleted is
// before the block it must be a blank address that's either
// before the first block or after any previous blocks.
if (first < i->first)
{
if (last < i->first) // If the entire range is before the
return; // block there's nothing left to do
first = i->first; // Advance to the next non-blank address
}
// first is now guaranteed to be >= i->first and < ope
if (last < ope) // Entire range is interior
{
// Copy trailing portion of the old block to a new block
if ((ope - last) > 1)
{
const address_type index = last - i->first + 1;
blocks[last + 1].assign(i->second.begin() + index, i->second.end());
}
// Truncate or delete old block
const address_type size = first - i->first;
if (size)
i->second.resize(size);
else
blocks.erase(i);
return;
}
else // Truncate block
{
const address_type size = first - i->first;
if (size)
i->second.resize(size);
else
blocks.erase(i--);
first = ope;
}
}
}
hex_data::size_type hex_data::size()
{
size_type s = 0;
for (iterator i = blocks.begin(); i != blocks.end(); ++i)
s += i->second.size();
return s;
}
// Returns the number of populated elements with addresses less than addr
hex_data::size_type hex_data::size_below_addr(address_type addr)
{
size_type s = 0;
for (iterator i = blocks.begin(); i != blocks.end(); ++i)
{
if ((i->first + i->second.size()) < addr)
s += i->second.size();
else if (i->first < addr)
s += addr - i->first;
}
return s;
}
// number of words in [lo, hi)
hex_data::size_type hex_data::size_in_range(address_type lo, address_type hi)
{
size_type s = 0;
for (iterator i = blocks.begin(); i != blocks.end(); ++i)
{
if (i->first < lo)
{
const size_type a = i->first + i->second.size();
if (a >= lo)
s += a - lo;
}
else
{
if ((i->first + i->second.size()) < hi)
s += i->second.size();
else if (i->first < hi)
s += hi - i->first;
}
}
return s;
}
// Return the max address of all of the set words with addresses less than or equal to hi
address_type hex_data::max_addr_below(address_type hi)
{
address_type s = 0;
for (iterator i = blocks.begin(); i != blocks.end(); ++i)
{
if (i->first <= hi)
{
const address_type a = i->first + i->second.size() - 1; //Max address of this block
if (a > s)
s = a;
}
}
if (s > hi)
return hi;
else
return s;
}
// Lowest address
address_type hex_data::min_address() const
{
return blocks.begin()->first;
}
// Highest address
address_type hex_data::max_address() const
{
return blocks.rbegin()->first + blocks.rbegin()->second.size() - 1;
}
//Return true if an element exists at addr
bool hex_data::is_set(address_type addr)
{
// Start at the end of the list and find the first (last) block with an address
// less than addr
reverse_iterator i = blocks.rbegin();
while ((i != blocks.rend()) && (i->first > addr))
++i;
if ((addr - i->first) >= i->second.size())
return false;
else
return true;
}
// Load from a file
void hex_data::load(const std::string& path)
{
std::ifstream f(path.c_str());
read(f);
}
// Convert a string from hex to binary and append it to a block
uint8_t hex2binary(hex_data::data_container& to, std::string& from)
{
value_type sum = 0, value;
uint8_t character;
bool first = true;
std::string::iterator i = from.begin();
while (i != from.end())
{
character = *i;
if ((character >= '0') && (character <= '9'))
character -= '0';
else if ((character >= 'A') && (character <= 'Z'))
character -= 'A' - 10;
else if ((character >= 'a') && (character <= 'z'))
character -= 'a' - 10;
else
break; // Bad character
if (first)
value = character << 4;
else
{
value |= character;
to.push_back(value);
sum += value;
}
first = !first;
++i;
}
return sum;
}
// Read data from an input stream
void hex_data::read(std::istream& s)
{
address_type address;
address_type extended_address(0);
std::string line;
data_container buffer;
while ((s.get() == ':') && s.good())
{
getline(s, line); // Read the whole line
if (line.size() <= 10) // Ignore truncated lines
break;
buffer.clear();
buffer.reserve(line.size() / 2); // Pre-allocate
if (hex2binary(buffer, line)) // Ignore lines with bad checksums
break;
address = buffer[1];
address = (address << 8) | buffer[2];
unsigned length = buffer[0];
const unsigned type = buffer[3];
value_type* data = &buffer[4];
switch (type)
{
case 0: //Data block
{
address += extended_address;
iterator i = blocks.begin();
for (; i != blocks.end(); ++i) // Find a block that includes address
{
address_type num = 0;
// If the start of the new block is interior to an existing block...
if ((i->first <= address) && ((i->first + i->second.size()) > address))
{
// Store the portion of the new block that overlaps the existing block
const size_type index = address - i->first;
num = i->second.size() - index;
if (num > length)
num = length;
std::copy(data, data + num, &(i->second[index]));
}
// If the end of the new block is interior to an existing block...
if ((address < i->first) && ((address + length) > i->first))
{
// Create a new block for the non-overlapping portion
num = i->first - address;
if (num > length)
num = length;
blocks[address].assign(data, data + num);
}
length -= num;
address += num;
data += num;
// Bail out early if there's nothing left to do
if (0 == length)
break;
}
// Handle any leftover bytes
if (length)
blocks[address].assign(data, data + length);
break;
}
case 1: break; // Ignore EOF record
case 2: // Segment address record (INHX32)
if ((0 == address) && (2 == length))
{
extended_address = buffer[4];
extended_address = (extended_address << 8) | buffer[5];
extended_address <<= 4;
segment_addr_rec = true;
}
break;
case 4: // Linear address record (INHX32)
if ((0 == address) && (2 == length))
{
extended_address = buffer[4];
extended_address = (extended_address << 8) | buffer[5];
extended_address <<= 16;
linear_addr_rec = true;
}
break;
}
}
}
// Write all data to a file
void hex_data::write(const char* path)
{
std::ofstream ofs(path);
if (!ofs) // Bail out on bad files
return;
write(ofs);
ofs.close();
}
// Write all data to an output stream
void hex_data::write(std::ostream& os)
{
uint8_t checksum;
uint16_t linear_address(0);
if (!os) // Bail out on bad streams
return;
os.setf(std::ios::hex, std::ios::basefield); //Set the stream to ouput hex instead of decimal
os.setf(std::ios::uppercase); //Use uppercase hex notation
os.fill('0'); //Pad with zeroes
//If we already know that this is an INHX32M file, start with a segment address record
// otherwise check all of the blocks just to make sure
if (linear_addr_rec)
{
os << INH32M_HEADER << std::endl;
}
else
{
for (iterator i = blocks.begin(); i != blocks.end(); i++)
{
if (i->first > 0xFFFF) //Check the upper 16 bits
{
linear_addr_rec = true;
os << INH32M_HEADER << std::endl;
break; //Only need to find one
}
}
}
for (iterator i = blocks.begin(); i != blocks.end(); i++)
{
// Check upper 16 bits of the block address for non-zero,
// which indicates that a segment address record is needed
if (i->first > 0xFFFF)
{
const uint16_t addr(i->first >> 16);
//Has a record for this segment already been emitted?
if (addr != linear_address)
{
//Emit a new segment address record
os << ":02000004";
os.width(4);
os << addr; //Address
// Create a checksum for the linear address record
checksum = 0x06 + addr + (addr >> 8);
checksum = 0x01 + ~checksum;
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t
// arguments to a stream
os << static_cast<uint16_t>(checksum); // Checksum byte
os << std::endl;
linear_address = addr;
}
}
checksum = 0;
os << ':'; //Every line begins with ':'
os.width(2);
os << i->second.size(); //Length
checksum += i->second.size();
os.width(4);
os << static_cast<uint16_t>(i->first); //Address
checksum += static_cast<uint8_t>(i->first); // Low byte
checksum += static_cast<uint8_t>(i->first >> 8); // High byte
os << "00"; //Record type
for (unsigned j = 0; j < i->second.size(); ++j) //Store the data bytes, LSB first, ASCII HEX
{
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t
// arguments to a stream
os << static_cast<uint16_t>(i->second[j]);
checksum += i->second[j];
}
checksum = 0x01 + ~checksum;
os.width(2);
// OSX (or maybe GCC), seems unable to handle uint8_t arguments to a stream
os << static_cast<uint16_t>(checksum); // Checksum byte
os << std::endl;
}
os << ":00000001FF\n"; //EOF marker
}
// Make things pretty
// Truncate blocks to a given length as needed
void hex_data::tidy(hex_data::size_type length)
{
for (iterator i = blocks.begin(); i != blocks.end(); i++)
{
if (i->second.size() > length) //If the block is too long...
{
//Make an interator that points to the first element to copy out of i->second
data_container::iterator k(i->second.begin());
advance(k, length);
// Assign the extra elements to a new block and truncate the original
blocks[i->first + length].assign(k, i->second.end());
i->second.erase(k, i->second.end());
}
}
}
//Compare two sets of hex data
// Return true if every word in hex1 has a corresponding, and equivalent, word in hex2
bool compare(hex_data& hex1, hex_data& hex2, value_type mask, address_type begin, address_type end)
{
//Walk block list from hex1
for (hex_data::iterator i = hex1.begin(); i != hex1.end(); ++i)
{
//Walk the block
address_type addr(i->first);
for (hex_data::data_container::iterator j = i->second.begin(); j != i->second.end(); ++j, ++addr)
{
if ((addr < begin) || (addr > end))
continue;
//Compare both sides through the given mask
if (((*j) & mask) != (hex2.get(addr) & mask))
return false;
}
}
return true;
}
}