#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#define ULONG unsigned long
#define UBYTE unsigned char
#define BOOL char
#define TRUE 1
#define FALSE 0

#define STRING_SIZE 100

UBYTE far data_blk[65536];

#define HEX(c) ((c>'9')?c-'A'+10:c-'0')

void stch_l(char *chr_ptr, ULONG *u_ptr) {
  *u_ptr=0;
  while (isxdigit(*chr_ptr)) {
    *u_ptr=*u_ptr*16+HEX(*chr_ptr);
    chr_ptr++;
  }
}

BOOL load(char *dest, char *src, ULONG *addr, ULONG *next_addr) {
  UBYTE chksum, temp1, temp2, byte_count, i;

  chksum=byte_count=(HEX(src[0]))*16 + HEX(src[1]); src+=2;
  temp1=(HEX(src[0]))*16 + HEX(src[1]);
  temp2=(HEX(src[2]))*16 + HEX(src[3]); src+=4;
  *addr=(long)temp1*256 + (long)temp2;
  *next_addr=*addr+(long)byte_count-3;
  chksum+=temp1+temp2;
  for (i=0;i<byte_count-3;i++) {
    temp1=(HEX(src[0]))*16 + HEX(src[1]); src+=2;
    chksum+=temp1;
    *(dest+(long)*addr+i)=temp1;
  }
  temp1=(HEX(src[0]))*16 + HEX(src[1]); src+=2;
  chksum+=temp1;
  if (chksum == 255) return(TRUE);
  return(FALSE);
}

void main(int argc, char **argv) {
  FILE *file_ptr;
  char string[STRING_SIZE];
  ULONG addr,next_addr;
  ULONG min_addr=65535,max_addr=0;
  ULONG line_num=0;
  ULONG i;

  if (argc!=3 || argc==2 && argv[1][0]=='?')
    printf("USAGE: %s <source> <destination>\n",argv[0]);
  else {
    if (!(file_ptr=fopen(argv[1],"r")))
      printf("Unable to open %s for input\n",argv[1]);
    else {
      for (i=0;i<65536;i++) {
        data_blk[i]=0xff;
      }
      while (fgets(string,STRING_SIZE,file_ptr)) {
        line_num++;
        if (string[0]!='S')
          printf("Not a Motorola Hex format file\n");
        else {
          switch (string[1]) {
            case '0':
              if (!load(string,&string[2],&addr,&next_addr))
                printf("format or checksum error in line %ld\n",line_num);
              else {
                string[next_addr]='\0';
                printf("  Loading program %s\n",string);
              }
              break;
            case '1':
              if (!load(data_blk,&string[2],&addr,&next_addr))
                printf("format or checksum error in line %ld\n",line_num);
              else {
                max_addr=max((long)max_addr,next_addr);
                min_addr=min((long)min_addr,addr);
              }
              break;
            case '2':
              printf("This program does not support 24 bit addresses\n");
              break;
            case '8':
              printf("This program does not support 24 bit addresses\n");
              break;
            case '9':
              if (!load(data_blk,&string[2],&addr,&next_addr))
                printf("format or checksum error in line %ld",line_num);
              else {
                printf("  Start address = %ld\n",addr);
              }
              break;
          }
        }
      }
      fclose(file_ptr);
      printf("  Address range = %ld - %ld\n",min_addr,max_addr-1);
      while (TRUE) {
        printf("What is the starting address of the EPROM (Hex) ? ");
        gets(string);
        stch_l(string,&addr);
        if (addr>65535 || addr<0)
          printf("Invalid address\n");
        else break;
      }
      while (TRUE) {
        printf("What is the ending address of the EPROM (Hex) [%d]? ",max_addr-1);
        gets(string);
        if (string[0] == '\0') break;
        stch_l(string,&max_addr);
        if (max_addr>65535 || max_addr<0)
          printf("Invalid address\n");
        else break;
      }
      if (!(file_ptr=fopen(argv[2],"wb")))
        printf("Unable to open %s for output\n",argv[2]);
      else {
        while (max_addr-addr>=16384) {
          fwrite(&data_blk[addr],16384,1,file_ptr);
          addr+=16384;
        }
        fwrite(&data_blk[addr],max_addr-addr,1,file_ptr);
      }
      fclose(file_ptr);
    }
  }
}
