/*
 * Copyright (C) 2015 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

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

const char *progname;
const char *method;
int opt_v = 0;
const char *chip;

static int ncols;
static int nrows;
static char *ioname[1024];
static int iotype[1024];
static int nios;
static char *matrix[1024][32][4];

static void
read_inout(const char *filename)
{
	FILE *fp;
	char line[1024];

	fp = fopen(filename, "r");
	assert(fp);

	nios = 0;
	while (fgets(line, sizeof(line), fp) != NULL) {
		char *ptr;
		char s[1024];
		int slen;

		if (line[0] != '#') {
			ptr = line;

			/* Read name. */
			slen = 0;
			while (*ptr != ':') {
				if (*ptr == 'x') {
					s[slen++] = 'X';
				} else {
					s[slen++] = tolower(*ptr);
				}
				ptr++;
			}
			s[slen] = '\0';

			ioname[nios] = strdup(s);
			assert(ioname[nios]);

			/* Skip ':'. */
			ptr++;

			/* Skip space. */
			while (*ptr == ' ') {
				ptr++;
			}

			/* Read in/out/inout. */
			slen = 0;
			while (*ptr != '\n') {
				s[slen++] = *ptr++;
			}
			s[slen] = '\0';

			if (strcmp(s, "in") == 0) {
				iotype[nios] = 2;
			} else if (strcmp(s, "out") == 0) {
				iotype[nios] = 1;
			} else if (strcmp(s, "inout") == 0) {
				iotype[nios] = 2 | 1;
			} else {
				assert(0);
			}
			nios++;

			/* Skip '\n'. */
			ptr++;
		}
	}

	fclose(fp);
}

static int
match(const char *name, const char *pattern)
{
	for (;;) {
		if (*name == '\0'
		 && *pattern == '\0') {
			return 1;
		}
		if (*name == *pattern) {
			name++;
			pattern++;
		} else if ('0' <= *name && *name <= '9' && *pattern == 'X') {
			while ('0' <= *name
			    && *name <= '9') {
				name++;
			}
			pattern++;
		} else {
			return 0;
		}
	}
}

static int
io_get(const char *name)
{
	int i;

	for (i = 0; ; i++) {
		if (i == nios) {
			fprintf(stderr, "%s: port type unknown.\n", name);
			assert(0);
			exit(1);
		}
		if (match(name, ioname[i])) {
			return iotype[i];
		}
	}
}

static void
split(const char *name, char *fp, int *np)
{
	char *ptr;

	strcpy(fp, name);

	ptr = fp + strlen(fp);
	while ('0' <= ptr[-1] && ptr[-1] <= '9') {
		ptr--;
	}

	if (*ptr) {
		*np = atoi(ptr);
		*ptr = '\0';
	} else {
		*np = -1;
	}
}

static void
read_matrix(const char *filename)
{
	FILE *fp;
	char line[1024];
	int row;
	int col;
	char *ptr;

	fp = fopen(filename, "r");
	assert(fp);

	row = 0;
	while (fgets(line, sizeof(line), fp) != NULL) {
		col = 0;
		ptr = line;
		if (*ptr == '#') {
			/* Comment */
			while (*ptr != '\n') {
				ptr++;
			}

		} else if (*ptr == '-') {
			/* Separator */
			while (*ptr != '\n') {
				assert(*ptr == '-'
				    || *ptr == '+');
				ptr++;
			}
			if (matrix[row][0][0]) {
				row++;
			}

		} else {
			/* Pin Definition */
			while (*ptr != '\n') {
				while (*ptr == '\t'
				    || *ptr == ' ') {
					ptr++;
				}
				if (*ptr == '\n') {
					/* EOLN */
				} else if (*ptr == '|') {
					ptr++;
					col++;
					if (ncols <= col) {
						ncols = col + 1;
					}
				} else {
					char name[1024];
					int namelen;
					int n;

					namelen = 0;
					while (*ptr != '\n'
					    && *ptr != ' '
					    && *ptr != '|') {
						if (*ptr != '/') {
							name[namelen++] = tolower(*ptr);
						}
						ptr++;
					}
					name[namelen] = '\0';
					while (*ptr == '\t'
					    || *ptr == ' ') {
						ptr++;
					}

					for (n = 0; ; n++) {
						assert(n < 4);
						if (matrix[row][col][n] == NULL) {
							break;
						}
					}
					matrix[row][col][n] = strdup(name);
					assert(matrix[row][col][n]);
				}
			}
		}
	}
	nrows = row + 1;

	fclose(fp);
}

static int
col_width(int col)
{
	int width;
	int row;
	int r;

	width = 0;
	for (row = 0; row < nrows; row++) {
		for (r = 0; r < 4; r++) {
			if (matrix[row][col][r]
			 && width < strlen(matrix[row][col][r])) {
				width = strlen(matrix[row][col][r]);
			}
		}
	}

	return width;
}

static int
row_height(int row)
{
	int height;
	int col;
	int r;

	height = 0;
	for (col = 0; col < ncols; col++) {
		for (r = 0; r < 4; r++) {
			if (height <= r
			 && matrix[row][col][r]) {
				height = r + 1;
			}
		}
	}

	return height;
}

static void
write_table(const char *filename)
{
	FILE *fp;
	int row;
	int col;
	int r;
	int ret;

	fp = fopen(filename, "w");
	assert(fp);

	fprintf(fp, "#\n");
	fprintf(fp, "# Copyright (C) 2016 FAUmachine Team <info@faumachine.org>.\n");
	fprintf(fp, "# This program is free software. You can redistribute it and/or modify it\n");
	fprintf(fp, "# under the terms of the GNU General Public License, either version 2 of\n");
	fprintf(fp, "# the License, or (at your option) any later version. See COPYING.\n");
	fprintf(fp, "#\n");

	for (row = 0; row < nrows; row++) {
		int height;

		for (col = 0; col < ncols; col++) {
			int width;

			if (col != 0) {
				fprintf(fp, "+");
			}
			width = col_width(col);
			if (width != 0) {
				int n;

				fprintf(fp, "-");
				for (n = 0; n < width; n++) {
					fprintf(fp, "-");
				}
				fprintf(fp, "-");
			}
		}
		fprintf(fp, "\n");

		/* Get height of row. */
		height = row_height(row);

		for (r = 0; r < height; r++) {
			for (col = 0; col < ncols; col++) {
				int width;

				if (col != 0) {
					fprintf(fp, "|");
				}

				/* Get width of column. */
				width = col_width(col);

				/* Print contents of column. */
				if (width != 0) {
					int n;

					fprintf(fp, " ");
					if (matrix[row][col][r]) {
						for (n = 0; n < strlen(matrix[row][col][r]); n++) {
							fprintf(fp, "%c", toupper(matrix[row][col][r][n]));
						}
					} else {
						n = 0;
					}
					for ( ; n < width; n++) {
						fprintf(fp, " ");
					}
					fprintf(fp, " ");
				}
			}
			fprintf(fp, "\n");
		}
	}

	ret = fclose(fp);
	assert(0 <= ret);
}

static int
chip_get(void)
{
	static const char *chip_name[][2] = {
		{ "32-QFN", "QFN32" },
		{ "100-LQFP", "LQFP100" },
		{ NULL, NULL }
	};
	static int chip_col = -1;

	if (chip_col == -1) {
		int c;

		for (c = 0; ; c++) {
			if (chip_name[c][0] == NULL) {
				fprintf(stderr, "ERROR: %s: Unknown chip.\n", chip);
				exit(1);
			}
			if (strcasecmp(chip, chip_name[c][0]) == 0
			 || strcasecmp(chip, chip_name[c][1]) == 0) {
				break;
			}
		}
		for (chip_col = 0; ; chip_col++) {
			if (chip_col == ncols) {
				fprintf(stderr, "ERROR: %s: Not in table.\n", chip);
				exit(1);
			}
			if (matrix[0][chip_col][0] != NULL
			 && (strcasecmp(matrix[0][chip_col][0], chip_name[c][0]) == 0
			  || strcasecmp(matrix[0][chip_col][0], chip_name[c][1]) == 0)) {
				break;
			}
		}
	}

	return chip_col;
}

static int
pin_get(void)
{
	static int pin_col = -1;

	if (pin_col == -1) {
		for (pin_col = 0; ; pin_col++) {
			assert(pin_col < ncols);
			if (matrix[0][pin_col][0] != NULL
			 && matrix[0][pin_col][1] != NULL
			 && strcmp(matrix[0][pin_col][0], "pin") == 0
			 && strcmp(matrix[0][pin_col][1], "name") == 0) {
				break;
			}
		}
	}

	return pin_col;
}

static int
isport(const char *str)
{
	if (! str) {
		return 0;
	}
	if (strncmp(str, "pt", 2) == 0
	 && isalpha(str[2])
	 && isdigit(str[3])) {
		return 1;
	}
	if (strncmp(str, "p", 1) == 0
	 && isalpha(str[1])
	 && isdigit(str[2])) {
		return 1;
	}
	return 0;
}

static int
port_row(int port, int pin)
{
	static int table['z' - 'a' + 1][64];
	static int init = 0;

	assert('a' <= port && port <= 'z');
	assert(0 <= pin && pin < 64);

	if (! init) {
		int po;
		int pi;
		int pin_col;
		int row;

		/* Set default. */
		for (po = 'a'; po <= 'z'; po++) {
			for (pi = 0; pi < 64; pi++) {
				table[po - 'a'][pi] = -1;
			}
		}

		/* Get rows. */
		pin_col = pin_get();

		for (row = 1; row < nrows; row++) {
			if (isport(matrix[row][pin_col][0])) {
				char name[100];
				int num;

				split(matrix[row][pin_col][0], name, &num);
				assert('a' <= name[strlen(name) - 1]
				    && name[strlen(name) - 1] <= 'z');
				assert(0 <= num && num <= 31);
				table[name[strlen(name) - 1] - 'a'][num] = row;
			}
		}
		
		init = 1;
	}

	return table[port - 'a'][pin];
}

static int
row_port_first(void)
{
	int pin_col;
	int row;

	pin_col = pin_get();

	for (row = 1; ; row++) {
		assert(row < nrows);

		if (isport(matrix[row][pin_col][0])) {
			return row;
		}
	}
}

static int
row_port_next(int row)
{
	int pin_col;

	pin_col = pin_get();

	for (;;) {
		row++;
		if (row == nrows) {
			return -1;
		}
		if (isport(matrix[row][pin_col][0])) {
			return row;
		}
	}
}

static int
def_get(void)
{
	static int def_col = -1;

	if (def_col == -1) {
		for (def_col = 0; ; def_col++) {
			assert(def_col < ncols);

			if (matrix[0][def_col][0] != NULL
			 && strcmp(matrix[0][def_col][0], "default") == 0) {
				break;
			}
		}
		assert(0 <= def_col
		    && def_col < ncols
		    && strcmp(matrix[0][def_col][0], "default") == 0);
	}

	return def_col;
}

static int
alt_get(int alt)
{
	static int alt_table[17] = {
		-1, -1, -1, -1,
		-1, -1, -1, -1,
		-1, -1, -1, -1,
		-1, -1, -1, -1,
		-1
	};

	if (alt_table[0] == -1) {
		int col;

		for (col = 0; col < ncols; col++) {
			if (matrix[0][col][0] != NULL
			 && strncmp(matrix[0][col][0], "alt", 3) == 0) {
				char name[100];
				int num;

				split(matrix[0][col][0], name, &num);
				assert(num < sizeof(alt_table) / sizeof(alt_table[0]) - 1);
				alt_table[num] = col;
			}
		}
	}

	return alt_table[alt];
}

static const char *
def_lookup(char c, int num, int n)
{
	int row;
	int col;

	/* Lookup row. */
	row = port_row(c, num);
	if (row < 0) {
		return NULL;
	}

	/* Lookup default col. */
	col = def_get();
	if (col < 0) {
		return NULL;
	}

	return matrix[row][col][n];
}

static const char *
lookup(char c, int num, int alt, int n)
{
	int row;
	int col;

	/* Lookup row. */
	row = port_row(c, num);
	if (row < 0) {
		return NULL;
	}

	/* Lookup col. */
	col = alt_get(alt);
	if (col < 0) {
		return NULL;
	}

	return matrix[row][col][n];
}

static int
sig_same(char port0, int pin0, int alt0, char port1, int pin1, int alt1)
{
	int n1;
	const char *ent0;
	const char *ent1;

	ent0 = lookup(port0, pin0, alt0, 0);
	assert(ent0);

	for (n1 = 0; n1 < 4; n1++) {
		ent1 = lookup(port1, pin1, alt1, n1);
		if (ent1
		 && strcmp(ent0, ent1) == 0) {
			return 1;
		}
	}
	return 0;
}

static int
sig_done(char port1, int pin1, int alt1)
{
	char port0;
	int pin0;
	int alt0;

	for (port0 = 'a'; port0 < 'z'; port0++) {
		for (pin0 = 0; pin0 < 32; pin0++) {
			for (alt0 = 0; alt0 < 8; alt0++) {
				int row;
				int col;

				if (port0 == port1
				 && pin0 == pin1
				 && alt0 == alt1) {
					return 0;
				}
				row = port_row(port0, pin0);
				col = alt_get(alt0);
				if (row != -1
				 && col != -1
				 && matrix[row][col][0]
				 && sig_same(port0, pin0, alt0, port1, pin1, alt1)) {
					return 1;
				}
			}
		}
	}
	/*NOTREACHED*/
	return 0;
}

static void
sig_first(char *portp, int *pinp, int *altp)
{
	char port;
	int pin;
	int alt;
	int row;
	int col;

	for (port = 'a'; ; port++) {
		assert(port <= 'z');

		for (pin = 0; pin < 32; pin++) {
			for (alt = 0; alt < 8; alt++) {
				row = port_row(port, pin);
				col = alt_get(alt);
				if (row != -1
				 && col != -1
				 && matrix[row][col][0]) {
					goto found;
				}
			}
		}
	}

found:	;
	*portp = port;
	*pinp = pin;
	*altp = alt;
}

static void
sig_next(char port, int pin, int alt, char *portp, int *pinp, int *altp)
{
	int row;
	int col;

	goto start;

	for (port = 'a'; port < 'z'; port++) {
		for (pin = 0; pin < 32; pin++) {
			for (alt = 0; alt < 8; alt++) {
				row = port_row(port, pin);
				col = alt_get(alt);
				if (row != -1
				 && col != -1
				 && matrix[row][col][0]
				 && ! sig_done(port, pin, alt)) {
					goto found;
				}
			start:	;
			}
		}
	}

	port = -1;
	pin = -1;
	alt = -1;
found:	;
	*portp = port;
	*pinp = pin;
	*altp = alt;
}

static void
sig_other_first(char port0, int pin0, int alt0, char *port1p, int *pin1p, int *alt1p)
{
	char port1;
	int pin1;
	int alt1;
	int row;
	int col;

	port1 = port0;
	pin1 = pin0;
	alt1 = alt0;

	goto start;

	for (port1 = 'a'; port1 <= 'z'; port1++) {
		for (pin1 = 0; pin1 < 32; pin1++) {
			for (alt1 = 0; alt1 < 8; alt1++) {
				row = port_row(port1, pin1);
				col = alt_get(alt1);
				if (row != -1
				 && col != -1
				 && matrix[row][col][0]
				 && sig_same(port0, pin0, alt0, port1, pin1, alt1)) {
					goto found;
				}
			start:	;
			}
		}
	}
	port1 = -1;
	pin1 = -1;
	alt1 = -1;
found:
	*port1p = port1;
	*pin1p = pin1;
	*alt1p = alt1;
}

static void
sig_other_next(char port0, int pin0, int alt0, char *port1p, int *pin1p, int *alt1p)
{
	sig_other_first(port0, pin0, alt0, port1p, pin1p, alt1p);
}

static int
check_matrix_match(int port0, int pin0, int alt0, int n0, int port1, int pin1, int alt1)
{
	const char *ent0;
	const char *ent1;
	int n1;

	if (alt0 == -1) {
		ent0 = def_lookup(port0, pin0, n0);
	} else {
		ent0 = lookup(port0, pin0, alt0, n0);
	}

	for (n1 = 0; ; n1++) {
		if (n1 == 4) {
			if (opt_v) {
				fprintf(stderr, "WARNING: Cell mismatch:\n");
				if (alt0 == -1) {
					fprintf(stderr, "\tpt%c%d default", port0, pin0);
				} else {
					fprintf(stderr, "\tpt%c%d alt%d", port0, pin0, alt0);
				}
				fprintf(stderr, " <-> ");
				if (alt1 == -1) {
					fprintf(stderr, "pt%c%d default\n", port1, pin1);
				} else {
					fprintf(stderr, "pt%c%d alt%d\n", port1, pin1, alt1);
				}
				fprintf(stderr, "\tAdding %s.\n", ent0);
			}

			for (n1 = 0; ; n1++) {
				int row;
				int col;

				assert(n1 < 4);

				row = port_row(port1, pin1);
				if (alt1 == -1) {
					col = def_get();
				} else {
					col = alt_get(alt1);
				}

				if (! matrix[row][col][n1]) {
					matrix[row][col][n1] = strdup(ent0);
					assert(matrix[row][col][n1]);
					break;
				}
			}
			return 0;
		}

		if (alt1 == -1) {
			ent1 = def_lookup(port1, pin1, n1);
		} else {
			ent1 = lookup(port1, pin1, alt1, n1);
		}

		if (ent0 == NULL) {
			return 1;
		}
		if (ent1 != NULL
		 && strcmp(ent0, ent1) == 0) {
			return 1;
		}
	}
}

static void
check_matrix_cell(int port0, int pin0, int alt0, int port1, int pin1, int alt1)
{
	int n0;
	int n1;

	/* Look for any match. */
	for (n0 = 0; n0 < 4; n0++) {
		for (n1 = 0; n1 < 4; n1++) {
			const char *ent0;
			const char *ent1;

			if (alt0 == -1) {
				ent0 = def_lookup(port0, pin0, n0);
			} else {
				ent0 = lookup(port0, pin0, alt0, n0);
			}
			if (alt1 == -1) {
				ent1 = def_lookup(port1, pin1, n1);
			} else {
				ent1 = lookup(port1, pin1, alt1, n1);
			}

			if (ent0 != NULL
			 && ent1 != NULL
			 && strcmp(ent0, ent1) == 0) {
				goto match;
			}
		}
	}

	return;

match:	;
	/* Check, that *all* entries match. */
	for (n0 = 0; n0 < 4; n0++) {
		check_matrix_match(port0, pin0, alt0, n0, port1, pin1, alt1);
	}
	for (n1 = 0; n1 < 4; n1++) {
		check_matrix_match(port1, pin1, alt1, n1, port0, pin0, alt0);
	}
}

static void
check_matrix(void)
{
	char c0;
	int pin0;
	int alt0;
	char c1;
	int pin1;
	int alt1;

	for (c0 = 'a'; c0 <= 'z'; c0++) {
		for (pin0 = 0; pin0 < 32; pin0++) {
			for (alt0 = -1; alt0 < 8; alt0++) {
				for (c1 = c0; c1 <= 'z'; c1++) {
					for (pin1 = 0; pin1 < 32; pin1++) {
						for (alt1 = -1; alt1 < 8; alt1++) {
							if (c1 < c0
							 || (c1 == c0 && pin1 < pin0)
							 || (c1 == c0 && pin1 == pin0 && alt1 < alt0)
							 || (c1 == c0 && pin1 == pin0 && alt1 == alt0)) {
								continue;
							}
							check_matrix_cell(c0, pin0, alt0, c1, pin1, alt1);
						}
					}
				}
			}
		}
	}
}

static void
write_src_100_lqfp_pin(FILE *fp, int pin, char pos)
{
	int chip_col;
	int pin_col;
	int row;

	chip_col = chip_get();
	pin_col = pin_get();

	/* Lookup pin. */
	for (row = 0; ; row++) {
		char str[10];

		assert(row < nrows);
		sprintf(str, "%d", pin);
		if (strcmp(matrix[row][chip_col][0], str) == 0) {
			break;
		}
	}
	fprintf(fp, "%s", matrix[row][chip_col][0]);
	fprintf(fp, "\t");
	fprintf(fp, "%d", pin);
	fprintf(fp, "\t");
	switch (io_get(matrix[row][pin_col][0])) {
	case 1: fprintf(fp, "out"); break;
	case 2: fprintf(fp, "in"); break;
	case 3: fprintf(fp, "io"); break;
	}
	fprintf(fp, "\t");
	fprintf(fp, "line");
	fprintf(fp, "\t");
	fprintf(fp, "%c", pos);
	fprintf(fp, "\t");
	fprintf(fp, "\t");
	fprintf(fp, "%s", matrix[row][pin_col][0]);
	fprintf(fp, "\t");
	fprintf(fp, "std_logic");
	fprintf(fp, "\n");
}

static void
write_src_32_qfn(FILE *fp)
{
	int pin;

	for (pin = 1; pin <= 8; pin++) {
		write_src_100_lqfp_pin(fp, pin, 't');
	}
	fprintf(fp, "#\n");
	for (pin = 9; pin <= 16; pin++) {
		write_src_100_lqfp_pin(fp, pin, 'r');
	}
	fprintf(fp, "#\n");
	for (pin = 24; 17 <= pin; pin--) {
		write_src_100_lqfp_pin(fp, pin, 'b');
	}
	fprintf(fp, "#\n");
	for (pin = 32; 25 <= pin; pin--) {
		write_src_100_lqfp_pin(fp, pin, 'l');
	}
}

static void
write_src_100_lqfp(FILE *fp)
{
	int pin;

	for (pin = 1; pin <= 25; pin++) {
		write_src_100_lqfp_pin(fp, pin, 'l');
	}
	fprintf(fp, "#\n");
	for (pin = 26; pin <= 50; pin++) {
		write_src_100_lqfp_pin(fp, pin, 'b');
	}
	fprintf(fp, "#\n");
	for (pin = 75; 51 <= pin; pin--) {
		write_src_100_lqfp_pin(fp, pin, 'r');
	}
	fprintf(fp, "#\n");
	for (pin = 100; 76 <= pin; pin--) {
		write_src_100_lqfp_pin(fp, pin, 't');
	}
}

static void
write_src(const char *filename)
{
	FILE *fp;

	fp = fopen(filename, "w");
	assert(fp);

	if (strcmp(chip, "32-QFN") == 0) {
		write_src_32_qfn(fp);

	} else if (strcmp(chip, "100-LQFP") == 0) {
		write_src_100_lqfp(fp);

	} else {
		fprintf(stderr, "Unknown chip...\n");
		exit(1);
	}

	fclose(fp);
}

static int
have_pin(const char *name, int num)
{
	char port;
	int pin;
	int alt;
	int n;
	char name2[1024];
	int num2;

	for (port = 'a'; port <= 'z'; port++) {
		for (pin = 0; pin < 32; pin++) {
			for (alt = 0; alt < 8; alt++) {
				for (n = 0; n < 4; n++) {
					const char *ent;

					ent = lookup(port, pin, alt, n);
					if (! ent) continue;
					split(ent, name2, &num2);
					if (strcmp(name, name2) != 0
					 || num != num2) continue;

					return 1;
				}
			}
		}
	}

	return 0;
}

static void
do_pin(FILE *fp, const char *name, int num)
{
	char port;
	int pin;
	int alt;
	int n;
	char name2[1024];
	int num2;

	if (num == -1) {
		fprintf(fp, "\tcpssp->NAME.%s = val;\n", name);
	} else {
		fprintf(fp, "\t\tcpssp->NAME.%s%d = val;\n", name, num);
	}
	for (port = 'a'; port <= 'z'; port++) {
		for (pin = 0; pin < 32; pin++) {
			for (alt = 0; alt < 8; alt++) {
				for (n = 0; n < 4; n++) {
					const char *ent;

					ent = lookup(port, pin, alt, n);
					if (! ent) continue;
					split(ent, name2, &num2);
					if (strcmp(name, name2) != 0
					 || num != num2) continue;

					if (num != -1) {
						fprintf(fp, "\t");
					}
					fprintf(fp, "\tNAME_(%c%d_%d_int_in_set)(cpssp);\n",
							port, pin, alt);
					return;
				}
			}
		}
	}
}

static void
write_comp_pin(FILE *fp, const char *name, int num)
{
	if (0 <= num) {
		fprintf(fp, "static void\n");
		fprintf(fp, "%s_outN_set(struct cpssp *cpssp, int n, unsigned int val)\n",
				name);
	} else {
		fprintf(fp, "static void\n");
		fprintf(fp, "%s_out_set(struct cpssp *cpssp, unsigned int val)\n",
				name);
	}
	fprintf(fp, "{\n");
	
	if (0 <= num) {
		fprintf(fp, "\tswitch (n) {\n");

		for (num = 0; num < 64; num++) {
			if (have_pin(name, num)) {
				fprintf(fp, "\tcase %d:\n", num);
				do_pin(fp, name, num);
				fprintf(fp, "\t\tbreak;\n");
			}
		}

		fprintf(fp, "\tdefault:\n");
		fprintf(fp, "\t\tbreak;\n");
		fprintf(fp, "\t}\n");

	} else {
		do_pin(fp, name, num);
	}

	fprintf(fp, "}\n");
	fprintf(fp, "\n");
}

static void
write_matrix(const char *filename)
{
	int nports;
	int npins;
	int nalts;
	char todo_name[1024][1024];
	int todo_num[1024];
	int ntodos;
	int todo;
	FILE *fp;
	int row;
	int col;
	char c;
	int pin;
	int alt;
	int n;
	char name[1024];
	int num;
	int done;
	const char *ent;

	/* Get number of ports/pins. */
	nports = 0;
	npins = 0;
	for (c = 'a'; c <= 'z'; c++) {
		for (pin = 0; pin < 64; pin++) {
			if (port_row(c, pin) != -1) {
				if (nports < c - 'a' + 1) {
					nports = c - 'a' + 1;
				}
				if (npins < pin) {
					npins = pin + 1;
				}
			}
		}
	}
	fprintf(stderr, "%d ports, %d pins\n", nports, npins);

	/* Get number of alternatives. */
	for (alt = 0; 0 <= alt_get(alt); alt++) {
	}
	nalts = alt;

	/* Get all signal names. */
	ntodos = 0;
	for (row = row_port_first(); row != -1; row = row_port_next(row)) {
		for (alt = 0, col = alt_get(alt); col != -1; alt++, col = alt_get(alt)) {
			for (n = 0; n < 4 && matrix[row][col][n]; n++) {
				split(matrix[row][col][n], name, &num);

				for (todo = 0; ; todo++) {
					if (todo == ntodos) {
						strcpy(todo_name[ntodos], name);
						todo_num[ntodos] = num;
						ntodos++;
					}
					if (strcmp(todo_name[todo], name) == 0) {
						break;
					}
				}
			}
		}
	}
	do {
		done = 1;

		for (todo = 0; todo < ntodos - 1; todo++) {
			if (strcmp(todo_name[todo + 1], todo_name[todo]) < 0
			 || (strcmp(todo_name[todo + 1], todo_name[todo]) == 0
			  && todo_num[todo + 1] < todo_num[todo])) {
				strcpy(name, todo_name[todo]);
				num = todo_num[todo];
				strcpy(todo_name[todo], todo_name[todo + 1]);
				todo_num[todo] = todo_num[todo + 1];
				strcpy(todo_name[todo + 1], name);
				todo_num[todo + 1] = num;
				done = 0;
			}
		}
	} while (! done);


	fp = fopen(filename, "w");
	assert(fp);

	fprintf(fp, "#ifdef STATE\n");
	fprintf(fp, "\n");

	fprintf(fp, "struct {\n");

	for (todo = 0; todo < ntodos; todo++) {
		if (todo_num[todo] < 0) {
			sprintf(name, "%s", todo_name[todo]);
		} else {
			sprintf(name, "%s0", todo_name[todo]);
		}
		if (io_get(name) & 1) {
			if (todo_num[todo] < 0) {
				fprintf(fp, "\tunsigned int %s;\n", todo_name[todo]);
			} else {
				for (pin = 0; pin < 64; pin++) {
					if (have_pin(todo_name[todo], pin)) {
						fprintf(fp, "\tunsigned int %s%d;\n", todo_name[todo], pin);
					}
				}
			}
		}
	}

	for (c = 'a'; c < 'a' + nports; c++) {
		fprintf(fp, "\tunsigned int %c_ext[%d][%d];\n", c, npins, nalts);
	}

	fprintf(fp, "} NAME;\n");

	fprintf(fp, "\n");
	fprintf(fp, "#endif /* STATE */\n");
	fprintf(fp, "#ifdef EXPORT\n");

	fprintf(fp, "\n");
	for (c = 'a'; c <= 'z'; c++) {
		int have;

		have = 0;
		for (pin = 0; pin < npins; pin++) {
			if (port_row(c, pin) != -1) {
				have = 1;
				break;
			}
		}
		if (! have) {
			continue;
		}

		fprintf(fp, "/*forward*/ static void\n");
		fprintf(fp, "NAME_(port%cNM_in_set)(struct cpssp *cpssp, int alt, int pin, unsigned int val);\n", c);
	}

	fprintf(fp, "\n");

	for (todo = 0; todo < ntodos; todo++) {
		if (todo_num[todo] < 0) {
			sprintf(name, "%s", todo_name[todo]);
		} else {
			sprintf(name, "%s0", todo_name[todo]);
		}
		if (io_get(name) & 1) {
			if (0 <= todo_num[todo]) {
				fprintf(fp, "/*forward*/ static void\n");
				fprintf(fp, "%s_outN_set(struct cpssp *cpssp, int n, unsigned int val);\n", todo_name[todo]);
			} else {
				fprintf(fp, "/*forward*/ static void\n");
				fprintf(fp, "%s_out_set(struct cpssp *cpssp, unsigned int val);\n", todo_name[todo]);
			}
		}
	}

	fprintf(fp, "\n");

	fprintf(fp, "/*forward*/ static void\n");
	fprintf(fp, "NAME_(create)(struct cpssp *cpssp);\n");
	fprintf(fp, "/*forward*/ static void\n");
	fprintf(fp, "NAME_(destroy)(struct cpssp *cpssp);\n");
	fprintf(fp, "\n");

	fprintf(fp, "#endif /* EXPORT */\n");
	fprintf(fp, "#ifdef BEHAVIOR\n");
	fprintf(fp, "\n");

	for (sig_first(&c, &pin, &alt);
	    c != -1 && pin != -1 && alt != -1;
	    sig_next(c, pin, alt, &c, &pin, &alt)) {
		int io;
		char c1;
		int pin1;
		int alt1;

		io = 0;
		for (n = 0; n < 4; n++) {
			ent = lookup(c, pin, alt, n);
			if (! ent) break;
			io |= io_get(ent);
		}

		/* Comment */
		fprintf(fp, "/*\n");
		fprintf(fp, " * pt%c%d/%d\n", c, pin, alt);
		for (sig_other_first(c, pin, alt, &c1, &pin1, &alt1);
		    c1 != -1 && pin1 != -1 && alt1 != -1;
		    sig_other_next(c1, pin1, alt1, &c1, &pin1, &alt1)) {
			fprintf(fp, " * pt%c%d/%d\n", c1, pin1, alt1);
		}
		switch (io) {
		case 1: fprintf(fp, " * (out)\n"); break;
		case 2: fprintf(fp, " * (in)\n"); break;
		case 3: fprintf(fp, " * (inout)\n"); break;
		default: assert(0); /* Mustn't happen. */
		}
		fprintf(fp, " */\n");

		if (strcmp(method, "inout") == 0) {
			/* *_resolve function. */
			fprintf(fp, "static unsigned int\n");
			fprintf(fp, "NAME_(%c%d_%d_resolve)(struct cpssp *cpssp)\n",
					c, pin, alt);
			fprintf(fp, "{\n");
			fprintf(fp, "\tunsigned int val = SIG_STD_LOGIC_Z;\n");
			fprintf(fp, "\n");
			for (n = 0; n < 4; n++) {
				ent = lookup(c, pin, alt, n);
				if (! ent) break;

				if (io_get(ent) & 1) {
					fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%s);\n", ent);
				}
			}
			fprintf(fp, "\n");
			fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%c_ext[%d][%d]);\n", c, pin, alt);
			for (sig_other_first(c, pin, alt, &c1, &pin1, &alt1);
			    c1 != -1 && pin1 != -1 && alt1 != -1;
			    sig_other_next(c1, pin1, alt1, &c1, &pin1, &alt1)) {
				fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%c_ext[%d][%d]);\n", c1, pin1, alt1);
			}
			fprintf(fp, "\n");
			fprintf(fp, "\treturn val;\n");
			fprintf(fp, "}\n");

		} else {
			/* *_resolve functions. */
			if (io & 1) {
				/* Out */
				fprintf(fp, "static unsigned int\n");
				fprintf(fp, "NAME_(%c%d_%d_resolve_int)(struct cpssp *cpssp)\n",
						c, pin, alt);
				fprintf(fp, "{\n");
				fprintf(fp, "\tunsigned int val = SIG_STD_LOGIC_Z;\n");
				fprintf(fp, "\n");
				for (n = 0; n < 4; n++) {
					ent = lookup(c, pin, alt, n);
					if (! ent) break;

					if (io_get(ent) & 1) {
						fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%s);\n", ent);
					}
				}
				fprintf(fp, "\n");
				fprintf(fp, "\treturn val;\n");
				fprintf(fp, "}\n");

				fprintf(fp, "\n");
			}

			fprintf(fp, "static unsigned int\n");
			fprintf(fp, "NAME_(%c%d_%d_resolve_ext)(struct cpssp *cpssp)\n",
					c, pin, alt);
			fprintf(fp, "{\n");
			fprintf(fp, "\tunsigned int val = SIG_STD_LOGIC_Z;\n");
			fprintf(fp, "\n");
			fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%c_ext[%d][%d]);\n", c, pin, alt);
			for (sig_other_first(c, pin, alt, &c1, &pin1, &alt1);
			    c1 != -1 && pin1 != -1 && alt1 != -1;
			    sig_other_next(c1, pin1, alt1, &c1, &pin1, &alt1)) {
				fprintf(fp, "\tval = sig_std_logic_resolve(val, cpssp->NAME.%c_ext[%d][%d]);\n", c1, pin1, alt1);
			}
			fprintf(fp, "\n");
			fprintf(fp, "\treturn val;\n");
			fprintf(fp, "}\n");
		}

		fprintf(fp, "\n");

		/*
		 * *_int_out_set function.
		 */
		fprintf(fp, "static void\n");
		fprintf(fp, "NAME_(%c%d_%d_int_out_set)(struct cpssp *cpssp, unsigned int val)\n",
				c, pin, alt);
		fprintf(fp, "{\n");
		for (n = 0; n < 4; n++) {
			ent = lookup(c, pin, alt, n);
			if (! ent) break;

			if (io_get(ent) & 2) {
				split(ent, name, &num);
				if (num == -1) {
					fprintf(fp, "\t%s_in_set(cpssp, val);\n", name);
				} else {
					fprintf(fp, "\t%s_inN_set(cpssp, %d, val);\n", name, num);
				}
			}
		}
		fprintf(fp, "}\n");

		fprintf(fp, "\n");

		if (io & 1) {
			/*
			 * *_ext_out_set function.
			 */
			fprintf(fp, "static void\n");
			fprintf(fp, "NAME_(%c%d_%d_ext_out_set)(struct cpssp *cpssp, unsigned int val)\n",
					c, pin, alt);
			fprintf(fp, "{\n");
			fprintf(fp, "\tNAME_(port%cNM_out_set)(cpssp, %d, %d, val);\n", c, alt, pin);
			for (sig_other_first(c, pin, alt, &c1, &pin1, &alt1);
			    c1 != -1 && pin1 != -1 && alt1 != -1;
			    sig_other_next(c1, pin1, alt1, &c1, &pin1, &alt1)) {
				fprintf(fp, "\tNAME_(port%cNM_out_set)(cpssp, %d, %d, val);\n", c1, alt1, pin1);
			}
			fprintf(fp, "}\n");

			fprintf(fp, "\n");
		}

		if (io & 1) {
			/*
			 * *_int_in_set function.
			 */
			fprintf(fp, "static void\n");
			fprintf(fp, "NAME_(%c%d_%d_int_in_set)(struct cpssp *cpssp)\n",
					c, pin, alt);
			fprintf(fp, "{\n");
			fprintf(fp, "\tunsigned int val;\n");
			fprintf(fp, "\n");
			if (strcmp(method, "inout") == 0) {
				fprintf(fp, "\tval = NAME_(%c%d_%d_resolve)(cpssp);\n", c, pin, alt);
			} else {
				fprintf(fp, "\tval = NAME_(%c%d_%d_resolve_int)(cpssp);\n", c, pin, alt);
			}
			fprintf(fp, "\n");
			if (strcmp(method, "inout") == 0) {
				fprintf(fp, "\tNAME_(%c%d_%d_int_out_set)(cpssp, val);\n", c, pin, alt);
			}
			fprintf(fp, "\tNAME_(%c%d_%d_ext_out_set)(cpssp, val);\n", c, pin, alt);
			fprintf(fp, "}\n");

			fprintf(fp, "\n");
		}

		/*
		 * *_ext_in_set function.
		 */
		fprintf(fp, "static void\n");
		fprintf(fp, "NAME_(%c%d_%d_ext_in_set)(struct cpssp *cpssp)\n",
				c, pin, alt);
		fprintf(fp, "{\n");
		fprintf(fp, "\tunsigned int val;\n");
		fprintf(fp, "\n");
		if (strcmp(method, "inout") == 0) {
			fprintf(fp, "\tval = NAME_(%c%d_%d_resolve)(cpssp);\n", c, pin, alt);
		} else {
			fprintf(fp, "\tval = NAME_(%c%d_%d_resolve_ext)(cpssp);\n", c, pin, alt);
		}
		fprintf(fp, "\n");
		fprintf(fp, "\tNAME_(%c%d_%d_int_out_set)(cpssp, val);\n", c, pin, alt);
		fprintf(fp, "}\n");

		fprintf(fp, "\n");
	}

	/*
	 * Create flow pins -> components.
	 */
	for (c = 'a'; c <= 'z'; c++) {
		int have;
		char c1;
		int pin1;
		int alt1;

		have = 0;
		for (pin = 0; pin < npins; pin++) {
			if (port_row(c, pin) != -1) {
				have = 1;
				break;
			}
		}
		if (! have) {
			continue;
		}

		fprintf(fp, "static void\n");
		fprintf(fp, "NAME_(port%cNM_in_set)(struct cpssp *cpssp, int alt, int pin, unsigned int val)\n", c);
		fprintf(fp, "{\n");
		fprintf(fp, "\tstatic void (*cb[%d][%d])(struct cpssp *) = {\n",
				npins, nalts);
		for (pin = 0; pin < npins; pin++) {
			fprintf(fp, "\t\t{");
			for (alt = 0; alt < nalts; alt++) {
				if (! lookup(c, pin, alt, 0)) {
					fprintf(fp, "NULL");
				} else {
					for (sig_first(&c1, &pin1, &alt1);
					    ;
					    sig_next(c1, pin1, alt1, &c1, &pin1, &alt1)) {
						assert(c1 != -1 && pin1 != -1 && alt1 != -1);
						if (sig_same(c, pin, alt, c1, pin1, alt1)) {
							fprintf(fp, "NAME_(%c%d_%d_ext_in_set)", c1, pin1, alt1);
							break;
						}
					}
				}
				fprintf(fp, ",");
			}
			fprintf(fp, "},\n");
		}
		fprintf(fp, "\t};\n");
		fprintf(fp, "\n");
		fprintf(fp, "\tcpssp->NAME.%c_ext[pin][alt] = val;\n", c);
		fprintf(fp, "\n");
		fprintf(fp, "\tif (cb[pin][alt]) {\n");
		fprintf(fp, "\t\t(*cb[pin][alt])(cpssp);\n");
		fprintf(fp, "\t}\n");
		fprintf(fp, "}\n");

		fprintf(fp, "\n");
	}

	/*
	 * Create flow components -> pins.
	 */
	for (todo = 0; todo < ntodos; todo++) {
		if (todo_num[todo] < 0) {
			sprintf(name, "%s", todo_name[todo]);
		} else {
			sprintf(name, "%s0", todo_name[todo]);
		}
		if (io_get(name) & 1) {
			write_comp_pin(fp, todo_name[todo], todo_num[todo]);
		}
	}

	/*
	 * *_create function.
	 */
	fprintf(fp, "static void\n");
	fprintf(fp, "NAME_(create)(struct cpssp *cpssp)\n");
	fprintf(fp, "{\n");

	for (todo = 0; todo < ntodos; todo++) {
		if (todo_num[todo] < 0) {
			sprintf(name, "%s", todo_name[todo]);
		} else {
			sprintf(name, "%s0", todo_name[todo]);
		}
		if (io_get(name) & 1) {
			if (todo_num[todo] < 0) {
				fprintf(fp, "\tcpssp->NAME.%s = SIG_STD_LOGIC_Z;\n", todo_name[todo]);
			} else {
				for (pin = 0; pin < 64; pin++) {
					if (have_pin(todo_name[todo], pin)) {
						fprintf(fp, "\tcpssp->NAME.%s%d = SIG_STD_LOGIC_Z;\n", todo_name[todo], pin);
					}
				}
			}
		}
	}
	for (c = 'a'; c < 'a' + nports; c++) {
		for (pin = 0; pin < npins; pin++) {
			for (alt = 0; alt < nalts; alt++) {
				fprintf(fp, "\tcpssp->NAME.%c_ext[%d][%d] = SIG_STD_LOGIC_Z;\n", c, pin, alt);
			}
		}
	}

	fprintf(fp, "}\n");

	fprintf(fp, "\n");

	/*
	 * *_destroy function.
	 */
	fprintf(fp, "static void\n");
	fprintf(fp, "NAME_(destroy)(struct cpssp *cpssp)\n");
	fprintf(fp, "{\n");
	fprintf(fp, "}\n");

	fprintf(fp, "\n");
	fprintf(fp, "#endif /* BEHAVIOR */\n");

	fclose(fp);
}

static void __attribute__((__noreturn__))
usage(int retval)
{
	fprintf(stderr, "Usage: %s [-c] <chip> <inout> <matrix> <method> <dump> <src> <c-source>\n", progname);
	exit(retval);
}

int
main(int argc, char **argv)
{
	int c;

	progname = argv[0];

	/* Get options. */
	while ((c = getopt(argc, argv, "v")) != -1) {
		switch (c) {
		case 'v':
			opt_v = 1;
			break;
		default:
			usage(1);
			/*NOTREACHED*/
		}
	}

	argc -= optind;
	argv += optind;

	/* Get parameter. */
	if (0 < argc) {
		chip = *argv;
		if (strcmp(chip, "QFN32") == 0) {
			chip = "32-QFN";
		} else if (strcmp(chip, "LQFP100") == 0) {
			chip = "100-LQFP";
		}
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (0 < argc) {
		read_inout(*argv);
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (0 < argc) {
		read_matrix(*argv);
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (0 < argc) {
		method = *argv;
		argc--;
		argv++;
	} else {
		usage(1);
	}

	check_matrix();

	if (0 < argc) {
		write_table(*argv);
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (0 < argc) {
		write_src(*argv);
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (0 < argc) {
		write_matrix(*argv);
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (argc != 0) {
		usage(1);
	}

	return 0;
}
