#include "avrpp.h"
#include <setjmp.h> // lightweight try/throw/catch
/*-----------------------------------------------------------------------
Hex format manipulations
-----------------------------------------------------------------------*/
struct HexParser{
const char*lp,*end; // line read pointer and end pointer
int lnum; // line number (for error reporting)
jmp_buf env; // longjmp environment for lightweight try/throw/catch
byte sum; // checksum
HexParser(const char*start,dword len):lp(start),end(start+len) {}
char readChar();
dword readHex(int=2); // Pick a hexdecimal value from hex record
int parse(dword=0);
};
char HexParser::readChar() {
if (lp==end) longjmp(env,2); // "throw" EOF error
char c=*lp++; // get next character
if (c=='\n') ++lnum; // count current line number
return c;
}
// Pick a hexdecimal value from hex record
// count: number of digits to get (2,4,6,8)
// Doesn't return in case of error
dword HexParser::readHex(int count) {
dword val = 0;
while (count--) {
char c=readChar(); // doesn't return in case of EOF
if (c>=0x40) c&=~0x20; // upcase 'a'..'f' => 'A'..'F'
if (byte(c-='0')>=10 && (byte(c-=7)<10 || c>15)) longjmp(env,3); // wrong hexadecimal digit
val=val<<4|c;
if (!(count&1)) sum += (byte)val;
}
return val;
}
// Input Intel/Motorola hex file into data buffer
// base: Base address to be added to the hex file (for EEP files)
// return: error code, 0 if okay
int HexParser::parse(dword base) {
word seg=0, hadr=0; // address expansion values for intel hex
lnum=1;
int ret=setjmp(env); // "try"
if (ret) return ret; // "catch"
dword addr,count;
for(;;) switch (readChar()) {
case ':': { /* Intel Hex format */
sum=0;
count=readHex(); // byte count
addr=readHex(4); // offset
switch (readHex()) { // block type?
case 0x00: // data block
addr+=(seg<<4)+(hadr<<16);
while (count--) store_buffer((byte)readHex(),base+addr++); // Store it into buffer
break;
case 0x01: return 0; // end: successful
case 0x02: // segment base [19:4]
if (count!=2) return 7; // wrong data length
seg=(word)readHex(4);
break;
case 0x03 : // program start address (segment:offset)
if (count!=4) return 7;
readHex(8); // ignore value here
break;
case 0x04 : // high address base [31:16]
if (count!=2) return 7;
hadr=(word)readHex(4);
break;
case 0x05 : // program start address (linear)
if (count!=4) return 7;
readHex(8); // ignore value here
break;
default: return 6; // invalid block type
}
readHex(); // get check sum
if (sum) return 5; // check sum must be zero
}break;
case 'S': { // Motorola S format
sum=1;
switch (readChar()) { // record type? (S1/S2/S3)
case '1': {
count=readHex()-3;
addr=readHex(4);
}break;
case '2': {
count=readHex()-4;
addr=readHex(6);
}break;
case '3': {
count=readHex()-5;
addr=readHex(8);
}break;
case '0':
case '4': {
count=readHex()-1;
if ((int)count<0) return 7;
readHex(count<<1); // read and ignore bytes (checksum test only)
count=0;
}break;
case '5':
case '6':
case '7': return 0;
default: return 6; // invalid block type
}
if ((int)count<0) return 7; // wrong length
while (count--) store_buffer((byte)readHex(),base+addr++); // Store it into buffer
readHex(); // get check sum
if (sum) return 5; // test check sum
}break;
// ignore all other characters except ":" or "S"
}
}
// Simplifications:
// * Checksum is not tested for end-of-data record
// * ':' or 'S' can be everywhere
// * Intel and Motorola-S records may be intermixed
// * end-of-data record is required, not implicit at end-of-file
// * No intermediate line buffer
// Input Intel/Motorola hex file into data buffer
// Returns erraneous line number, or 0 for success.
int32 loadhex (
const char*fdata,dword fsize, // Input file
dword base // Base address to be added to the hex file (for EEP files)
) {
HexParser parser(fdata,fsize);
if (parser.parse(base)) return parser.lnum; // error code discarded for now
return 0;
}
Detected encoding: UTF-8 | 0
|