/* hex2bin.c -- yet another reader and writer of Intel hex files Copyright (C) 2011 John R Coffman . *********************************************************************** When invoked as 'hex2bin' read a sequence of Intel hex files and create an overlaid binary file. When invoked as 'bin2hex' read a binary file and create an Intel hex file. All command line numeric constants may be specified in any radix. *********************************************************************** This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License in the file COPYING in the distribution directory along with this program. If not, see . **********************************************************************/ #include #include #include #include "mytypes.h" #define true 1 #define false 0 #define SEG_MASK 0x00FFFFu #define LBA_MASK 0x00FF0000ul #define MAX_MASK (LBA_MASK|SEG_MASK) #define ONE_MEG 0x100000ul dword upper_lba = 0; /* upper address */ dword address_mask = SEG_MASK; /* address mask */ byte pad = 0xFF; byte *buffer; dword rom_size = 0; dword overwrite; /* count of possible overwrites */ byte h2b, verbose, segmented; char *outfilename = NULL; char *binfilename = NULL; dword source_address, source_limit; dword dest_address, dest_limit; FILE *infile; FILE *outfile; byte checksum; char line[1024]; char *lp; long int lineno; dword convert_constant(char *str) { char *final; dword value = strtoul(str, &final, 0); if (*final == 'k' || *final == 'K') value *= 1024ul; else if (*final == 'M' || *final == 'm') value *= ONE_MEG; return value; } void error(byte level, char *msg) { printf("%s(%d): %s\n", level>1 ? "Error" : "Warning", (int)level, msg); if (level>1) exit(level); else if (level==0) printf("line %ld %s", lineno, line); } int getnibble(void) { char ch; ch = -1; if (lp) { ch = *lp++; if (ch>='0' && ch<='9') ch -= '0'; else if (ch>='A' && ch<='F') ch -= 'A'-10; else if (ch>='a' && ch<='f') ch -= 'a'-10; else { error(0,"Illegal hex digit"); ch = -1; } } else error(0,"Line is too short"); return (int)ch; } int getbyte(void) { int b = getnibble(); b <<= 4; b += getnibble(); checksum += b; return b; } int getword(void) { int w = getbyte(); w <<= 8; w += getbyte(); return w; } dword getdword(void) { dword d = getword(); d <<= 16; d += getword(); return d; } /* added for SREC files */ dword get6word(void) { dword d = getword(); d <<= 8; d += getbyte(); return d; } void putbyte(dword address, byte data) { if (address < source_address || address > source_limit) return; address -= source_address; address += dest_address; if (address > dest_limit) return; if (address >= rom_size) { printf("Line %ld ", lineno); error(2,"Data beyond end of ROM"); } if (buffer[address] != pad) { overwrite++; if (verbose || overwrite<=100) printf("Warning(1): Overwrite at ROM address 0x%lX\n", address); } buffer[address] = data; } void usage(void) { printf("hex2bin.c (bin2hex) -- " __TIMESTAMP__ ".\n" "Copyright (c) 2011 John R Coffman. All rights reserved.\n" "Distributed under the GNU General Public License, a copy of which\n" "is contained in the file COPYING in the distribution directory.\n\n"); if (h2b) printf( "Usage:\n" " hex2bin [ [/M]]+\n\n" " Options:\n" " -o \n" " -p \n" " -R default 64K\n" " -v []\n" " Flags:\n" " -d \n" " -D \n" " -s \n" " -S \n" " Suffix:\n" " /M marks a Motorola S-record input file\n" ); else printf( "Usage:\n" " bin2hex [ ]+\n\n" " Options:\n" " -g use Intel seGmented addressing\n" " -o \n" " -p \n" " -R default 1024K\n" " -v []\n" " Flags:\n" " -d \n" " -D \n" " -s \n" " -S \n" ); } void hout_byte(byte data) { checksum -= data; fprintf(outfile, "%02X", (int)data); } void hout_word(word data) { hout_byte(data>>8); hout_byte(data); } void begin_record(byte length) { checksum = 0; fputc(':', outfile); hout_byte(length); } void end_record(void) { hout_byte(checksum); fputc('\n', outfile); } void write_lba(dword address) { if (verbose==5) printf("Address: %06lX\n", address); if ((address & LBA_MASK) != upper_lba) { upper_lba = address & LBA_MASK; begin_record(2); hout_word(0); if (rom_size > ONE_MEG || !segmented) { hout_byte(4); /* linear address */ hout_word(upper_lba>>16); } else { /* handle ROMs 1meg and smaller */ hout_byte(2); /* segment address */ hout_word(upper_lba>>4); } end_record(); } } void write_data(word nbytes, byte *buf, dword address) { /* compress from the high end */ while (nbytes && buf[nbytes-1]==pad) --nbytes; /* compress from the low end */ while (nbytes && *buf==pad) { ++buf; ++address; --nbytes; } if (nbytes) { write_lba(address); begin_record(nbytes); hout_word(address & 0xFFFFu); hout_byte(0); /* data record */ while(nbytes--) hout_byte(*buf++); end_record(); } } #define min(a,b) ((a)<(b)?(a):(b)) #define NREC 16 void write_hex_file(FILE *outfile) { dword nbytes; dword vaddr; dword n; byte *buf; buf = buffer; vaddr = 0; nbytes = rom_size; n = min(nbytes, NREC); do { write_data(n, buf, vaddr); buf += n; vaddr += n; nbytes -= n; n = min(nbytes, NREC); } while (n); /* write the end-of-file record */ fprintf(outfile,":00000001FF\n"); } void scan_bin_file(char *filename) { dword length; dword nbytes; int data; dword inaddr; infile = fopen(filename, "rb"); if (!infile) { strcpy(line,"Cannot find file: "); error(5, strcat(line, filename)); } /*** length = filelength(fileno(infile)); ***/ fseek(infile, 0L, SEEK_END); length = ftell(infile); /***/ nbytes = 0; inaddr = dest_address; if (source_address < length) { fseek(infile, source_address, SEEK_SET); while (inaddr=3) printf("%s", lp-1); checksum = 0; rectype = getnibble(); ldata = getbyte(); switch(rectype) { /* get variable address field */ case 0: case 1: case 5: case 9: laddr = getword(); ldata -= 2; break; case 2: case 8: laddr = get6word(); ldata -= 3; break; case 3: case 7: laddr = getdword(); ldata -= 4; break; default: error(0,"Unknown record type:"); } if (rectype>=1 && rectype<=3) { index = 0; while (--ldata) { data = getbyte(); /* no address mask used */ putbyte(laddr + index, data); index++; } } else if (rectype==0) { printf("Comment: "); while (--ldata) { printf("%c", (char)getbyte()); } printf("\n"); } /* else records 5,7,8,9 are ignored */ data = getbyte(); /* get final checksum */ if (checksum != 0xFF) { error(0,"Checksum failure"); } } while (lp && !EndOfFile); fclose(infile); } void scan_Intel_file(char *filename) { byte ldata; dword laddr; byte rectype; dword value; dword index; byte data; byte EndOfFile = 0; infile = fopen(filename, "rt"); if (!infile) { strcpy(line,"Cannot find file: "); error(5, strcat(line, filename)); } upper_lba = 0; lineno = 0; do { lineno++; lp = fgets(line, nelem(line)-1, infile); if (lp == NULL) break; if (*lp++ != ':') { printf("Comment: %s",--lp); continue; } if (verbose>=3) printf("%s", lp-1); checksum = 0; ldata = getbyte(); laddr = getword(); rectype = getbyte(); switch (rectype) { case 0: /* data record */ index = 0; while (ldata--) { data = getbyte(); putbyte(upper_lba + ((laddr + index)&address_mask), data); index++; } break; case 1: /* end of file record */ EndOfFile = 1; break; case 2: /* segment address */ address_mask = SEG_MASK; value = getword(); upper_lba = value<<4; /* start of segment */ ldata -= 2; break; case 4: /* linear upper address */ address_mask = MAX_MASK; value = getword(); upper_lba = value<<16; /* full 32-bit address range */ ldata -= 2; break; case 3: /* start CS:IP */ case 5: /* linear start address */ value = getdword(); ldata -= 4; break; default: error(0,"Unknown record type:"); } getbyte(); /* get final checksum */ if ( (checksum & 0xFF) ) { error(0,"Checksum failure"); } } while (lp && !EndOfFile); fclose(infile); } void scan_hex_file(char *filename) { int i = strlen(filename); if (i>3 && filename[i-2]=='/' && (filename[i-1]=='M') ) { filename[i-2] = 0; /* remove suffix */ scan_srec_file(filename); } else scan_Intel_file(filename); } void global_options(int argc, char *argv[]) { int iarg; char *cp; char *tp; char ch; h2b = false; rom_size = ONE_MEG; /* bin2hex default value */ /* decide which conversion to do */ if (strstr(argv[0],"hex2bin") #ifdef MSDOS || strstr(argv[0],"HEX2BIN") #endif ) { h2b = true; rom_size = 64 * 1024ul; /* default value */ } /* assume 'bin2hex' otherwise */ if (argc<2) { usage(); exit(0); } /* scan the global command line options */ for (iarg = 0; iarg MAX_MASK+1) error(5, "ROM size too big"); if (rom_size < 256) error(5, "ROM size too small"); *cp = *tp = 0; break; case 'v': /* print verbose statistics */ verbose++; if (!*tp) tp = argv[++iarg]; if (*tp>='1' && *tp<='5' && tp[1]==0) verbose += (*tp - '1'); else tp = cp; *cp = *tp = 0; break; case 'Y': { int i; for (i=0; i