Subversion Repositories pentevo

Rev

Rev 908 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <alloca.h>
  7.  
  8. #include "ihex.h"
  9.  
  10. static int get_hexnibble(char sym)
  11. {
  12.         if( '0'<=sym && sym<='9' )
  13.                 return sym-'0';
  14.         else if( 'A'<=sym && sym<='F' )
  15.                 return sym-'A'+10;
  16.         else if( 'a'<=sym && sym<='f' )
  17.                 return sym-'a'+10;
  18.  
  19.         return -1;
  20. }
  21.  
  22. static int get_hexbyte(char * ptr)
  23. {
  24.         return get_hexnibble(*ptr)*16 + get_hexnibble(*(ptr+1));
  25. }
  26.  
  27. int parse_ihex(uint8_t * buffer, int32_t disp, int32_t low_bound, int32_t high_bound, uint8_t fill, char * filename)
  28. {
  29.         char chbuf[IHEX_BUF_SZ];
  30.  
  31.         int32_t i;
  32.  
  33.         FILE * file;
  34.  
  35.         int line = 1;
  36.  
  37.         int was_eof = 0;
  38.  
  39.         int t;
  40.  
  41.         int32_t seg_addr = 0;
  42.  
  43.  
  44.  
  45.         // open the file
  46.         file = fopen(filename, "rt");
  47.         if( !file )
  48.         {
  49.                 fprintf(stderr,"ERROR: can't open file <%s>: %s!\n",filename,strerror(errno));
  50.                 return 0;
  51.         }
  52.  
  53.  
  54.  
  55.         // fill the whole buffer with given fill value
  56.         for(i=low_bound+disp;i<high_bound+disp;i++)
  57.                 buffer[i] = fill;
  58.  
  59.        
  60.         // parse interhex line by line
  61.         while( memset(chbuf, 0, IHEX_BUF_SZ), fgets(chbuf, IHEX_BUF_SZ, file) )
  62.         {
  63.                 if( strlen(chbuf) > MAX_IHEX_LENGTH )
  64.                 { // check maximum length of ihex string
  65.                         fprintf(stderr,"ERROR: line %d of file <%s> is too long!\n",line,filename);
  66.                         goto GENERAL_ERROR;
  67.                 }
  68.  
  69.                 // ihex line parser
  70.                 int pos=0;
  71.                 //
  72.                 int ihex_length;
  73.                 int ihex_addr;
  74.                 int ihex_type;
  75.                 int ihex_checksum;
  76.  
  77.                 int curr_length;
  78.                 int datapos = 0;
  79.  
  80.  
  81.                 // parser states
  82.                 enum parser_states
  83.                 {
  84.                         ST_INIT,
  85.                         ST_LEN,
  86.                         ST_ADDR,
  87.                         ST_TYPE,
  88.                         ST_DATA,
  89.                         ST_CHECKSUM,
  90.                         ST_DONE
  91.                 };
  92.                 //
  93.                 enum parser_states state = ST_INIT;
  94.  
  95.                 while( chbuf[pos]!='\r' && chbuf[pos]!='\n' && chbuf[pos]!=0 )
  96.                 {
  97.                         switch( state )
  98.                         {
  99.                         case ST_INIT:
  100.                                 if( chbuf[pos]!=':' ) goto PARSE_ERROR;
  101.                                 state = ST_LEN;
  102.                                 pos++;
  103.                                 break;
  104.                         case ST_LEN:
  105.                                 t = get_hexbyte(chbuf+pos);
  106.                                 pos+=2;
  107.                                 if( t<0 ) goto PARSE_ERROR;
  108.                                 state = ST_ADDR;
  109.                                 ihex_length = t;
  110.                                 curr_length = t;
  111.                                 break;
  112.                         case ST_ADDR:
  113.                                 t = get_hexbyte(chbuf+pos);
  114.                                 pos+=2;
  115.                                 if( t<0 ) goto PARSE_ERROR;
  116.                                 ihex_addr = t*256;
  117.                                 t = get_hexbyte(chbuf+pos);
  118.                                 pos+=2;
  119.                                 if( t<0 ) goto PARSE_ERROR;
  120.                                 ihex_addr += t;
  121.                                 state = ST_TYPE;
  122.                                 break;
  123.                         case ST_TYPE:
  124.                                 t = get_hexbyte(chbuf+pos);
  125.                                 pos+=2;
  126.                                 if( t<0 ) goto PARSE_ERROR;
  127.                                 ihex_type = t;
  128.                                 state = ST_DATA;
  129.                                 break;
  130.                         case ST_DATA:
  131.                                 if( curr_length>0 )
  132.                                 {
  133.                                         t = get_hexbyte(chbuf+pos);
  134.                                         pos+=2;
  135.                                         if( t<0 ) goto PARSE_ERROR;
  136.  
  137.                                         // reuse buffer
  138.                                         chbuf[datapos++] = t;
  139.  
  140.                                         curr_length--;
  141.                                 }
  142.                                 else
  143.                                 {
  144.                                         state = ST_CHECKSUM;
  145.                                 }
  146.                                 break;
  147.                         case ST_CHECKSUM:
  148.                                 t = get_hexbyte(chbuf+pos);
  149.                                 pos+=2;
  150.                                 if( t<0 ) goto PARSE_ERROR;
  151.                                 ihex_checksum = t;
  152.                                 state = ST_DONE;
  153.                                 break;
  154.                         default: // incl. ST_DONE
  155.                                 goto PARSE_ERROR;
  156.                         }
  157.                 }
  158.  
  159.                 // parse succeeded - checksum test
  160.                 t=0;
  161.                 for(i=0;i<ihex_length;i++)
  162.                         t+=chbuf[i];
  163.                 t+=ihex_length;
  164.                 t+=ihex_addr>>8;
  165.                 t+=ihex_addr;
  166.                 t+=ihex_type;
  167.                 t+=ihex_checksum;
  168.  
  169.                 if( t&255 )
  170.                 {
  171.                         fprintf(stderr,"ERROR: line %d of file <%s>: checksum error!\n",line,filename);
  172.                         goto GENERAL_ERROR;
  173.                 }
  174.  
  175.                 if( was_eof )
  176.                 {
  177.                         fprintf(stderr,"ERROR: line %d of file <%s>: ihex record encountered after EOF record!\n",line,filename);
  178.                         goto GENERAL_ERROR;
  179.                 }
  180.  
  181.                 // apply ihex line
  182.                 switch( ihex_type )
  183.                 {
  184.                 case IHEX_TYPE_DATA:
  185.                         // should be non-zero length
  186.                         if( ihex_length<=0 )
  187.                         {
  188.                                 fprintf(stderr,"ERROR: line %d of file <%s>: data record has zero length!\n",line,filename);
  189.                                 goto GENERAL_ERROR;
  190.                         }
  191.                         // put data into buffer, also check ranges
  192.                         for(i=0;i<ihex_length;i++)
  193.                         {
  194.                                 int32_t addr = seg_addr*16 + ihex_addr + i;
  195.                                 if( addr<low_bound || high_bound<=addr )
  196.                                 {
  197.                                         fprintf(stderr,"ERROR: line %d of file <%s>: data record gets outside the range %x..%x!\n",line,filename,low_bound,high_bound);
  198.                                         goto GENERAL_ERROR;
  199.                                 }
  200.                                 else
  201.                                 {
  202.                                         buffer[disp+addr] = chbuf[i];
  203.                                 }
  204.                         }
  205.                         break;
  206.                 case IHEX_TYPE_SEGADDR:
  207.                         // set up new segment addr
  208.                         if( ihex_length!=2 )
  209.                         {
  210.                                 fprintf(stderr,"ERROR: line %d of file <%s>: segment address record wrong length (must be 2)!\n",line,filename);
  211.                                 goto GENERAL_ERROR;
  212.                         }
  213.                         seg_addr = ((uint8_t)chbuf[0])*256 + ((uint8_t)chbuf[1]);
  214.                         break;
  215.                 case IHEX_TYPE_EOF:
  216.                         if( ihex_length!=0 )
  217.                         {
  218.                                 fprintf(stderr,"ERROR: line %d of file <%s>: EOF record wrong length (must be 0)!\n",line,filename);
  219.                                 goto GENERAL_ERROR;
  220.                         }
  221.                         was_eof = 1;
  222.                         break;
  223.                 default: // unknown or unsupported record type
  224.                         fprintf(stderr,"ERROR: line %d of file <%s>: unknown record type %d!\n",line,filename,ihex_type);
  225.                         goto GENERAL_ERROR;
  226.                 }
  227.  
  228.                 line++;
  229.         }
  230.  
  231.         // EOF reached or error.
  232.         if( feof(file) )
  233.         {
  234.                 if( was_eof )
  235.                 {
  236.                         fclose(file);
  237.                         return 1;
  238.                 }
  239.  
  240.                 fprintf(stderr, "file <%s>: unexpected EOF!\n",filename);
  241.         }
  242.         else if( ferror(file) )
  243.         {
  244.                 fprintf(stderr, "file <%s>: %s!\n",filename,strerror(errno));
  245.         }
  246.         else
  247.         {
  248.                 fprintf(stderr, "file <%s>: unknown error!\n",filename);
  249.         }
  250.         goto GENERAL_ERROR;
  251.  
  252.  
  253. PARSE_ERROR:
  254.         fprintf(stderr,"ERROR: line %d of file <%s>: parse error!\n",line,filename);
  255. GENERAL_ERROR:
  256.         fclose(file);
  257.         return 0;
  258. }
  259.  
  260.  
  261.  
  262.  
  263.  
  264. static char get_hex_nibble(uint8_t value)
  265. {
  266.         value &= 0x0F;
  267.  
  268.         if( value<10 )
  269.                 return value+'0';
  270.         else
  271.                 return value-10+'A';
  272. }
  273.  
  274. static void write_hex_value(char * buf, uint8_t value, uint8_t * checksum)
  275. {
  276.         buf[0] = get_hex_nibble(value>>4);
  277.         buf[1] = get_hex_nibble(value);
  278.  
  279.         if( checksum )
  280.                 *checksum += value;
  281. }
  282.  
  283. static int write_ihex_line(FILE * file, uint8_t type, uint16_t addr, uint8_t * buffer, int32_t length)
  284. {
  285.         if( length<0 || length>255 )
  286.         {
  287.                 fprintf(stderr, "ERROR: attempt to generate ihex line with wrong length %d!\n",length);
  288.                 return 0;
  289.         }
  290.  
  291.         uint32_t chlen = length*2 + 12;
  292.        
  293.         char * chbuf = alloca(chlen);
  294.         memset(chbuf,0,chlen);
  295.  
  296.         uint32_t chpos = 0;
  297.  
  298.         uint8_t checksum = 0;
  299.  
  300.  
  301.         // fill buffer
  302.         chbuf[chpos++] = ':';
  303.  
  304.         // length
  305.         write_hex_value(&chbuf[chpos], length, &checksum); chpos+=2;
  306.  
  307.         // address
  308.         write_hex_value(&chbuf[chpos], addr>>8, &checksum); chpos+=2;
  309.         write_hex_value(&chbuf[chpos], addr   , &checksum); chpos+=2;
  310.  
  311.         // type
  312.         write_hex_value(&chbuf[chpos], type, &checksum); chpos+=2;
  313.  
  314.         // data
  315.         for(int32_t i=0;i<length;i++)
  316.         {
  317.                 write_hex_value(&chbuf[chpos], buffer[i], &checksum);
  318.                 chpos+=2;
  319.         }
  320.  
  321.         // checksum
  322.         write_hex_value(&chbuf[chpos], 1+(~checksum), NULL); chpos+=2;
  323.  
  324.         // string zero termination
  325.         chbuf[chpos++] = 0;
  326.  
  327.         if( chpos!=chlen )
  328.         {
  329.                 fprintf(stderr, "FATAL ERROR: wrong buffer allocation size!\n");
  330. //printf("chpos=%d, chlen=%d, length=%d\n",chpos,chlen,length);
  331.                 exit(1);
  332.         }
  333.  
  334.         // put string to file
  335.         if( fprintf(file,"%s\n",chbuf) < 0 )
  336.         {
  337.                 fprintf(stderr,"ERROR: can't write to the output file: %s!\n",strerror(errno));
  338.                 return 0;
  339.         }
  340.  
  341.         return 1;
  342. }
  343.  
  344. int write_ihex(uint8_t * buffer, int32_t disp, int32_t low_bound, int32_t high_bound, char * filename)
  345. {
  346.         FILE * file;
  347.  
  348.  
  349.         // create file
  350.         file = fopen(filename, "wt");
  351.         if( !file )
  352.         {
  353.                 fprintf(stderr,"ERROR: can't create file <%s>: %s!\n",filename,strerror(errno));
  354.                 return 0;
  355.         }
  356.  
  357.  
  358.         int32_t prev_seg_addr = (-1);
  359.  
  360.  
  361.         int32_t beg_addr;
  362.  
  363.  
  364.  
  365.  
  366.         beg_addr = low_bound;
  367.  
  368.  
  369.         while( beg_addr<high_bound )
  370.         {
  371.                 int32_t end_addr = (beg_addr & (~15)) + 16;
  372.  
  373.                 if( end_addr>high_bound )
  374.                         end_addr = high_bound;
  375.  
  376. //printf("beg_addr: %05X\n",beg_addr);
  377.  
  378.                 // generate segment addr, if needed
  379.                 int32_t seg_addr, addr;
  380.  
  381.                 addr = beg_addr & 0x0FFFF;
  382.                 seg_addr = (beg_addr - addr) >> 4;
  383.                 if( seg_addr != prev_seg_addr )
  384.                 {
  385.                         uint8_t buf[2];
  386.                         buf[0] = seg_addr>>8;
  387.                         buf[1] = seg_addr;
  388.  
  389.                         if( !write_ihex_line(file, IHEX_TYPE_SEGADDR, 0, buf, 2) )
  390.                         {
  391.                                 fclose(file);
  392.                                 return 0;
  393.                         }
  394.                        
  395.                         prev_seg_addr = seg_addr;
  396.                 }
  397.  
  398.  
  399.                 // generate data
  400.                 if( !write_ihex_line(file, IHEX_TYPE_DATA, addr, &buffer[disp+beg_addr], (end_addr-beg_addr)) )
  401.                 {
  402.                         fclose(file);
  403.                         return 0;
  404.                 }
  405.  
  406.                 beg_addr = end_addr;
  407.         }
  408.  
  409.        
  410.         // generate EOF record
  411.         if( !write_ihex_line(file, IHEX_TYPE_EOF, 0, NULL, 0) )
  412.         {
  413.                 fclose(file);
  414.                 return 0;
  415.         }
  416.  
  417.  
  418.         fclose(file);
  419.         return 1;
  420. }
  421.  
  422.