/* Filename: intelhex.cc
* Routines for reading/writing Intel INHX8M and INHX32 files
Copyright (c) 2002, Terran Development Corporation
All rights reserved.
This code is made available to the public under a BSD-like license, a copy of which
should have been provided with this code in the file LICENSE. For a copy of the BSD
license template please visit http://www.opensource.org/licenses/bsd-license.php
$Id: intelhex.cc,v 1.3 2007/06/17 05:03:19 bfoz Exp $
* */
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "intelhex.h"
namespace intelhex
{
#define INH32M_HEADER ":020000040000FA"
/* hex_data::hex_data()
{
format = HEX_FORMAT_INHX8M;
}
*/
//Extend the data block array by one element
// and return a pointer to the new element
hex_data::dblock* hex_data::new_block()
{
dblock b;
blocks.push_back(b);
return &blocks.back();
}
//Extend the data block array by one element
// and return a pointer to the new element
// Initialize the element with address and length
hex_data::dblock* hex_data::add_block(address_t address, size_type length, element fill)
{
dblock db; //A list of pointers would be faster, but this isn't too bad
blocks.push_back(db);
blocks.back().first = address;
blocks.back().second.resize(length, fill);
return &blocks.back();
}
//Array access operator
//Assumes that the blocks have been sorted by address in ascending order
// Sort order is maintained
hex_data::element &hex_data::operator[](hex_data::address_t addr)
{
if(blocks.size() == 0) //Add a block if the sequence is empty
add_block(0,0);
//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;
element relative_addr = addr - i->first;
//If addr is outside of a block and not-adjacent to the end of any block add a new block
if( relative_addr > i->second.size() )
return add_block(addr,1)->second[0];
//If addr is adjacent to the end of the block resize it
if( relative_addr == i->second.size() )
i->second.resize(i->second.size()+1, 0xFFFF);
return i->second[relative_addr];
}
//FIXME Nasty kludge
// I should really create an iterator class to handle this
hex_data::element hex_data::get(address_t addr, element blank)
{
//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 no block can be found, return the blank value
if( i == blocks.rend() )
return blank;
element relative_addr = addr - i->first;
//If relative_addr is past the end of the block, return blank
if( relative_addr >= i->second.size() )
return blank;
// std::cout << __FUNCTION__ << ": addr = " << std::hex << addr << "\n";
// std::cout << __FUNCTION__ << ": blank = " << std::hex << blank << "\n";
// std::cout << __FUNCTION__ << ": i->first = " << std::hex << i->first << "\n";
// std::cout << __FUNCTION__ << ": i->second.size() = " << std::hex << i->second.size() << "\n";
// std::cout << __FUNCTION__ << "2\n";
return i->second[relative_addr];
}
// Delete all allocated memory
void hex_data::clear()
{
format = HEX_FORMAT_INHX8M;
blocks.clear();
}
//Add a new word to the end of the sequence
// Assumes the sequence has been sorted
void hex_data::push_back(hex_data::element a)
{
if(blocks.size() == 0) //Add a block if the sequence is empty
add_block(0,0);
blocks.back().second.push_back(a); //Append the new word
}
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_t addr)
{
size_type s=0;
// std::cout << __FUNCTION__ << ": addr = " << std::hex << addr << std::endl;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
// std::cout << __FUNCTION__ << ": i->first = " << std::hex << i->first << std::endl;
// std::cout << __FUNCTION__ << ": i->second.size = " << std::hex << i->second.size() << std::endl;
if( (i->first + i->second.size()) < addr)
s += i->second.size();
else if( i->first < addr )
s += addr - i->first;
}
// std::cout << __FUNCTION__ << ": s = " << std::hex << s << std::endl;
return s;
}
//number of words in [lo, hi)
hex_data::size_type hex_data::size_in_range(address_t lo, address_t 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
hex_data::address_t hex_data::max_addr_below(address_t hi)
{
address_t s=0;
// std::cout << __FUNCTION__ << ": hi = " << hi << std::endl;
for(iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
if( i->first <= hi)
{
const address_t a = i->first + i->second.size() - 1; //Max address of this block
// std::cout << __FUNCTION__ << ": a = " << a << std::endl;
if( a > s )
s = a;
}
}
if( s > hi )
return hi;
else
return s;
}
//Return true if an element exists at addr
bool hex_data::isset(address_t 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 a hex file from disk
//Destroys any data that has already been loaded
bool hex_data::load(const char *path)
{
FILE *fp;
dblock *db; //Temporary pointer
unsigned int hi, lo, address, count, rtype, i, j;
uint16_t linear_address(0);
uint32_t a;
if( (fp=fopen(path, "r"))==NULL )
{
// printf("%s: Can't open %s\n", __FUNCTION__, path);
return false;
}
clear(); //First, clean house
//Start parsing the file
while(!feof(fp))
{
if(fgetc(fp)==':') //First character of every line should be ':'
{
// std::cout << __FUNCTION__ << ": Got line" << std::endl;
fscanf(fp, "%2x", &count); //Read in byte count
fscanf(fp, "%4x", &address); //Read in address
fscanf(fp, "%2x", &rtype); //Read type
count /= 2; //Convert byte count to word count
address /= 2; //Byte address to word address
switch(rtype) //What type of record?
{
case 0: //Data block so store it
//Make a data block
a = (static_cast<uint32_t>(linear_address) << 16) + address;
db = add_block(a, count);
// std::cout << __FUNCTION__ << ": db->first = " << std::hex << db->first << std::endl;
// std::cout << __FUNCTION__ << ": db->first*2 = " << std::hex << (db->first*2) << std::endl;
for(i=0; i<count; i++) //Read all of the data bytes
{
fscanf(fp, "%2x", &lo); //Low byte
fscanf(fp, "%2x", &hi); //High byte
db->second[i] = ((hi<<8)&0xFF00) | (lo&0x00FF); //Assemble the word
}
break;
case 1: //EOF
break;
case 2: //Segment address record (INHX32)
segment_addr_rec = true;
break;
case 4: //Linear address record (INHX32)
if(address == 0x0000)
{
fscanf(fp, "%4x", &linear_address); //Get the new linear address
linear_addr_rec = true;
}
else
{
//FIXME There's a problem
}
break;
}
fscanf(fp,"%*[^\n]\n"); //Ignore the checksum and the newline
}
else
{
printf("%s: Bad line\n", __FUNCTION__);
fscanf(fp, "%*[^\n]\n"); //Ignore the rest of the line
}
}
fclose(fp);
blocks.sort(); //Sort the data blocks by address (ascending)
return true;
}
//Write all data to a file
void hex_data::write(const char *path)
{
std::ofstream ofs(path);
if(!ofs)
{
std::cerr << "Couldn't open the output file stream\n";
exit(1);
}
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)
{
std::cerr << "Couldn't open the output file stream\n";
exit(1);
}
truncate(8); //Truncate each record to length=8 (purely aesthetic)
blocks.sort(); //Sort the data blocks by address (ascending)
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::cout << __FUNCTION__ << ": linear_addr_rec == true\n";
}
else
{
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
{
if(i->first & 0xFFFF0000) //Check the upper 16 bits
{
linear_addr_rec = true;
os << INH32M_HEADER;
// std::cout << __FUNCTION__ << ": Found an 04 at " << i->first << std::endl;
// std::cout << __FUNCTION__ << ": i->first & 0xFFFF0000 == " << (i->first & 0xFFFF0000) << 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 & 0xFFFF0000) != 0 )
{
//Has a record for this segment already been emitted?
if( static_cast<uint16_t>(i->first >> 16) != linear_address )
{
//Emit a new segment address record
os << ":02000004";
os.width(4);
os << linear_address; //Address
os << (0x01 + ~(0x06 + ((linear_address>>8)&0xFF) + (linear_address&0xFF)));
os << std::endl;
linear_address = (i->first & 0xFFFF0000) >> 16; //Update segment_address
}
}
checksum = 0;
os << ':'; //Every line begins with ':'
os.width(2);
os << i->second.size()*2; //Record length
checksum += i->second.size()*2;
os.width(4);
os << static_cast<uint16_t>(i->first*2); //Address
checksum += static_cast<uint8_t>(i->first & 0x00FF);
checksum += static_cast<uint8_t>(i->first >> 8);
os << "00"; //Record type
for(unsigned j=0; j<i->second.size(); ++j) //Store the data bytes, LSB first, ASCII HEX
{
os.width(2);
os << (i->second[j] & 0x00FF);
os.width(2);
os << ((i->second[j]>>8) & 0x00FF);
checksum += static_cast<uint8_t>(i->second[j] & 0x00FF);
checksum += static_cast<uint8_t>(i->second[j] >> 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); //Bogus checksum byte
os << std::endl;
}
os << ":00000001FF\n"; //EOF marker
}
//Truncate all of the blocks to a given length
// Maintains sort order
void hex_data::truncate(hex_data::size_type len)
{
for(iterator i=blocks.begin(); i!=blocks.end(); i++)
{
if(i->second.size() > len) //If the block is too long...
{
//Insert a new block
iterator j(i);
j = blocks.insert(++j, dblock());
j->first = i->first + len; //Give the new block an address
//Make an interator that points to the first element to copy out of i->second
dblock::second_type::iterator k(i->second.begin());
advance(k, len);
j->second.assign(k, i->second.end()); //Assign the extra bytes to the new block
i->second.erase(k, i->second.end()); //Truncate the original block
}
}
}
//Compare two sets of hex data
// Return true is every word in hex1 has a corresponding, and equivalent, word in hex2
// Assumes both data sets are sorted
bool compare(hex_data& hex1, hex_data& hex2)
{
//Walk block list from hex1
for( hex_data::iterator i = hex1.begin(); i != hex1.end(); ++i )
{
//Walk the block
hex_data::address_t addr(i->first);
for( hex_data::data_container::iterator j = i->second.begin(); j != i->second.end(); ++j)
{
if( (*j) != hex2.get(addr, 0xFFFF) )
return false;
++addr;
}
}
return true;
}
}
syntax highlighted by Code2HTML, v. 0.9.1