/*
 *		Perkin-Elmer UNIX Configuration Utility
 *
 *
 *		(C) 1980, Richard Miller
 *		    University of Wollongong
 *		Mod 1982, Ross Nealon.
 *		    Mods provide more information for device
 *		    drivers, enabling them to perform for
 *		    arbitrary device configurations.
 */

#include <stdio.h>
#include <ctype.h>

#include "../h/ccb.h"

/*
 * Table describing device characteristics
 */
#define CDEV		01	/* is a character device */
#define BDEV		02	/* is a block device */
#define DEVAD		04	/* device addresses required */
#define CNTLAD		010	/* controller address required */
#define SELCHAD		020	/* selector channel address required */
#define SIZE		040	/* size specification required */
#define MAP		0100	/* logical device map required */
#define SPEEDS		0200	/* baud rate table required */
#define CONSDEV		0400	/* can be console device */
#define FSDEV		01000	/* can be file system device */
#define	DREQ		02000	/* must be defined */
#define	INF		04000	/* output inf structures for driver */
#define	ISDEF		010000	/* is defined */
#define	UNIT		020000	/* unit spec allowed */
#define	ACD		040000	/* Auto Channl Driver allowed */

char	null[]	= "nulldev";	/* name of no-op fill-in routine */
char	no[]	= "nodev";	/* name of error fill-in routine */
char	zero[]	= "0";		/* null fill-in entry */

struct	dinf	{
	int	d_nidev;	/* devices in this inf struct */
	int	*d_dev;		/* list of device addresses */
	int	d_cntl;		/* controller address */
	int	d_selch;	/* channel address */
	int	d_size;		/* capacity for discs (ordinal) */
	int	*d_map;		/* logical device map for discs */
	struct	dinf	*d_next;
	};

struct devconf {
			/* Static data */
	char	*d_name;	/* external name */
	int	d_flags;	/* device attributes - see #defines above */
	int	d_maxdev;	/* maximum number of devices permitted
					(or 0 for no limit) */
	int	d_nccb;		/* number of channel control blocks per dev */
	int	d_unit;		/* number of unit structures
					(or 0 for no output) */
	char	*d_bsw[5];	/* names of routines for bdevsw
					(if block device) */
	char	*d_csw[7];	/* names of routines for cdevsw
					(if character device) */
	char	*d_int[2];	/* names of interrupt handlers for even & odd
					address (character device), or for
					device & controller (block device) */
	char	*d_ust[2];	/* names of structures for unit number output */

			/* Input data */
	int	d_major;	/* major device number (if both BDEV and CDEV,
					this is block device number) */
	int	d_defined;	/* d_flags bits which have been defined */
	int	d_acd;		/* Auto Channel Driver use. */
	int	d_ndev;		/* number of devices to be configured */
	struct	dinf	d_i;	/* info structure */
	int	d_nspeeds;	/* speeds table for vdu type devs */
	int	*d_speeds;
	}	devconf[]	=	{

/* Comms Mux, PALS or PASLA */

	{	"vdu", DREQ+CDEV+DEVAD+SPEEDS+CONSDEV+ACD, 0, 1, 0,
		{ },
		{ "vduopen", "vduclose", "vduread", "vduwrite", "vduioctl",
			"vdustop", "vdu" },
		{ "vdurint", "vduxint" },
		{ },
	},

/* Memory pseudo device */

	{	"mem", CDEV, 0, 0, 0,
		{ },
		{ "mmopen", "mmclose", "mmread", "mmwrite", "mmioctl",
			null, zero },
		{ },
		{ },
	},

/* Cartridge disk - 2.5 or 10 Megabyte */

	{	"dsk", BDEV+CDEV+DEVAD+CNTLAD+SELCHAD+SIZE+FSDEV+INF, 0, 0, 0,
		{ null, null, "dskstrategy", "dsktab" },
		{ null, null, "dskread", "dskwrite", no, null, zero },
		{ "dskdintr", "dskcintr" },
		{ },
	},

/* Controlling terminal pseudo device */

	{	"tty", CDEV, 0, 0, 0,
		{ },
		{ "syopen", null, "syread", "sywrite", "sysioctl", null, zero },
		{ },
		{ },
	},

/* Magnetic tape - 800 or 1600 bpi */

	{	"mt", BDEV+CDEV+DEVAD+SELCHAD, 0, 0, 0,
		{ "mtopen", "mtclose", "mtstrategy", "mttab" },
		{ "mtopen", "mtclose", "mtread", "mtwrite",
			"mtioctl", null, zero },
		{ "mtintr" },
		{ },
	},

/* Current loop interface */

	{	"cli", CDEV+DEVAD, 0, 0, 0,
		{ },
		{ "cliopen", "cliclose", "cliread", "cliwrite",
			"cliioctl", null, "cli" },
		{ "cliint" },
		{ },
	},

/* Line printer */

	{	"lp", CDEV+DEVAD, 1, 0, 0,
		{ },
		{ "lpopen", "lpclose", no, "lpwrite", "lpioctl", null, zero },
		{ "lpint" },
		{ },
	},

/* MSM disk - 67 or 256 megabyte */

	{	"msm", CDEV+BDEV+DEVAD+CNTLAD+SELCHAD+SIZE+MAP+FSDEV+INF, 0, 0, 0,
		{ null, null, "msmstrategy", "msmtab" },
		{ null, null, "msmread", "msmwrite", no, null, zero },
		{ "msmdintr", "msmcintr" },
		{ },
	},

/* Synchronous data set adapter */

	{	"dsa", CDEV+DEVAD+ACD, 0, 1, 0,
		{ },
		{ "dsaopen", "dsaclose", "dsaread", "dsawrite", "dsaioctl",
			"dsastop", "dsa" },
		{ "dsarint", "dsaxint" },
		{ },
	},

/* mx - multiplexed file driver pseudo device */

	{	"mx", CDEV+UNIT, 0, 0, 5,
		{ },
		{ "mxopen", "mxclose", "mxread", "mxwrite",
			"mxioctl", null, zero },
		{ },
		{ "mxf", "mxf" },
	},

/* 40 megabyte disk */

	{	"d40", CDEV+BDEV+DEVAD+CNTLAD+SELCHAD+MAP+FSDEV, 0, 0, 0,
		{ null, null, "d40strategy", "d40tab" },
		{ null, null, "d40read", "d40write", no, null, zero },
		{ "d40dintr", "d40cintr" },
		{ },
	},

/* Precision clock */

	{	"clock", DREQ+DEVAD, 1, 0, 0,
		{ },
		{ },
		{ "clock" },
		{ },
	},

/* Univac U100 protocol pseudo device */

	{	"uv", CDEV+UNIT, 0, 0, 1,
		{ },
		{ "uvopen", "uvclose", "uvread", "uvwrite",
			null, null, zero },
		{ },
		{ "univac", "uv" },
	},

/* Polynet ring */

	{	"ring", CDEV+DEVAD+SELCHAD+INF+UNIT, 0, 0, 20,
		{ },
		{ "ringopen", "ringclose", "ringread", "ringwrite",
			"ringioctl", null, zero },
		{ "ringrint", "ringxint" },
		{ "tty", "ring" },
	},

/* Path protocol driver (for ring) */

	{	"pp", CDEV, 0, 0, 0,
		{ },
		{ no, no, no, no,
			no, no, no },
		{ },
		{ },
	},

/* Comms Mux, PALS or PASLA data capture */

	{	"cx", CDEV+DEVAD+SPEEDS+ACD, 0, 1, 0,
		{ },
		{ "cxopen", "cxclose", "cxread", "cxwrite", "cxioctl",
			"cxstop", "cx" },
		{ "cxrint", "cxxint" },
		{ },
	},

/* Null name marks end of table */

	{	0	},
};

/*
 * Table describing line disciplines
 */

struct	lineconf	{
	char	*l_name;		/* external name */
	int	l_include;		/* include this discipline if not 0 */
	int	l_select;		/* selectable via ioctl */
					/* N.B. All entries with select=1 must
					   precede those with select=0 */
	char	*l_lsw[10];		/* names of linesw entries */
	}	lineconf[ ]	=	{

/* Standard tty driver - always included */

	{	"tty", 1, 1,
		"ttyopen", null, "ttread", "ttwrite", no,
		"ttyinput", "ttyrend", null, "ttstart", null,
	},

/* Packet switch */

	{	"pack", 0, 1,
		"pkopen", "pkclose", "pkread", "pkwrite", "pkioctl",
		"pkrint", null, null, "pkxint", null,
	},

/* Univac U100 protocol */

	{	"uv", 0, 1,
		"uvon", "uvoff", null, null, no,
		null, "uvrend", null, "uvstart", null,
	},

/* TerMux Ring protocol */

	{	"termux", 0, 1,
		no, no, no, no, no,
		no, no, no, no, no,
	},

/* Null name marks end of table */

	{	0	},
};

/*
 * Table for special device assignments
 */
struct spdev {
	char	*s_name;		/* external name */
	int	s_flag;			/* d_flag type required */
	int	s_minor;		/* minor device number */
	struct devconf *s_dev;		/* pointer to device description */
} spdev[] = {

	"root",	FSDEV,		0, 0,
	"swap",	FSDEV,		0, 0,
	"pipe",	FSDEV,		0, 0,
	"cons",	CONSDEV,	0, 0,
	0
};

/*
 * Table for parsing main-level keywords
 */

#define K_REQ		01	/* definition required */
#define K_NUM		02	/* needs numeric parameter */
#define K_KEY		04	/* needs keyword parameter */
#define	K_LIST		010	/* needs numeric parameter list */

int	Pcpu(), Pmemory(), Pfloat(), Pdfloat();
int	Pswaplo(), Pnswap(), Pdump();
int	Pmon();

struct keyparm {
	char	*k_name;		/* external name */
	int	k_flags;		/* see #defines above */
	int	(*k_fn)();		/* routine to process keyword */
	int	k_defined;		/* has been defined if nonzero */
} keyparm[] {

	"cpu",		K_REQ+K_NUM,	Pcpu,		0,
	"memory",	K_REQ+K_NUM,	Pmemory,	0,
	"float",	K_KEY,		Pfloat,		0,
	"dfloat",	K_KEY,		Pdfloat,	0,
	"wcs",		0,		0,		0,
	"swaplo",	K_REQ+K_NUM,	Pswaplo,	0,
	"nswap",	K_REQ+K_NUM,	Pnswap,		0,
	"dump",		K_KEY,		Pdump,		0,
	"lra",		0,		0,		0,
	"pic",		0,		0,		0,
	"monitor",	K_LIST,		Pmon,		0,
	0
};

/*
 * Table for parsing device parameters
 */

int	Pdev(), Pcntl(), Pselch(), Psize(), Pmap(), Pspeeds(), Punit(), Pacd();

struct devparm {
	char	*p_name;	/* external name */
	int	p_flag;		/* d_flags value associated with this parm */
	int	(*p_fn)();	/* function to process this parm */
} devparm[] = {

	"dev",		DEVAD,		Pdev,
	"cntl",		CNTLAD,		Pcntl,
	"selch",	SELCHAD,	Pselch,
	"size",		SIZE,		Psize,
	"map",		MAP,		Pmap,
	"speeds",	SPEEDS,		Pspeeds,
	"unit",		UNIT,		Punit,
	"acd",		ACD,		Pacd,
	0
};

/*
 * Preprocessor #include files for c.c
 */
char	*includes[] = {
	"../h/param.h",
	"../h/systm.h",
	"../h/buf.h",
	"../h/tty.h",
	"../h/conf.h",
	"../h/proc.h",
	"../h/dir.h",
	"../h/user.h",
	"../h/text.h",
	"../h/file.h",
	"../h/inode.h",
	"../h/acct.h",
	"../h/ccb.h",
	"../h/selch.h",
	"../h/mx.h",
	"../h/uv.h",
	"../h/ring.h",
	"../h/inf.h",
	0
};

/*
 * Other declarations for c.c
 */
char	*junk[] = {
	"buf\tbuf[NBUF]",
	"file\tfile[NFILE]",
	"inode\tinode[NINODE]",
	"proc\tproc[NPROC]",
	"text\ttext[NTEXT]",
	"buf\tbfreelist",
	"acct\tacctbuf",
	"inode\t*acctp",
	0
};

/*
 * Configuration data from input
 */
int	cpu;			/* cpu model number */
int	memory;			/* maximum memory */
int	singlefloat;		/* single precision floating point:
					0=none, 1=hardware, 2=software */
int	doublefloat;		/* double precision floating point: as above */
int	swaplo;			/* origin of swap area */
int	nswap;			/* length of swap area */
char	*dumpsw;		/* dump routine */
int	nccb;			/* number of auto-driver 'channel' cntl blks */
int	mx;			/* flag to include mx driver */
int	*monitor;		/* monitored devices */
int	nmon;			/* number of monitored devices */

char	devmap[256];		/* map device address => minor device number */
char	devint[256];		/* map device address => interrupt table offset */
#define	MAXHANDLER	64	/* maximum number of interrupt routines */
#define SELCHOFFS	4	/* offset of built-in selch entry in table */
char	*handler[MAXHANDLER] = {	/* names of interrupt routines */
	"spurint",			/* offset 0 is unexpected interrupt */
	"selchint",			/* selector channel always included */
};

int	nhandler = 2;		/* number of interrupt routines */
short	selchaddr[16];		/* selector channel addresses */
int	nselch;			/* number of selector channels */
short	cntladdr[32];		/* device controller addresses */
int	ncntl;			/* controllers in system */
int	ndevs;			/* counter of devs under inf control */


/*
 * Files
 */
char	*configfile;			/* input file name */
char	*cfile = "c.c";			/* C output file name */
char	*sfile = "param.s";		/* as output file name */

/*
 * Input parsing information
 */
enum token_t {		/* types of input token */
	ENDFILE,
	KEYWORD,
	NUMERIC
};
enum token_t	token_type;		/* type of next token on input file */
int	token_value;		/* value of token if numeric */
char	token_key[64];		/* value of token if keyword */
int	linenumber = 0;			/* current input line number */


/*
 * Shorthand macros
 */
#define	match(a)	!strcmp(a, token_key)

/*
 *	mkconf [ -c cfile ] [ -s sfile ] [ config ]
 *
 * Read configuration description in config file (default stdin).
 * Produce cfile (default "c.c") containing device driver information, and
 * sfile (default "param.s") containing conditional assembly parameters.
 */

main(argc, argv)
char **argv;
{
	/*
	 * Obey arguments
	 */
	while (--argc > 0) {
		argv++;
		if (argv[0][0] == '-') switch (argv[0][1]) {
			case 'c':	/* change cfile */
				if (--argc <= 0)
					error("Missing cfile name");
				cfile = *++argv;
				break;

			case 's':	/* change sfile */
				if (--argc <= 0)
					error("Missing sfile name");
				sfile = *++argv;
				break;

			default:
				error("Unknown argument %s", *argv);
				break;
		} else configfile = *argv;
	}
	/*
	 * Read config file and store data in internal form
	 */
	if (configfile && freopen(configfile, "r", stdin) == NULL)
		error("Can't open %s", configfile);
	linenumber = 1;		/* start line numbering for error messages */
	token();
	while (getconfig())
		;
	linenumber = 0;		/* turn off line numbers for error messages */

	/*
	 * Check validity of data and compute remaining parameters
	 */
	mkconf();

	/*
	 * Make C file
	 */
	if (freopen(cfile, "w", stdout) == NULL)
		error("Can't write %s", cfile);
	putcfile();

	/*
	 * Make as file
	 */
	if (freopen(sfile, "w", stdout) == NULL)
		error("Can't write %s", sfile);
	putsfile();
	exit(0);
}
/*
 * Make "c.c" file
 *
 * Contains	#includes
 *		bdevsw, cdevsw and linesw tables
 *		device assignments for root, swap, pipes
 *		other configuration variables
 *		device driver configuration information
 */
putcfile()
{
	register struct devconf		*dp;
	register struct lineconf	*lp;
	register struct dinf		*ip;
	register int			major, i;
	register int			nldisp = 0;

	/*
	 * #includes
	 */
	{	register char	**pp;

		for (pp = includes; *pp; pp++)
			printf("#include \"%s\"\n", *pp);
	}
	/*
	 * Declarations for block switch routines
	 */
	printf("\nint	%s(), %s();\n", no, null);
	for (dp = devconf; dp->d_name; dp++) {
		if ((dp->d_flags&BDEV) && dp->d_ndev > 0) {
			register char *sep = "int\t";

			for (i = 0; i < 3; i++)
				if (dp->d_bsw[i] != no && dp->d_bsw[i] != null) {
					printf("%s%s()", sep, dp->d_bsw[i]);
					sep = ", ";
					}

			printf(";\n");
			if (dp->d_bsw[3] != zero)
				printf("struct buf	%s;\n", dp->d_bsw[3]);
		}
	}
	/*
	 * bdevsw
	 */
	printf("\nstruct bdevsw	bdevsw[] = {\n");
	major = 0;
	for (dp = devconf; dp->d_name; dp++) if (dp->d_flags&BDEV) {
		if (dp->d_ndev > 0) {
			printf("\t%s, %s, %s, ",
				dp->d_bsw[0], dp->d_bsw[1], dp->d_bsw[2]);
			if (dp->d_bsw[3] == zero)
				printf("0,");
			else
				printf("&%s,", dp->d_bsw[3]);
		} else
			printf("\tnodev, nodev, nodev, 0,");
		printf("\t/* %s = %d */\n", dp->d_name, major);
		dp->d_major = major;
		major++;
	}
	printf("\t0\n};\n\n");
	/*
	 * declarations for char switch routines
	 */
	for (dp = devconf; dp->d_name; dp++) {
		if ((dp->d_flags&CDEV) && dp->d_ndev > 0) {
			register char *sep = "int\t";

			for (i = 0; i < 6; i++)
				if (dp->d_csw[i] != no && dp->d_csw[i] != null) {
					printf("%s%s()", sep, dp->d_csw[i]);
					sep = ", ";
				}

			printf(";\n");
			if (dp->d_csw[6] != zero)
				printf("struct tty	%s[%d];\n",
					dp->d_csw[6], dp->d_ndev);
		}
	}
	/*
	 * cdevsw
	 */
	printf("\nstruct cdevsw	cdevsw[] = {\n");
	major = 0;
	for (dp = devconf; dp->d_name; dp++) if (dp->d_flags&CDEV) {
		if (dp->d_ndev > 0) {
			printf("\t%s, %s, %s, %s, %s, %s, %s,",
				dp->d_csw[0], dp->d_csw[1], dp->d_csw[2],
				dp->d_csw[3], dp->d_csw[4], dp->d_csw[5],
				dp->d_csw[6]);
		} else
			printf("\tnodev, nodev, nodev, nodev, nodev, nodev, 0,");
		printf("\t/* %s = %d */\n", dp->d_name, major);
		if (!(dp->d_flags & BDEV))
			dp->d_major = major;
		major++;
		if (!strcmp(dp->d_name, "mx")  &&  dp->d_flags & ISDEF)
			/* allow proper loading of mx driver */
			mx = 1;
	}
	printf("\t0\n};\n\n");
	/*
	 * Definitions for linesw
	 */
	for (lp = lineconf; lp->l_name; lp++) {
		if (lp->l_include) {
			register char	*sep = "int\t";
			for (i = 0; i < 10; i++)
				if (lp->l_lsw[i] != no && lp->l_lsw[i] != null) {
					printf("%s%s()", sep, lp->l_lsw[i]);
				sep = ", ";
			}
			printf(";\n");
		}
	}
	/*
	 * linesw
	 */
	printf("\nstruct linesw	linesw[] = {\n");
	major = 0;
	for (lp = lineconf; lp->l_name; lp++) {
		if (lp->l_include) {
			for (i = 0; i < 10; i++) {
				printf(i==0? "\t": i==5? ",\n\t\t": ", ");
				printf("%s", lp->l_lsw[i]);
			}
			printf(",\t/* %s = %d */\n", lp->l_name, major);
			if (lp->l_select)
				nldisp++;
			major++;
		}
	}
	printf("\t0\n};\n\nint	nldisp = %d;\n", nldisp);

	/*
	 * Substructure information
	 */
	for (dp = devconf;  dp->d_name; dp++)  {
		if (!(dp->d_flags & ISDEF))
			continue;

		if (dp->d_unit)  {
			printf("int\t%sunit\t=\t%d;\n", dp->d_name, dp->d_unit);
			if (dp->d_ust[0])
				printf("struct\t%s\t%s[%d];\n", dp->d_ust[0],
								dp->d_ust[1],
								dp->d_unit);
		}

		if (dp->d_nccb)
			printf("int\t%sacd\t=\t%d;\n", dp->d_name,
						       dp->d_acd);
	}
	if (!mx)  {
		/* prevent loading of mx driver */
		printf("mxsys()\n{\n\tu.u_error = EINVAL;\n}\n");
		}
	printf("\n");

	/*
	 * Special device assignments
	 */
	{	register struct spdev	*sp;

		for (sp = spdev; sp->s_name; sp++)
			printf("dev_t\t%sdev = makedev(%d, %d);\n",
				sp->s_name, sp->s_dev->d_major, sp->s_minor);
	}
	/*
	 * Assorted configuration variables
	 */
	printf("int\tM_click\t=\t%d;\n", (is3240(cpu)  ?  2048 : 256));
	printf("int\tM_shift\t=\t%d;\n", (is3240(cpu)  ?    11 :   8));
	printf("daddr_t\tswplo\t= %d;\nint\tnswap\t= %d;\n", swaplo, nswap);
	printf("caddr_t\tmemtop\t= %d;\n", memory);
	printf("\nint\t%s();\nint\t(*dumpsw)() = %s;\n\n", dumpsw, dumpsw);
	{	register char	**pp;

		for (pp = junk; *pp; pp++)
			printf("struct\t%s;\n", *pp);
	}
	/*
	 *   Channel busy table and raw I/O buffers
	 */
	printf("int\tnselch\t=\t%d;\n", nselch);
	if (nselch)  {
		printf("struct\tselchtab\tselchtab[%d];\n", nselch);
		}

	printf("int\tncntl\t=\t%d;\n", ncntl);
	if (ncntl)  {
		printf("struct\tbuf\trawbuf[%d];\n", ncntl);
		printf("struct\tcntltab\tcntltab[%d];\n", ncntl);
		}

	/*
	 * Driver configuration data
	 */
	for (dp = devconf; dp->d_name; dp++)
		if (dp->d_ndev > 0)
			if (dp->d_flags & INF)
				devinf(dp);
			else
				devout(dp);

	/*
	 *   Time out control
	 *	Must be output after devconf stuff.
	 */
	printf("int\tndevs\t=\t%d;\n", ndevs);
	printf("struct\tiotimer\tiotimer[%d];\n", ndevs);

	/*
	 * devmap
	 */
	printf("\nchar\tdevmap[] = {\n");
	for (i = 0; i < 256; i++)
		printf("%s%2d,", (i&017)? " ": "\n\t", devmap[i]);
	printf("\n};\n");
	/*
	 * devint
	 */
	printf("\nchar\tdevint[] = {\n");
	for (i = 0; i < 256; i++)
		printf("%s%2d,", (i&017)? " ": "\n\t", devint[i]);
	printf("\n};\n");
	/*
	 * handler table
	 */
	{	register char	*sep = "int\t";

		for (i = 0; i < nhandler; i++) {
			printf("%s%s()", sep, handler[i]);
			if (i%6 == 5)
				sep = ";\nint\t";
			else
				sep = ", ";
		}
	}
	printf(";\n\nint\t(*handler[])() = {\n");
	for (i = 0; i < nhandler; i++)
		printf("\t%s,\n", handler[i]);
	printf("};\n");
}

devinf(dp)
register struct devconf *dp;
{
	register struct dinf *ip;
	register int i;
	register int devno = 0;

	printdevs(dp);
	printf("struct\tinf\t%sinf[]\t=\t{\n", dp->d_name);
	for (ip = dp->d_i.d_next;  ip;  ip = ip->d_next)
		for (i = 0;  i < ip->d_nidev;  i++)  {
			printf("  /* device #%d */\t{\n", devno++);
			printf("\t0x%02x,  0x%02x,  %d,  0x%02x,  %d,\n",
				ip->d_dev[i],
				ip->d_cntl,  devmap[ip->d_cntl],
				ip->d_selch, devmap[ip->d_selch]);
			printf("\t0, 0, 0, 0, 0,\n");
			printf("\t-1, DNACT, DIR_IN, %d, %d,\n", 
				ckmon(ip->d_dev[i]), ndevs++);
			printf("\t%d, 0, 0, 0,\n", ip->d_size);
			printf("\t0, 0, 0, 0, 0, 0, 0,");
			printmap(dp, ip);
			printf("\n\t0, 0, 0, 0, 0, { },");
			printf("\n\t},\n");
			}

	printf("\n\t};\n");

	/* standard info */
	if (dp->d_nccb)
		printccb(dp);
	if (dp->d_flags & SPEEDS)
		printspeeds(dp);
}

devout(dp)
register struct devconf *dp;
{
	register int i;

	if (dp->d_flags & DEVAD) {
		printdevs(dp);
		printf("char\t%saddr[] = {", dp->d_name);
		for (i = 0; i < dp->d_ndev; i++)
			printf("%s0x%02x,", i&07 ? " " : "\n\t",
				dp->d_i.d_dev[i]);
		printf("\n};\n");
	}
	if (dp->d_flags & CNTLAD)
		printf("int\t%scntl = 0x%02x;\n", dp->d_name, dp->d_i.d_cntl);
	if (dp->d_flags & SELCHAD)
		printf("int\t%sselch = 0x%02x;\n", dp->d_name, dp->d_i.d_selch);
	if (dp->d_nccb)
		printccb(dp);
	if (dp->d_flags & SIZE)
		printf("int\t%ssize = %d;\n", dp->d_name, dp->d_i.d_size);
	if (dp->d_flags & MAP) {
		printf("int\t%smap[] = {", dp->d_name);
		printmap(dp, &dp->d_i);
		printf("\n};\n");
	}
	if (dp->d_flags & SPEEDS)
		printspeeds(dp);
}

ckmon(addr)
register addr;
{
	register int i;

	for (i=0; i<nmon; i++)
		if (addr == monitor[i])
			return(i);

	return(-1);
}

printdevs(dp)
register struct devconf *dp;
{
	if (dp->d_maxdev != 1)
		printf("int\tn%s = %d;\n", dp->d_name, dp->d_ndev);
}

printmap(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	register int i;

	if (!(ip->d_map))  {
		/* no map defined */
		for (i=0; i<16; i++)
			printf("%s%8d,", (i&01 ? "\t" : "\n\t"), 0);
		return;
		}

	/* map defined */
	for (i=0; i<16; i++)
		printf("%s%8d,", (i&01 ? "\t" : "\n\t"), ip->d_map[i]);
}

printspeeds(dp)
register struct devconf *dp;
{
	register int i;

	printf("char\t%srate[][4] = {", dp->d_name);
	for (i = 0; i < dp->d_nspeeds; i++)
		printf("%sB%d,", i&03 ? " " : "\n\t", dp->d_speeds[i]);
	printf("\n};\n");
}

printccb(dp)
register struct devconf *dp;
{
	int	factor	=	1;

	printf("struct ccb *%sccb = &ccb[%d];\n", dp->d_name, nccb);
	if ((dp->d_acd & CC_IO) == CC_IO)
		/* acd for I and O, need 1 ccb for each */
		factor = 2;
	nccb += dp->d_ndev * dp->d_nccb * factor;
}

/*
 * Find entry in keyword table
 * It had better be there!
 */
struct keyparm *
findkey(name)
register char	*name;
{
	register struct keyparm	*kp;

	for (kp = keyparm; kp->k_name; kp++)
		if (!strcmp(kp->k_name, name))
			return(kp);
	abort();
}


/*
 * Make "param.s" file
 *
 * Contains	parameters for conditional assembly of UNIX kernel routines
 */
putsfile()
{
	putparm("M3200", is3200(cpu));
	putparm("M3240", is3240(cpu));
	putparm("FPREGS", singlefloat == 1);
	putparm("DPREGS", doublefloat == 1);
	putparm("SPFPT", singlefloat == 2);
	putparm("DPFPT", doublefloat == 2);
	putparm("NCCB", nccb);
	putparm("WCS", findkey("wcs")->k_defined);
	putparm("LRA", is3200(cpu) || findkey("lra")->k_defined);
	putparm("PIC", findkey("pic")->k_defined);
}

is3200(cpu)
register int cpu;
{
	switch (cpu)  {
		case 3210:
		case 3220:
		case 3230:
		case 3240:
		case 3250:
			return(1);

		default:
			return(0);
		}
}

is3240(cpu)
register int cpu;
{
	switch (cpu)  {
		case 3210:
		case 3230:
		case 3240:
		case 3250:
			return(1);

		default:
			return(0);
		}
}

/*
 * Equate assembly parameter name to value
 */
putparm(name, value)
char	*name;
{
	printf("%s\tequ\t%d\n", name, value);
}

/*
 * Check validity of input data and generate internal parameters
 */
mkconf()
{
	register struct devconf	*dp;
	register struct dinf    *ip;

	/*
	 * Check that all required main keywords are defined
	 */
	{	register struct keyparm	*kp;

		for (kp = keyparm; kp->k_name; kp++)
			if ((kp->k_flags & K_REQ) && !kp->k_defined)
				error("%s not specified", kp->k_name);
	}

	/*
	 * Check that special device assignments have been defined
	 */
	{	register struct spdev	*sp;

		for (sp = spdev; sp->s_name; sp++)
			if (sp->s_dev == 0)
				error("%s device not specified", sp->s_name);
	}

	/*
	 * Device processing
	 */
	for (dp = devconf; dp->d_name; dp++) {
		register struct devparm	*pp;

		/*
		 * Check that required devices & parameters are defined
		 *   UNIT specifier is optional.
		 */
		dp->d_flags &= ~(UNIT | ACD);
		if (dp->d_ndev == 0) {
			if (dp->d_flags & DREQ)
				error("%s not defined", dp->d_name);
			continue;
		}
		for (pp = devparm; pp->p_name; pp++)
			if ((dp->d_flags & pp->p_flag)
			    && ! (dp->d_defined & pp->p_flag))
				error("%s not specified for %s", pp->p_name,
					dp->d_name);
		/*
		 * Remember interrupt information
		 */
		if (dp->d_flags & DEVAD) {
			register int	minordev = 0;
			register int	i;
			register char	*int0, *int1;

			int0 = mkhandler(dp->d_int[0]);
			if (dp->d_int[1])
				int1 = mkhandler(dp->d_int[1]);
			else
				int1 = 0;

			/* scan each information structure */
			if (dp->d_flags & INF)
				/* use inf list */
				ip = dp->d_i.d_next;
			else
				/* use default struct */
				ip = &dp->d_i;

			while (ip)  {
				for (i = 0; i < ip->d_nidev; i++) {
					register int	dev = ip->d_dev[i];
	
					mkintvec(dev, int0);
					devmap[dev] = minordev;
					if ((dp->d_flags&(CDEV|BDEV)) == CDEV && int1) {
						if (dev & 01)
							error("%s addresses must be even",
								dp->d_name);
						mkintvec(dev + 1, int1);
						devmap[dev + 1] = minordev;
					}

					minordev++;
				}

				if (dp->d_flags & CNTLAD)
					mkcntl(ip->d_cntl, int1);
				if (dp->d_flags & SELCHAD)
					mkselch(ip->d_selch);

				ip = ip->d_next;
			}
		}
	}
}

/*
 * Add a name to the interrupt handler table
 *
 * Return offset in table (in bytes)
 */
mkhandler(name)
char	*name;
{
	handler[nhandler] = name;
	if (++nhandler > MAXHANDLER)
		error("Interrupt table overflow");
	return((nhandler-1) * sizeof(*handler));
}

/*
 * Add an address to the selch address table (if not already present)
 * Also record 'minor device number' and interrupt handler for selch address
 */
mkselch(address)
register int	address;
{
	register int	i;

	for (i = 0; i < nselch; i++)
		if (selchaddr[i] == address)
			return;

	selchaddr[nselch] = address;
	devmap[address] = nselch;
	devint[address] = SELCHOFFS;
	nselch++;
}

/*
 * Add controller address to controller table if not already there.
 * Also record a pseudo minor device number.
 */
mkcntl(address, intv)
register int address;
register char *intv;
{
	register int i;

	mkintvec(address, intv);
	for (i = 0; i < ncntl; i++)
		if (cntladdr[i] == address)
			/* already marked */
			return;

	cntladdr[ncntl] = address;
	devmap[address] = ncntl;
	ncntl++;
}

/*
 * Put interrupt handler table offset into interrupt table for given device
 *
 * Check that addresses are not reassigned
 */
mkintvec(address, offset)
{
	if (devint[address] != 0)
		error("device address 0x%02x reassigned", address);
	devint[address] = offset;
}

/*
 * Get the next complete configuration entry from the input file
 *
 * Returns 0 at end of file
 */
getconfig()
{
	register struct devconf		*dp;
	register struct keyparm		*kp;
	register struct spdev		*sp;
	register struct dinf		*ip;

	/*
	 * First thing must be a keyword
	 */
	switch (token_type) {
		case ENDFILE:
			return(0);

		case NUMERIC:
			error("Extraneous numeric data");
			return(0);

		case KEYWORD:
			break;

		default:
			abort();
	}
	/*
	 * Check for device declaration
	 */
	for (dp = devconf; dp->d_name; dp++) {
		if (match(dp->d_name)) {
			token();
			dp->d_flags |= ISDEF;

			if (dp->d_flags & INF)  {
				/* allocate new info structure */
				register struct dinf *fp;

				ip = malloc(sizeof (struct dinf));
				fp = &dp->d_i;
				while (fp->d_next)
					fp = fp->d_next;

				fp->d_next = ip;
				ip->d_next = 0;
				}
			else
				/* use default inf structure */
				ip = &dp->d_i;

			while (getdevice(dp, ip))
				;
			if (!(dp->d_flags & DEVAD))
				dp->d_ndev = 1;
			/*
			 * Some device names are also line discipline names
			 * Check for a line discipline.
			 */
			getline(dp->d_name, 0);
			return(1);
		}
	}
	/*
	 * Check for line discipline declaration
	 */
	if (getline(token_key, 1)) {
		return(1);
	}
	/*
	 * Check for special device assignment
	 */
	for (sp = spdev; sp->s_name; sp++) {
		if (match(sp->s_name)) {
			token();
			if (token_type != KEYWORD)
				error("%s requires device name",
					sp->s_name);
			for (dp = devconf; dp->d_name; dp++) {
				if (match(dp->d_name)) {
					token();
					if (!(dp->d_flags & sp->s_flag))
						error("%s is wrong type of device",
							dp->d_name);
					sp->s_dev = dp;
					dp->d_flags |= DREQ;
					if (token_type != NUMERIC)
						error("missing minor device number");
					sp->s_minor = token_value;
					token();
					break;
				}
			}
			if (dp->d_name == 0)
				error("unknown device %s", token_key);
			return(1);
		}
	}
	/*
	 * Check for other main-level keywords
	 */
	for (kp = keyparm; kp->k_name; kp++) {
		if (match(kp->k_name)) {
			token();
			getkey(kp);
			kp->k_defined = 1;
			return(1);
		}
	}
	error("Unknown keyword %s", token_key);
}

/*
 * Process a single main-level non-device entry
 */
getkey(kp)
register struct keyparm	*kp;
{
	if (kp->k_flags & (K_NUM|K_LIST)) {
		if (token_type != NUMERIC)
			error("%s requires a numeric parameter",
				kp->k_name);
		(*kp->k_fn)();
		if (kp->k_flags & K_NUM)
			token();
	} else if (kp->k_flags & K_KEY) {
		if (token_type != KEYWORD)
			error("%s requires a keyword parameter",
				kp->k_name);
		(*kp->k_fn)();
		token();
	}
}

/*
 * Main keyword parameter routines
 */
Pcpu()
{
	switch (token_value) {
		case  732:
		case  832:
		case 3210:
		case 3220:
		case 3230:
		case 3240:
		case 3250:
			cpu = token_value;
			break;

		default:
			error("Unknown CPU model %d", token_value);
	}
}

Pmon()
{
	getlist(&nmon, &monitor);
	if (nmon > 4)
		error("At most 4 devices can be monitored currentely");
}

Pmemory()
{
	if (token_value < 128*1024)
		error("Sorry, not enough memory to run UNIX");
	if (token_value > 960*1024)
		error("Sorry, a maximumum of 960K memory currently supported");
	memory = token_value;
}

Pfloat()
{
	if (match("hard"))
		singlefloat = 1;
	else if (match("soft"))
		singlefloat = 2;
	else
		error("'hard' or 'soft' expected");
}

Pdfloat()
{
	if (match("hard"))
		doublefloat = 1;
	else if (match("soft"))
		doublefloat = 2;
	else
		error("'hard' or 'soft' expected");
}

Pswaplo()
{
	swaplo = token_value;
}

Pnswap()
{
	nswap = token_value;
}

Pdump()
{
	if (match("mt"))
		dumpsw = "mtdump";
	else if (match("dsk"))
		dumpsw = "dskdump";
	else if (match("msm"))
		dumpsw = "msmdump";
	else if (match("d40"))
		dumpsw = "d40dump";
	else
		error("%s not a dump device", token_key);
}

/*
 * Get next entry in input description of a device
 *
 * Check that parameter is permitted for this device type
 * Call routine to process parameter
 * Returns 0 for failure
 */
getdevice(dp, ip)
register struct devconf	*dp;
register struct dinf    *ip;
{
	register struct devparm		*pp;

	for (pp = devparm; pp->p_name; pp++) {
		if (match(pp->p_name)) {
			token();
			if (!(dp->d_flags & pp->p_flag))
				error("%s not permitted for %s", pp->p_name,
					dp->d_name);
			(*pp->p_fn)(dp, ip);
			dp->d_defined |= pp->p_flag;
			return(1);
		}
	}
	return(0);
}

/*
 * Device parameter routines
 */

Pdev(dp, ip)
register struct devconf	*dp;
register struct dinf    *ip;
{
	getlist(&ip->d_nidev, &ip->d_dev);
	dp->d_ndev += ip->d_nidev;
	if (dp->d_maxdev && dp->d_ndev > dp->d_maxdev)
		error("Too many device addresses for %s", dp->d_name);
}

Pcntl(dp, ip)
register struct devconf	*dp;
register struct dinf    *ip;
{
	ip->d_cntl = getnum();
}

Pselch(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	ip->d_selch = getnum();
}

Psize(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	register int	size;

	size = getnum();
	if (!strcmp(dp->d_name, "dsk")) {
		switch (size) {
			case 10:
			case 10*1024*1024:
				ip->d_size = 2;
				return;
			case 2:
			case 2*1024*1024:
				ip->d_size = 1;
				return;
			default:
				ip->d_size = 0;
				break;
		}
	} else if (!strcmp(dp->d_name, "msm")) {
		switch (size) {

				/* normal msm */
			case 67:
			case 67*1024*1024:
				ip->d_size = 1;
				return;
			case 256:
			case 256*1024*1024:
				ip->d_size = 2;
				return;

				/* cdd drive (on 3210) */
			case 32:
			case 32*1024*1024:
				ip->d_size = 3;
				break;
			case 64:
			case 64*1024*1024:
				ip->d_size = 4;
				break;
			case 96:
			case 96*1024*1024:
				ip->d_size = 5;
				break;

			default:
				ip->d_size = 0;
				break;
		}
	}
	error("incorrect size for %s", dp->d_name);
}

Pmap(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	int	nmap;

	getlist(&nmap, &ip->d_map);
	if (nmap != 16)
		error("%s map must have 16 entries", dp->d_name);
}

Pspeeds(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	getlist(&dp->d_nspeeds, &dp->d_speeds);
	if (dp->d_nspeeds % 4)
		error("must define 4 speeds per port");
}

Punit(dp, ip)
register struct devconf *dp;
register struct dinf	*ip;
{
	dp->d_unit = token_value;
}

Pacd(dp, ip)
register struct devconf *dp;
register struct dinf    *ip;
{
	register int acd;

	if (token_type == KEYWORD)  {
		if (match("input"))
			acd = CC_INPUT;
		else if (match("output"))
			acd = CC_OUTPUT;
		else if (match("io"))
			acd = CC_IO;
		else if (match("dma"))
			acd = CC_DMA;
		else if (match("selch"))
			acd = CC_SELCH;
		else if (match("selchin"))
			acd = CC_SELIN;
		else if (match("selchout"))
			acd = CC_SELOUT;
		else if (match("selchio"))
			acd = CC_SELIO;
		else
			acd = CC_OFF;

		token();
		}
	else
		acd = CC_OFF;

	dp->d_acd = acd;
}

/*
 * If key is name of a line discipline, mark it for inclusion
 *
 * Return 0 if not found
 */
getline(key, check)
char	*key;
int	check;
{
	register struct lineconf	*lp;

	for (lp = lineconf; lp->l_name; lp++) {
		if (!strcmp(key, lp->l_name)) {
			lp->l_include = 1;
			if (check)
				token();
			return(1);
		}
	}
	return(0);
}

/*
 * Get a list of (one or more) input numbers
 *
 * Return indirectly in *length the number of addresses read, and in *list
 * a pointer to the array of integer values read
 */
getlist(length, list)
int	*length;
int	**list;
{
	static	 int	nlist[1024];
	register int	*next;
	register int	len = 0;

	if (token_type != NUMERIC)
		error("Numeric item expected");
	while (token_type == NUMERIC) {
		if (len >= 1024)
			error("out of list space");
		nlist[len++] = token_value;
		token();
	}

	next = malloc(len * sizeof (int));
	if (next == NULL)
		error("Out of memory");

	*list = next;
	*length = len;
	for (len=0; len<(*length); len++)
		*next++ = nlist[len];
}

/*
 * Get a number from the input
 *
 * Return its value
 * Check that not more than one number is supplied
 */
getnum()
{
	register	retval;

	if (token_type != NUMERIC)
		error("Numeric item required");
	retval = token_value;
	token();
	if (token_type == NUMERIC)
		error("Only one numeric item expected");
	return(retval);
}

/*
 * Get the next input token
 *
 *	Skip white space
 *	Skip comments (from '*' to end of line)
 *	Read the next token (non-white-space string)
 *	Set token_type to:
 *		ENDFILE		- no more input
 *		NUMERIC		- integer (may be preceded by 0 or 0x for octal
 *				 or hex, followed by K or M for kilo- or mega-)
 *				- store value in token_value
 *		KEYWORD		- anything else
 *				- store keyword string in token_key
 */
token()
{
	register int	c;

	skipspace();
	if (feof(stdin) || (c = getchar()) == EOF) {	/* no more input */
		token_type = ENDFILE;
		return;
	}
	if (isdigit(c)) {	/* a number */
		register int	accum = 0, base = 10;

		if (c == '0') {
			c = getchar();
			if (c == 'x' || c == 'X') {
				c = getchar();
				base = 16;
			} else
				base = 8;
		}
		for (;;) {
			if (isdigit(c))
				c -= '0';
			else if (c >= 'a' && c <= 'f')
				c -= 'a'-10;
			else if (c >= 'A' && c <= 'F')
				c -= 'A'-10;
			else
				break;
			if (c >= base)
				error("Malformed number");
			accum *= base;
			accum += c;
			c = getchar();
		}
		if (c == 'k' || c == 'K') {
			c = getchar();
			accum *= 1024;
		} else if (c == 'm' || c == 'M') {
			c = getchar();
			accum *= 1024*1024;
		}
		ungetc(c, stdin);
		token_type = NUMERIC;
		token_value = accum;
		return;
	} else {		/* a keyword */
		register char	*p = token_key;
		register int	room = sizeof(token_key);

		while (!(isspace(c)) && c != EOF) {
			if (--room == 0)
				error("Impossibly long word");
			*p++ = c;
			c = getchar();
		}
		*p = '\0';
		token_type = KEYWORD;
		ungetc(c, stdin);
	}
}

/*
 * Skip space and comments on the input file
 * Also keep track of line numbers
 */
skipspace()
{
	register int	c;

	for (;;) {
		c = getchar();
		if (c == '*')
			while ((c = getchar()) != '\n')
				;
		if (c == '\n')
			linenumber++;
		else if (!(isspace(c)))
			break;
	}
	if (c != EOF)
		ungetc(c, stdin);
}

/*
 * Print error message and exit
 */
error(msg, a1, a2)
char *msg;
{
	if (linenumber != 0)
		fprintf(stderr, "Line %d: ", linenumber);
	fprintf(stderr, msg, a1, a2);
	fprintf(stderr, "\n");
	exit(1);
}
