/*	comps19 - compare customer s-records against verification file
	This program reads two S-record files into memory (customer code and RVU
	verification file), and compares the contents of selected address
	ranges as defined by a parameter file.

command line: comps19 prmfile ctmfile [ctmfile] ... -v verfile [verfile] ...

where
	prmfile		is a text file containing parameters for the ROM device
	ctmfile		is the customer's S-record file
	verfile		is the verification file received from the RVU center

prmfile format:

line 1:		device name		map start address (in hex)		size
line 2:		range name		start address					end address
line 3:		range name		start address					end address
.
.
.
and so on.  Here's an example:

MC68HC05C4	0		2000
Data_ROM	20		4F
Code_ROM	100		10FF
Vectors		1FF4	1FFF

*/

#include	<stdio.h>

#ifdef	DEBUG
#define	DB(x)	x
#else
#define	DB(x)
#endif

#define		MAX_RANGE_CNT	8		/* max no of ranges in a map */
#define		RANGE_NAME_SIZE	33
#define		FN_MAX		128			/* max size of filename */

typedef unsigned ADDRESS;

struct range {
char name [RANGE_NAME_SIZE];
ADDRESS start, end;
} ranges [MAX_RANGE_CNT];

int range_cnt;						/* no. of ranges active in this run */
char chipname [RANGE_NAME_SIZE];	/* what device are we using? */
char allok, reading, *ctm_buff, *ver_buff;
ADDRESS address, offset, rom_size;
int checksum, count;
FILE *infile;
int fileline;						/* line number of input file */
char getbuff [128];
char *dashes = "===========================================================\n";

extern char *malloc ();

main (argc, argv)
int argc;
char **argv;
{
	char *cur_buff;
	int ctm_cnt, ver_cnt, cur_arg;

	if (argc < 5) usage ();			/* check for valid command line */
	read_parms (argv [1]);			/* get parameter info */
	if ((ctm_buff = malloc (rom_size)) == (char *) NULL)
		error ("out of memory");
	if ((ver_buff = malloc (rom_size)) == (char *) NULL)
		error ("out of memory");
	memset (ctm_buff,0,rom_size);
	memset (ver_buff,0,rom_size);
	ctm_cnt = ver_cnt = 0;
	for (cur_buff = ctm_buff, cur_arg = 2; cur_arg < argc; cur_arg++)
	{
		if (!strcmp (argv [cur_arg], "-v") || !strcmp (argv [cur_arg], "-V"))
		{
			if (!ctm_cnt) error ("no customer filenames specified");
			cur_buff = ver_buff;
			continue;
		}
		(cur_buff == ctm_buff) ? ctm_cnt++ : ver_cnt++;
		read_s19 (argv [cur_arg], cur_buff);
	}
	if (!ver_cnt) error ("no verify filenames specified");

	allok = 1;
	for (cur_arg = 0; cur_arg < range_cnt; cur_arg++)
	{
		printf ("Checking %s range\n",ranges[cur_arg].name);
		comp_buff (cur_arg);
	}
	if (allok) printf ("Files check OK!\n");
	exit (0);
}

/* read_s19 reads data from file specified into buff */

read_s19 (filename, buff)
char *filename, *buff;
{
	int c;
	ADDRESS writeat;

	printf ("Reading %s file %s\n",
		(buff == ctm_buff) ? "customer" : "verify", filename);
	if ((infile = fopen (filename,"r")) == (FILE *) NULL)
		error ("could not open input file");
	fileline = reading = 0;
	while ((c = getbyte ()) != EOF)
	{
		DB (printf ("writing %02x to %04x\n",c,(address-1)-offset);)
		if ((writeat = (address - 1) - offset) < rom_size) buff [writeat] = c;
	}
	if (fclose (infile)) error ("error closing input file");
}

/* skipwhite moves pointer past white space to next non-white */

char *skipwhite (s)
char *s;
{
	while (isspace (*s)) s++;
	return s;
}

/* cvthex converts string of hex chars at ptr into 16-bit unsigned at dest */
/* skips white space, returns updated pointer */

char *cvthex (ptr, dest)
char *ptr;
ADDRESS *dest;
{
/*	DB (printf ("cvthex: starting buffer %s\n",ptr);) */
	ptr = skipwhite (ptr);
/*	DB (printf ("after skipwhite: %s\n",ptr);) */
	*dest = 0;
	while (isxdigit (*ptr))
	{
		*dest = (*dest << 4) + htoi (*ptr++);
/*		DB (printf ("dest is %04x buffer is %s\n",*dest,ptr);) */
	}
	return ptr;
}

/* read_parms reads parameter info from filename given */

read_parms (filename)
char *filename;
{
	char linebuff [128], workbuff [16], *dest, *cptr;
	int c;

	if ((infile = fopen (filename,"r")) == (FILE *) NULL)
		error ("could not open input file");

	if (fgets (linebuff,128, infile) == (char *) NULL)
		error ("parameter file error");

	getptoke (linebuff, chipname, &offset, &rom_size);
	printf ("\n%-33s start = %04x end = %04x\n%s",
		chipname, offset, offset + rom_size - 1,dashes);

	for (range_cnt = 0; range_cnt < MAX_RANGE_CNT; range_cnt++)
	{
		if (fgets (linebuff,128, infile) == (char *) NULL) break;
		getptoke (linebuff,
			ranges [range_cnt].name,
			&ranges [range_cnt].start,
			&ranges [range_cnt].end);
		printf ("%-33s start = %04x end = %04x\n",
			ranges [range_cnt].name,
			ranges [range_cnt].start,
			ranges [range_cnt].end);
	}
	printf ("%s", dashes);
	if (fclose (infile)) error ("error closing parameter file");
}

/* comp_buff compares customer data vs verify data for range given */
/* displays mismatches on screen and terminates if descrepancy found */

comp_buff (which)
int which;

{
    char *ctm, *ver, *last;
	ADDRESS where;
	char ok;

	ctm = ranges [which].start - offset + ctm_buff;
	ver = ranges [which].start - offset + ver_buff;
	last = ranges [which].end - offset + ctm_buff;

	for (ok = 1; ctm <= last; ctm++,ver++)
	if (ok)
	{
		if (*ctm == *ver) continue;
		else
		{
			allok = ok = 0;
			where = (ADDRESS) ctm;
		}
	}
	else
	{
		if (*ctm != *ver) continue;
		else
		{
			ok = 1;
			printf ("Mismatch at %04x length %04x\n",
				where - (ADDRESS) ctm_buff + offset,
				ctm - where);
		}
	}
	if (!ok)
		printf ("Mismatch at %04x length %04x\n",
			where - (ADDRESS) ctm_buff + offset,
			ctm - where);
}

/* getptoke parses up parameter line into string and two hex numbers */

getptoke (buffer, string, hex1, hex2)
char *buffer, *string;
ADDRESS *hex1, *hex2;
{
	buffer = skipwhite (buffer);
	while (isprint (*buffer)) *string++ = *buffer++;
	buffer = cvthex (buffer, hex1);
	cvthex (buffer, hex2);
}

char *next;

 /* getbyte returns next byte from input file, or EOF if end of file */

getbyte ()
{
    int c;

	for (;;)
	{
    if (!reading)
    {
		DB (printf ("scanning\n");)
        for (;;)
        {
			if (fgets (getbuff,128,infile) == (char *) NULL) return (EOF);
			fileline++;
			if (*getbuff != 'S') continue;
            if ((c = *(getbuff+1)) == '9') return (EOF);
            if (c == '1') break;
        }
		DB (printf ("start of line\n");)
		next = getbuff + 2;
        checksum = 0;
		if ((count = _getbyte ()) == EOF) return (EOF);
		if ((address = _getbyte ()) == EOF) return (EOF);
		if ((c = _getbyte ()) == EOF) return (EOF);
        address = (address << 8) + c;
        reading++;
    }
	if ((c = _getbyte ()) == EOF) return (EOF);
    address++;
    if (!count)
    {
        if (~checksum & 0xFF)
		{
			printf ("Checksum error on input file line %d\n",fileline);
			exit (1);
		}
		DB (printf ("end of line\n");)
        reading = 0;
    }
    else return (c);
	}
}

/* _getbyte returns the binary value of the next two ASCII characters read */
/* returns EOF if end of file occurs - also updates checksum */

_getbyte ()
{
    int c1,c2;

    count--;
	if ((c1 = *next++) == '\0')
	{
		DB (printf ("_getbyte got an EOF\n");)
		return (EOF);
	}
	if ((c2 = *next++) == '\0')
	{
		DB (printf ("_getbyte got an EOF\n");)
		return (EOF);
	}
    c1 = htoi (c1);
    checksum += c1 = (c1 << 4) + htoi (c2);
    return (c1);
}

/* htoi converts ASCII character to binary */

htoi (c)
char c;
{
	c = toupper (c) - '0';
    if (c > 9) c -= ('A' - '9' - 1);
    return c;
}


/* usage shows command format and quits to operating system */

usage ()
{
	error ("usage: comps19 ctmfile [ctmfile] ... -v verfile [verfile] ...");
}

error (problem)
char *problem;

{
	printf ("%s\n",problem);
	exit (1);
}

/* isxdigit - to fix problem with the one in Datalight C */

isxdigit (c)
char c;
{
	return ((c >= '0' && c <= '9')
         || (c >= 'A' && c <= 'F')
		 || (c >= 'a' && c <= 'f'));
}
