/* Copyright 2008-2012, Unpublished Work of Technologic Systems
 * All Rights Reserved.
 *
 * THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
 * PROPRIETARY AND TRADE SECRET INFORMATION OF TECHNOLOGIC SYSTEMS.
 * ACCESS TO THIS WORK IS RESTRICTED TO (I) TECHNOLOGIC SYSTEMS
 * EMPLOYEES WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE
 * OF THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN TECHNOLOGIC
 * SYSTEMS WHO HAVE ENTERED INTO APPROPRIATE LICENSE AGREEMENTS.  NO
 * PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, COPIED,
 * DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, CONDENSED,
 * EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, TRANSFORMED, ADAPTED
 * IN ANY FORM OR BY ANY MEANS, MANUAL, MECHANICAL, CHEMICAL,
 * ELECTRICAL, ELECTRONIC, OPTICAL, BIOLOGICAL, OR OTHERWISE WITHOUT
 * THE PRIOR WRITTEN PERMISSION AND CONSENT OF TECHNOLOGIC SYSTEMS.
 * ANY USE OR EXPLOITATION OF THIS WORK WITHOUT THE PRIOR WRITTEN
 * CONSENT OF TECHNOLOGIC SYSTEMS COULD SUBJECT THE PERPETRATOR TO
 * CRIMINAL AND CIVIL LIABILITY.
 */
/* To compile jed2vme, use the appropriate cross compiler and run the
 * command:
 *
 *   gcc -Wall -O -mcpu=arm9 -o jed2vme jed2vme.c
 *
 * On uclibc based initrd's, the following additional gcc options are
 * necessary: -Wl,--rpath,/slib -Wl,-dynamic-linker,/slib/ld-uClibc.so.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

static unsigned char preamble_5k[] = {
  0x65, 0xb1, 0xac, 0x5f, 0x5f, 0x5f, 0x5f, 0x31, 0x32, 0x2e, 0x31, 0xf2,
  0x19, 0xfe, 0x04, 0x56, 0x01, 0x10, 0x00, 0x08, 0x00, 0x06, 0x00, 0x09,
  0x00, 0x07, 0x00, 0x02, 0x03, 0x03, 0x02, 0x0b, 0xc0, 0x84, 0x3d, 0x10,
  0x01, 0x11, 0x08, 0x13, 0x68, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xff,
  0xff, 0x14, 0xc2, 0x09, 0x94, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff, 0x70,
  0x11, 0x08, 0x13, 0x38, 0x70, 0x12, 0x8a, 0x03, 0x13, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x70,
  0x11, 0x08, 0x13, 0xaa, 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a,
  0x94, 0x80, 0x02, 0x11, 0x08, 0x13, 0xc0, 0x70, 0x10, 0x01, 0x1b, 0x03,
  0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x11, 0x08, 0x13, 0x84, 0x70, 0x10,
  0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x11, 0x08, 0x13,
  0xe6, 0x70
};

static unsigned char postamble_5k[] = {
  0x11, 0x08, 0x13, 0x58, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xca, 0x2a,
  0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x11,
  0x08, 0x13, 0xa2, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xff, 0xff, 0x70,
  0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x94, 0x80, 0x02, 0x11, 0x08,
  0x13, 0x22, 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80,
  0x02, 0x12, 0x20, 0x13, 0x00, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff,
  0xff, 0x70, 0x11, 0x08, 0x13, 0x4d, 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a,
  0x03, 0x1a, 0x81, 0x80, 0x02, 0x12, 0x08, 0x13, 0x00, 0x14, 0x00, 0x15,
  0x80, 0x70, 0x11, 0x08, 0x13, 0xf4, 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a,
  0x03, 0x1a, 0xb2, 0x80, 0x02, 0x11, 0x08, 0x13, 0x4d, 0x70, 0x10, 0x01,
  0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x12, 0x08, 0x13, 0x00,
  0x14, 0x40, 0x15, 0xc0, 0x70, 0x11, 0x08, 0x13, 0x78, 0x70, 0x10, 0x01,
  0x1b, 0x03, 0x1a, 0x03, 0x1a, 0xc8, 0x81, 0x02, 0x11, 0x08, 0x13, 0xff,
  0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x10,
  0x00, 0x7f
};

static inline void printbuf_5k(unsigned char *b) {
	static unsigned char post[] =
	  { 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02 };
	static unsigned char pre[] = { 0x12, 0xfe, 0x04, 0x13 };

	fwrite(pre, sizeof(pre), 1, stdout);
	fwrite(b, 80, 1, stdout);
	fwrite(post, sizeof(post), 1, stdout);
}


static unsigned char preamble_8k[] = {
  0x65, 0x4b, 0xed, 0x5f, 0x5f, 0x5f, 0x5f, 0x31, 0x32, 0x2e, 0x31, 0xf2,
  0x19, 0x84, 0x06, 0x56, 0x01, 0x10, 0x00, 0x08, 0x00, 0x06, 0x00, 0x09,
  0x00, 0x07, 0x00, 0x02, 0x03, 0x03, 0x02, 0x0b, 0xc0, 0x84, 0x3d, 0x10,
  0x01, 0x11, 0x08, 0x13, 0x68, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xff,
  0xff, 0x14, 0xc2, 0x05, 0x94, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff, 0x70,
  0x11, 0x08, 0x13, 0x38, 0x70, 0x12, 0x8a, 0x04, 0x13, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xc0, 0x70, 0x11, 0x08, 0x13, 0xaa, 0x70, 0x10, 0x01, 0x1b,
  0x03, 0x1a, 0x81, 0x80, 0x02, 0x11, 0x08, 0x13, 0xc0, 0x70, 0x10, 0x01,
  0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x11, 0x08, 0x13, 0x84, 0x70, 0x10,
  0x01, 0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x11, 0x08, 0x13, 0xe6, 0x70
};

static unsigned char postamble_8k[] = {
  0x11, 0x08, 0x13, 0x58, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xca, 0x2a,
  0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x11, 0x08, 0x13,
  0xa2, 0x70, 0x12, 0x20, 0x13, 0xff, 0xff, 0xff, 0xff, 0x70, 0x10, 0x01,
  0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x11, 0x08, 0x13, 0x22, 0x70, 0x10,
  0x01, 0x1b, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x12, 0x20, 0x13, 0x00, 0x00,
  0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, 0x70, 0x11, 0x08, 0x13, 0x4d,
  0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x12, 0x08, 0x13,
  0x00, 0x14, 0x00, 0x15, 0x80, 0x70, 0x11, 0x08, 0x13, 0xf4, 0x70, 0x10,
  0x01, 0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x11, 0x08, 0x13, 0x4d, 0x70,
  0x10, 0x01, 0x1b, 0x03, 0x1a, 0x81, 0x80, 0x02, 0x12, 0x08, 0x13, 0x00,
  0x14, 0x40, 0x15, 0xc0, 0x70, 0x11, 0x08, 0x13, 0x78, 0x70, 0x10, 0x01,
  0x1b, 0x03, 0x1a, 0xc8, 0x81, 0x02, 0x11, 0x08, 0x13, 0xff, 0x70, 0x10,
  0x01, 0x1b, 0x03, 0x1a, 0x8a, 0x80, 0x02, 0x10, 0x00, 0x7f
};

static inline void printbuf_8k(unsigned char *b) {
	static unsigned char post[] =
	  { 0x70, 0x10, 0x01, 0x1b, 0x03, 0x1a, 0x03, 0x1a, 0x81, 0x80, 0x02 };
	static unsigned char pre[] = { 0x12, 0x84, 0x06, 0x13 };

	fwrite(pre, sizeof(pre), 1, stdout);
	fwrite(b, 97, 1, stdout);
	fwrite(post, sizeof(post), 1, stdout);
}

static void usage(void) {
	fprintf(stderr, "Usage: jed2vme [FILE]\n"
	  "Converts TS Lattice XP2 JEDEC .jed files to .vme files on stdout\n"
	  "Example: jed2vme ts7500.jed > ts7500.vme\n"
	  "\n"
	  "Options:\n"
	  "  -h, --help              This help\n"
	  "\n"
	  "If FILE is not given, converts JEDEC file on stdin.  If FILE ends\n"
	  "with .bz2 or .gz, jed2vme will attempt to decompress.\n"
	  );
}

int main(int argc, char **argv) {
	int c, xp2_8k = 0;
	int state = 0;
	int bit = 0;
	unsigned char buf[98];
	unsigned int sr = 0;
	FILE *in;

	if (argc > 1) {
		char b[512];
		int l = strlen(argv[1]);
		
		if (argv[1][0] == '-') {
			  usage();
			  return 0;
		} else if (strcmp(".bz2", &argv[1][l - 4]) == 0) {
			snprintf(b, 512, "exec bunzip2 -c '%s'", argv[1]);
			in = popen(b, "r");
		} else if (strcmp(".gz", &argv[1][l - 3]) == 0) {
			snprintf(b, 512, "exec gunzip -c '%s'", argv[1]);
			in = popen(b, "r");
		} else in = fopen(argv[1], "r");

		if (in == NULL) {
			perror(argv[1]);
			return 2;
		}
	} else in = stdin;
	
	while ((c = fgetc(in)) != EOF) {
		switch (state) {
		case 0:
			if (c == 2) state = 3;
			break;
		case 1: /* tag type  */
			if (c == 'Q') {
				char *x = (char *)buf;
				fscanf(in, "%s", x);
				if (strcmp("F1954736*", x) == 0) xp2_8k = 1;
				state = 3;
			} else if (c == 'L') {
				int offs;
				fscanf(in, "%x", &offs);
				if (xp2_8k) fwrite(preamble_8k, 
				  sizeof(preamble_8k), 1, stdout);
				else fwrite(preamble_5k, 
				  sizeof(preamble_5k), 1, stdout);
				if (offs == 0) state = 4;
				else state = 2;
			} else state = 2;
			break;
		case 2: /* skip until asterisk */
			if (c == '*') state = 3;
			break;
		case 3: /* skip until EOL */
			if (c == '\n') state = 1;
			break;
		case 4: /* next line has our data */
			if (c == '\n') state = 5;
			break;
		case 5: /* 1's and 0's */
			if (c == '0' || c == '1') {
				bit++;
				sr = (sr << 1) | (c - '0');
				if ((bit & 0x7) == 0) buf[(bit>>3) - 1] = sr;
				if (!xp2_8k && bit == 638) {
					bit = 0;
					buf[79] = sr<<2;
					printbuf_5k(buf);
				} else if (bit == 772) {
					bit = 0;
					buf[96] = sr<<4;
					printbuf_8k(buf);
				}
			} else if (c == '*') {
				assert(bit == 0);
				state = 3;
				if (pclose(in) == -1) fclose(in);
				if (xp2_8k) fwrite(postamble_8k, 
				  sizeof(postamble_8k), 1, stdout);
				else fwrite(postamble_5k,
				  sizeof(postamble_5k), 1, stdout);
				return 0;
			}
			break;
		}
	}
	if (pclose(in) == -1) fclose(in);
	return 1;
}
