mirror of
https://github.com/stargieg/bacnet-stack
synced 2025-10-26 23:35:52 +08:00
365 lines
9.5 KiB
C++
365 lines
9.5 KiB
C++
/*****************************************************************************
|
|
*
|
|
* Atmel Corporation
|
|
*
|
|
* File : HEXParser.cpp
|
|
* Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/
|
|
* Revision : $Revision: 1163 $
|
|
* Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $
|
|
* Updated by : $Author: ohlia $
|
|
*
|
|
* Support mail : avr@atmel.com
|
|
*
|
|
* Target platform : Win32
|
|
*
|
|
* AppNote : AVR911 - AVR Open-source Programmer
|
|
*
|
|
* Description : A simple Intel HEX file format reader/writer.
|
|
*
|
|
*
|
|
****************************************************************************/
|
|
#include "HEXParser.hpp"
|
|
|
|
|
|
/* Internal struct for managing HEX records */
|
|
struct HEXRecord // Intel HEX file record
|
|
{
|
|
unsigned char length; // Record length in number of data bytes.
|
|
unsigned long offset; // Offset address.
|
|
unsigned char type; // Record type.
|
|
unsigned char * data; // Optional data bytes.
|
|
};
|
|
|
|
|
|
void HEXFile::writeRecord( ofstream & f, HEXRecord * recp )
|
|
{
|
|
unsigned char checksum;
|
|
long recordPos; // Position inside record data field
|
|
|
|
/* Calculate checksum */
|
|
checksum = recp->length;
|
|
checksum += (unsigned char) ((recp->offset >> 8) & 0xff);
|
|
checksum += (unsigned char) (recp->offset & 0xff);
|
|
checksum += recp->type;
|
|
|
|
/* Write record header */
|
|
f.fill('0');
|
|
f << ":" << hex
|
|
<< setw(2) << (long) recp->length
|
|
<< setw(4) << (long) recp->offset
|
|
<< setw(2) << (long) recp->type;
|
|
|
|
/* Write data bytes */
|
|
for( recordPos = 0; recordPos < recp->length; recordPos++ )
|
|
{
|
|
checksum += recp->data[ recordPos ]; // Further checksum calculation
|
|
f << hex << setw(2) << (long) recp->data[ recordPos ];
|
|
}
|
|
|
|
/* Write checksum */
|
|
checksum = 0 - checksum; // Final checksum preparation
|
|
f << setw(2) << (long) checksum << endl;
|
|
|
|
/* Check for errors */
|
|
if( !f.good() )
|
|
throw new ErrorMsg( "Error writing HEX record to file!" );
|
|
}
|
|
|
|
|
|
void HEXFile::parseRecord( const string & hexLine, HEXRecord * recp )
|
|
{
|
|
unsigned char checksum;
|
|
long recordPos; // Position inside record data fields.
|
|
|
|
if( hexLine.size() < 11 ) // At least 11 characters.
|
|
throw new ErrorMsg( "Wrong HEX file format, missing fields! "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
|
|
/* Check format for line */
|
|
if( hexLine[0] != ':' ) // Always start with colon.
|
|
throw new ErrorMsg( "Wrong HEX file format, does not start with colon! "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
|
|
/* Parse length, offset and type */
|
|
recp->length = Util.convertHex( hexLine.substr( 1, 2 ) );
|
|
recp->offset = Util.convertHex( hexLine.substr( 3, 4 ) );
|
|
recp->type = Util.convertHex( hexLine.substr( 7, 2 ) );
|
|
|
|
/* We now know how long the record should be */
|
|
if( hexLine.size() < (11+recp->length*2) )
|
|
throw new ErrorMsg( "Wrong HEX file format, missing fields! "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
|
|
/* Process checksum */
|
|
checksum = recp->length;
|
|
checksum += (unsigned char) ((recp->offset >> 8) & 0xff);
|
|
checksum += (unsigned char) (recp->offset & 0xff);
|
|
checksum += recp->type;
|
|
|
|
/* Parse data fields */
|
|
if( recp->length )
|
|
{
|
|
recp->data = new unsigned char[ recp->length ];
|
|
|
|
/* Read data from record */
|
|
for( recordPos = 0; recordPos < recp->length; recordPos++ )
|
|
{
|
|
recp->data[ recordPos ] = Util.convertHex( hexLine.substr( 9 + recordPos*2, 2 ) );
|
|
checksum += recp->data[ recordPos ];
|
|
}
|
|
}
|
|
|
|
/* Correct checksum? */
|
|
checksum += Util.convertHex( hexLine.substr( 9 + recp->length*2, 2 ) );
|
|
if( checksum != 0 )
|
|
{
|
|
throw new ErrorMsg( "Wrong checksum for HEX record! "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Constructor */
|
|
HEXFile::HEXFile( long buffersize, long value )
|
|
{
|
|
if( buffersize <= 0 )
|
|
throw new ErrorMsg( "Cannot have zero-size HEX buffer!" );
|
|
|
|
data = new unsigned char[ buffersize ];
|
|
|
|
if( !data )
|
|
throw new ErrorMsg( "Memory allocation failed for HEX-line-buffer!" );
|
|
|
|
size = buffersize;
|
|
|
|
clearAll( value );
|
|
}
|
|
|
|
|
|
/* Destructor */
|
|
HEXFile::~HEXFile()
|
|
{
|
|
if( data ) delete data;
|
|
}
|
|
|
|
|
|
void HEXFile::readFile( const string & _filename )
|
|
{
|
|
ifstream f;
|
|
string hexLine; // Contains one line of the HEX file.
|
|
HEXRecord rec; // Temp record.
|
|
|
|
long baseAddress; // Base address for extended addressing modes.
|
|
long dataPos; // Data position in record.
|
|
|
|
/* Attempt to open file */
|
|
f.open( _filename.c_str(), ios::in );
|
|
if( !f )
|
|
throw new ErrorMsg( "Error opening HEX file for input!" );
|
|
|
|
/* Prepare */
|
|
baseAddress = 0;
|
|
start = size;
|
|
end = 0;
|
|
|
|
/* Parse records */
|
|
f >> hexLine; // Read one line.
|
|
while( !f.eof() )
|
|
{
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
|
|
/* Process record according to type */
|
|
parseRecord( hexLine, &rec );
|
|
|
|
switch( rec.type )
|
|
{
|
|
case 0x00 : // Data record ?
|
|
/* Copy data */
|
|
if( baseAddress + rec.offset + rec.length > size )
|
|
throw new ErrorMsg( "HEX file defines data outside buffer limits! "
|
|
"Make sure file does not contain data outside device "
|
|
"memory limits. "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
|
|
for( dataPos = 0; dataPos < rec.length; dataPos++ )
|
|
data[ baseAddress + rec.offset + dataPos ] = rec.data[ dataPos ];
|
|
|
|
/* Update byte usage */
|
|
if( baseAddress + rec.offset < start )
|
|
start = baseAddress + rec.offset;
|
|
|
|
if( baseAddress + rec.offset + rec.length - 1 > end )
|
|
end = baseAddress + rec.offset + rec.length - 1;
|
|
|
|
break;
|
|
|
|
|
|
case 0x02 : // Extended segment address record ?
|
|
baseAddress = (rec.data[0] << 8) | rec.data[1];
|
|
baseAddress <<= 4;
|
|
break;
|
|
|
|
case 0x03 : // Start segment address record ?
|
|
break; // Ignore it, since we have no influence on execution start address.
|
|
|
|
case 0x04 : // Extended linear address record ?
|
|
baseAddress = (rec.data[0] << 8) | rec.data[1];
|
|
baseAddress <<= 16;
|
|
break;
|
|
|
|
case 0x05 : // Start linear address record ?
|
|
break; // Ignore it, since we have no influence on exectuion start address.
|
|
|
|
case 0x01 : // End of file record ?
|
|
f.close();
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
return;
|
|
|
|
default:
|
|
throw new ErrorMsg( "Unsupported HEX record format! "
|
|
"Line from file was: (" + hexLine + ")." );
|
|
}
|
|
|
|
f >> hexLine; // Read next line.
|
|
}
|
|
|
|
|
|
/* We should not end up here */
|
|
throw new ErrorMsg( "Premature end of file encountered! Make sure file "
|
|
"contains an EOF-record." );
|
|
}
|
|
|
|
|
|
void HEXFile::writeFile( const string & _filename )
|
|
{
|
|
ofstream f;
|
|
HEXRecord rec; // Temp record.
|
|
|
|
long baseAddress; // Absolute data position.
|
|
long offset; // Offset from base address.
|
|
long dataPos; // Position inside data record.
|
|
|
|
enum
|
|
{
|
|
_first,
|
|
_writing,
|
|
_passed64k
|
|
} status; // Write status, see usage below.
|
|
|
|
/* Attempt to create file */
|
|
f.open( _filename.c_str(), ios::out );
|
|
if( !f )
|
|
throw new ErrorMsg( "Error opening HEX file for output!" );
|
|
|
|
/* Prepare */
|
|
status = _first;
|
|
rec.data = new unsigned char[ 16 ]; // Use only 16 byte records.
|
|
|
|
baseAddress = start & ~0xffff; // 64K aligned address.
|
|
offset = start & 0xffff; // Offset from the aligned address.
|
|
dataPos = 0;
|
|
|
|
/* Write first base address record to HEX file */
|
|
rec.length = 2;
|
|
rec.offset = 0;
|
|
rec.type = 0x02;
|
|
rec.data[1] = 0x00;
|
|
rec.data[0] = baseAddress >> 12; // Give 4k page index.
|
|
writeRecord( f, &rec ); // Write the HEX record to file.
|
|
|
|
|
|
/* Write all bytes in used range */
|
|
do
|
|
{
|
|
/* Put data into record */
|
|
rec.data[ dataPos ] = data[ baseAddress + offset + dataPos ];
|
|
dataPos++;
|
|
|
|
/* Check if we need to write out the current data record */
|
|
if( offset + dataPos >= 0x10000 || // Reached 64k boundary?
|
|
dataPos >= 16 || // Data record full?
|
|
baseAddress + offset + dataPos > end ) // End of used range reached?
|
|
{
|
|
/* Write current data record */
|
|
rec.length = dataPos;
|
|
rec.offset = offset;
|
|
rec.type = 0x00; // Data record.
|
|
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
writeRecord( f, &rec );
|
|
|
|
offset += dataPos;
|
|
dataPos = 0;
|
|
}
|
|
|
|
/* Check if we have passed a 64k boundary */
|
|
if( offset + dataPos >= 0x10000 )
|
|
{
|
|
/* Update address pointers */
|
|
offset -= 0x10000;
|
|
baseAddress += 0x10000;
|
|
|
|
/* Write new base address record to HEX file */
|
|
rec.length = 2;
|
|
rec.offset = 0;
|
|
rec.type = 0x02;
|
|
rec.data[0] = baseAddress >> 12; // Give 4k page index.
|
|
rec.data[1] = 0x00;
|
|
|
|
writeRecord( f, &rec ); // Write the HEX record to file.
|
|
}
|
|
} while( baseAddress + offset + dataPos <= end );
|
|
|
|
|
|
/* Write EOF record */
|
|
rec.length = 0;
|
|
rec.offset = 0;
|
|
rec.type = 0x01;
|
|
|
|
writeRecord( f, &rec );
|
|
|
|
f.close();
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
}
|
|
|
|
|
|
void HEXFile::setUsedRange( long _start, long _end )
|
|
{
|
|
if( _start < 0 || _end >= size || _start > _end )
|
|
throw new ErrorMsg( "Invalid range! Start must be 0 or larger, end must be "
|
|
"inside allowed memory range." );
|
|
|
|
start = _start;
|
|
end = _end;
|
|
}
|
|
|
|
|
|
void HEXFile::clearAll( long value )
|
|
{
|
|
for( long i = 0; i < size; i++ )
|
|
data[i] = (unsigned char) (value & 0xff);
|
|
}
|
|
|
|
|
|
long HEXFile::getData( long address )
|
|
{
|
|
if( address < 0 || address >= size )
|
|
throw new ErrorMsg( "Address outside legal range!" );
|
|
|
|
return data[ address ];
|
|
}
|
|
|
|
|
|
void HEXFile::setData( long address, long value )
|
|
{
|
|
if( address < 0 || address >= size )
|
|
throw new ErrorMsg( "Address outside legal range!" );
|
|
|
|
data[ address ] = (unsigned char) (value & 0xff);
|
|
}
|
|
|
|
|
|
/* end of file */
|
|
|