forked from MirrorRepos/RomWBW
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
633 lines
16 KiB
633 lines
16 KiB
/* hex2bin.c -- yet another reader and writer of Intel hex files
|
|
Copyright (C) 2011 John R Coffman <johninsd@gmail.com>.
|
|
***********************************************************************
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
**********************************************************************/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#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 <options> [<flags> <filename>[/M]]+\n\n"
|
|
" Options:\n"
|
|
" -o <output filename>\n"
|
|
" -p <pad byte>\n"
|
|
" -R <ROM size> default 64K\n"
|
|
" -v [<verbosity level>]\n"
|
|
" Flags:\n"
|
|
" -d <destination address in BIN file>\n"
|
|
" -D <destination limit in BIN file>\n"
|
|
" -s <source address in HEX file>\n"
|
|
" -S <source limit in HEX file>\n"
|
|
" Suffix:\n"
|
|
" /M marks a Motorola S-record input file\n"
|
|
);
|
|
else printf(
|
|
"Usage:\n"
|
|
" bin2hex <options> [<flags> <filename>]+\n\n"
|
|
" Options:\n"
|
|
" -g use Intel seGmented addressing\n"
|
|
" -o <output filename>\n"
|
|
" -p <pad byte>\n"
|
|
" -R <ROM size> default 1024K\n"
|
|
" -v [<verbosity level>]\n"
|
|
" Flags:\n"
|
|
" -d <destination address in HEX file>\n"
|
|
" -D <destination limit in HEX file>\n"
|
|
" -s <source address in BIN file>\n"
|
|
" -S <source limit in BIN file>\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<rom_size && inaddr<=dest_limit) {
|
|
data = fgetc(infile);
|
|
if (data == EOF) break;
|
|
buffer[inaddr++] = data;
|
|
}
|
|
}
|
|
|
|
fclose(infile);
|
|
}
|
|
|
|
void scan_srec_file(char *filename)
|
|
{
|
|
byte ldata;
|
|
dword laddr;
|
|
byte rectype;
|
|
dword index;
|
|
byte data;
|
|
byte EndOfFile = 0;
|
|
|
|
infile = fopen(filename, "rt");
|
|
if (!infile) {
|
|
strcpy(line,"Cannot find file: ");
|
|
error(5, strcat(line, filename));
|
|
}
|
|
lineno = 0;
|
|
laddr = 0;
|
|
do {
|
|
lineno++;
|
|
lp = fgets(line, nelem(line)-1, infile);
|
|
if (lp == NULL) break;
|
|
if (*lp++ != 'S') {
|
|
printf("Illegal: %s",--lp);
|
|
continue;
|
|
}
|
|
if (verbose>=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<argc; iarg++) {
|
|
cp = argv[iarg];
|
|
if (*cp == '-'
|
|
#ifdef MSDOS
|
|
|| *cp == '/'
|
|
#endif
|
|
) {
|
|
ch = cp[1];
|
|
tp = cp + 2;
|
|
switch (ch) {
|
|
case 'g':
|
|
segmented = 1; /* enable segmented addressing */
|
|
break; /* for ROMs <= 1M in size */
|
|
case 'h':
|
|
usage();
|
|
exit(0);
|
|
case 'o': /* outfile name specification */
|
|
if (!*tp) tp = argv[++iarg];
|
|
outfilename = strdup(tp);
|
|
*cp = *tp = 0;
|
|
break;
|
|
case 'p': /* specify the pad byte */
|
|
if (!*tp) tp = argv[++iarg];
|
|
pad = (byte)convert_constant(tp);
|
|
*cp = *tp = 0;
|
|
break;
|
|
case 'R': /* ROM file size specification */
|
|
if (!*tp) tp = argv[++iarg];
|
|
rom_size = convert_constant(tp);
|
|
if (rom_size > 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<argc; i++)
|
|
printf(" %s", argv[i]);
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} // if '-'
|
|
} // for (iarg ...
|
|
}
|
|
|
|
|
|
void process_cmd_input(int argc, char *argv[])
|
|
{
|
|
int iarg;
|
|
char *cp;
|
|
char *tp;
|
|
|
|
source_address = dest_address = 0;
|
|
source_limit = dest_limit = MAX_MASK;
|
|
|
|
for (iarg=1; iarg<argc; iarg++) {
|
|
cp = argv[iarg];
|
|
if (*cp == '-'
|
|
#ifdef MSDOS
|
|
|| *cp == '/'
|
|
#endif
|
|
) {
|
|
++cp;
|
|
tp = cp + 1;
|
|
switch (*cp) {
|
|
case 's': /* source */
|
|
if (!*tp) tp = argv[++iarg];
|
|
source_address = convert_constant(tp);
|
|
break;
|
|
case 'S': /* source limit */
|
|
if (!*tp) tp = argv[++iarg];
|
|
source_limit = convert_constant(tp);
|
|
break;
|
|
case 'd': /* destination */
|
|
if (!*tp) tp = argv[++iarg];
|
|
dest_address = convert_constant(tp);
|
|
break;
|
|
case 'D': /* destination limit */
|
|
if (!*tp) tp = argv[++iarg];
|
|
dest_limit = convert_constant(tp) - 1;
|
|
break;
|
|
} // switch
|
|
} // if (*cp == '-' ...
|
|
else if (*cp) { /* this must be a filename */
|
|
if (h2b) scan_hex_file(cp);
|
|
else scan_bin_file(cp);
|
|
|
|
/* reset the local relocation options */
|
|
source_address = 0;
|
|
dest_address = 0;
|
|
source_limit = MAX_MASK;
|
|
dest_limit = MAX_MASK;
|
|
}
|
|
} // for (iarg
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
dword index;
|
|
byte *ptr;
|
|
|
|
verbose = 0;
|
|
global_options(argc, argv);
|
|
|
|
buffer = malloc(rom_size);
|
|
if (!buffer) error(5,"Cannot allocate ROM buffer");
|
|
for (ptr=buffer, index=rom_size; index; index--) *ptr++ = pad;
|
|
|
|
process_cmd_input(argc, argv);
|
|
|
|
if (!outfilename) {
|
|
if (h2b) {
|
|
outfilename = "out.bin";
|
|
}
|
|
else {
|
|
outfilename = "out.hex";
|
|
}
|
|
error(1,"No output file specified");
|
|
printf("Using file named '%s' for output\n", outfilename);
|
|
}
|
|
outfile = fopen(outfilename, h2b ? "wb" : "wt");
|
|
if (!outfile) error(5,"Cannot create output file");
|
|
|
|
if (h2b) while (rom_size--) fputc(*buffer++, outfile);
|
|
else write_hex_file(outfile);
|
|
|
|
fclose(outfile);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
|