/*===========================================================================

  PROJECT   : JET-X CDMS
  PROGRAM   : PATCHGEN.C
  AUTHOR    : MARK COOKE
  COPYRIGHT : (C) Copyright 1997, Mark Cooke. All rights reserved.
  
  VERSION   : 1.2a
  
  FIXES
  -----

  22-Mar-96  Fix incorrect sequence at 0xFFF0.
  05-Apr-96  Remove Linefeed from output.
  21-Jul-97  Updated to create a set of JET-X patch commands from a .LST file
             as part of the PATCHGEN utility.
  01-Sep-97  Changed line endings to \r\n instead of just \n so the output is
             directly compatible with the PDP.

  DESCRIPTION
  -----------
  
  This software is a revised version of the CONVERT utility. It is written to
  take a patch listing, with JET-X code between 0x0000 and 0x4000, and to
  generate automatically the commands required to build the necessary byte
  pattern in ram.
  
  The output of this module is a set of JET-X patch commands.
  ===========================================================================*/

#define TARGET_DOS 1

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#if TARGET_DOS
#include <io.h>
#endif

#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

unsigned char image[64*1024];

void build_patch(FILE *f_out)
{
	int i, j, k, nSpans, from, to;
	int spans[16*1024][3], addr, len;

	nSpans = 0;

	/* Find the ROI */
	for (from = 0x0000; image[from] == 0; from ++)
		;

	for (to = 0x4000 ; image[to] == 0; to --)
		;

	to ++;

	/* Build a list of longest length spans */
	for (i = from; i <= to; i = i + len) {

		j = 0xC000;
		addr = 0;
		len  = 1;

		while(j < 0xFFE0) {

			k = 0;

			while ((image[i+k] == image[j+k]) && j+k < 0x10000)
				k ++;

			/* Only store long spans */
			if (k > len) {
				addr = j;
				len  = k;
			}

			if (k == 0)
				j ++;
			else
				j = j + k;
		}

		if (len > 6) {
			spans[nSpans][0] = i;
			spans[nSpans][1] = addr;
			spans[nSpans][2] = len;
			nSpans ++;
		}
	}

	i = from;
	
	for (j = 0; j < nSpans ; j ++) {

		/* If i < start of next span */
		if (spans[j][0] > i) {

			/* Write a CPPATNTR command for spans[j][0] - i bytes */
			for (k = 0; (k+10) < (spans[j][0] - i); k += 10) {
				
				fprintf(f_out, "COM CPPATNTR,#\"000A\"h/\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h/\r\n", 
						i + k,
						(image[i+k+0] + (image[i+k+1] << 8)),
						(image[i+k+2] + (image[i+k+3] << 8)),
						(image[i+k+4] + (image[i+k+5] << 8)),
						(image[i+k+6] + (image[i+k+7] << 8)),
						(image[i+k+8] + (image[i+k+9] << 8)));
			}

			if (spans[j][0] - i - k > 0) {

				fprintf(f_out, "COM CPPATNTR,#\"%.4X\"h/\"%.4X\"h",
						spans[j][0] - i - k,
						i + k);

				for (; k < (spans[j][0] - i); k += 2) {

					if ((k + 1) == (spans[j][0] - i))
						fprintf(f_out, "\"%.4X\"h", (image[i+k] + 0));
					else
						fprintf(f_out, "\"%.4X\"h", (image[i+k] + (image[i+k+1]<<8)));
				}

				fprintf(f_out, "/\r\n");
			}

			/* Set i to start of next span */
			i = spans[j][0];
		}

		/* If i == start of next span */
		if (spans[j][0] == i) {

			/* Write a CPCOPCP command to copy the span */
			for (k = 0; (k+255) < spans[j][2]; k += 255)
				fprintf(f_out, "COM CPCOPNTR,#\"00FF\"h/\"%.4X\"h\"%.4X\"h\r\n",
						i + k, spans[j][1] + k);

			if (k < spans[j][2])
				fprintf(f_out, "COM CPCOPNTR,#\"%.4X\"h/\"%.4X\"h\"%.4X\"h/\r\n",
						spans[j][2] - k, i + k, spans[j][1] + k);

			/* Set i to address of next location */
			i = i + spans[j][2];
		}
	}

	if (i < to) {

		/* Write CPPATNTR command for the remaining bytes */
		for (k = 0; (k+10) < (to - i); k += 10) {

			fprintf(f_out, "COM CPPATNTR,#\"000A\"h/\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h\"%.4X\"h/\r\n", 
					i + k,
					(image[i+k+0] + (image[i+k+1] << 8)),
					(image[i+k+2] + (image[i+k+3] << 8)),
					(image[i+k+4] + (image[i+k+5] << 8)),
					(image[i+k+6] + (image[i+k+7] << 8)),
					(image[i+k+8] + (image[i+k+9] << 8)));
		}

		if (to - i - k > 0) {

			fprintf(f_out, "COM CPPATNTR,#\"%.4X\"h/\"%.4X\"h",
					to - i - k, i + k);

			for (; k < (to - i); k += 2)
				fprintf(f_out, "\"%.4X\"h",
						(image[i+k+0] + (image[i+k+1] << 8)));

			fprintf(f_out, "/\r\n");
		}
	}

	return;
}

int main(int argc, char **argv)
{
	char  buffer[8192];
	char  values[16][256];
	char *s;
	FILE *f_inp;
	FILE *f_out;
	long  addr, val;
	unsigned long i, n;
	struct tm *ltime;
	time_t systime;

	memset(image, 0, 64*1024);

	if (argc != 3) {
		printf("usage: patchgen <input.lst> <output.cmd>\r\n");
		exit(-1);
	}

	f_inp = fopen(argv[1],"rb");
	if (f_inp == NULL) {
		perror("Opening input file");
		exit(-1);
	}

	f_out = fopen(argv[2],"wb+");
	if (f_out == NULL) {
		perror("Creating output file");
		fclose(f_inp);
		exit(-1);
	}

	systime = time(NULL);
	ltime = localtime(&systime);
	fprintf(f_out, "; %s created from %s on %s;\r\n", argv[2], argv[1], asctime(ltime));

	/* Do the guts of the reading */
	while(fgets(buffer, sizeof(buffer), f_inp)) {

		/* Try to do the trivial cases first */
		if (buffer[0] != ' ')
			continue;

		s = strchr(buffer, '\t');
		if (s == NULL)
			continue;
		
		*s = ' ';
		s = strchr(s, '\t');       
		if (s == NULL)  
			continue; 

		*s = '\0';

		if (strlen(buffer) < 16)
			continue;

		/* Look for non-numeric characters */
		for (i = 0; i < strlen(buffer); i ++) {
			if (!isxdigit(buffer[i]) && !isspace(buffer[i])) {
				if (buffer[i] != 'r') {
					continue;
				}
				else
					buffer[i] = ' ';
			}
		}

		/* try to pattern match */
		n = sscanf(buffer,"%s %s %s %s %s %s %s %s %s",
					&values[0][0], &values[1][0], &values[2][0],
					&values[3][0], &values[4][0], &values[5][0],
					&values[6][0], &values[7][0], &values[8][0]);

		if (n < 3)
			continue;

		if (sscanf(&values[1][0], "%lx", &addr) != 1)
			continue;

		if (strchr(values[2],'?') != NULL)
			continue;

		if (addr >= 0x0000FFF0)
			fprintf(stderr,"WARNING: CODE MAY BE TOO LARGE.\r\n");

		for (i = 2; i < n; i ++) {
			sscanf(&values[i][0], "%lx", &val);
			if (strlen(&values[i][0]) == 4) {
				image[addr]   = (unsigned char) (val & 0xFF);
				image[addr+1] = (unsigned char) (val >> 8);
				addr += 2;
			}
			else {
				image[addr] = (unsigned char) (val & 0xFF);
				addr ++;
			}
		}
	}

	fclose(f_inp);

	/* Add on the tags that Buildstd does */
	image[0xFFF0] = 0xEA;
	image[0xFFF1] = 0x00;
	image[0xFFF2] = 0xC0;
	image[0xFFF3] = 0x00;
	image[0xFFF4] = 0x00;

	build_patch(f_out);

	fclose(f_out);

	return 1;
}
