Source file: /~heha/hs/avrpp.zip/src/loadhex.cpp

#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: ASCII (7 bit)2