/*	$OpenBSD: ncr53cxxx.c,v 1.7 2005/10/08 15:54:49 krw Exp $ */
/*	$NetBSD: ncr53cxxx.c,v 1.14 2005/02/11 06:21:22 simonb Exp $	*/

/*
 * Copyright (c) 1995,1999 Michael L. Hitch
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Michael L. Hitch.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*	ncr53cxxx.c	- SCSI SCRIPTS Assembler		*/

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

#ifndef AMIGA
#define strcmpi	strcasecmp
#endif

#define	MAXTOKENS	16
#define	MAXINST		1024
#define	MAXSYMBOLS	128

struct {
	int	type;
	char	*name;
} tokens[MAXTOKENS];
int	ntokens;
int	tokenix;

void	f_proc (void);
void	f_pass (void);
void	f_list (void);		/* ENTRY, EXTERNAL label list */
void	f_define (void);	/* ABSOLUTE, RELATIVE label list */
void	f_move (void);
void	f_jump (void);
void	f_call (void);
void	f_return (void);
void	f_int (void);
void	f_intfly (void);
void	f_select (void);
void	f_reselect (void);
void	f_wait (void);
void	f_disconnect (void);
void	f_set (void);
void	f_clear (void);
void	f_load (void);
void	f_store (void);
void	f_nop (void);
void	f_arch (void);

struct {
	char	*name;
	void	(*func)(void);
} directives[] = {
	{"PROC",	f_proc},
	{"PASS",	f_pass},
	{"ENTRY",	f_list},
	{"ABSOLUTE",	f_define},
	{"EXTERN",	f_list},
	{"EXTERNAL",	f_list},
	{"RELATIVE",	f_define},
	{"MOVE",	f_move},
	{"JUMP",	f_jump},
	{"CALL",	f_call},
	{"RETURN",	f_return},
	{"INT",		f_int},
	{"INTFLY",	f_intfly},
	{"SELECT",	f_select},
	{"RESELECT",	f_reselect},
	{"WAIT",	f_wait},
	{"DISCONNECT",	f_disconnect},
	{"SET",		f_set},
	{"CLEAR",	f_clear},
	{"LOAD",	f_load},
	{"STORE",	f_store},
	{"NOP",		f_nop},
	{"ARCH",	f_arch},
	{NULL, NULL}};

u_int32_t script[MAXINST];
int	dsps;
char	*script_name = "SCRIPT";
u_int32_t inst0, inst1, inst2;
unsigned int	ninsts;
unsigned int	npatches;

struct patchlist {
	struct patchlist *next;
	unsigned	offset;
} *patches;

#define	S_LABEL		0x0000
#define	S_ABSOLUTE	0x0001
#define	S_RELATIVE	0x0002
#define	S_EXTERNAL	0x0003
#define	F_DEFINED	0x0001
#define	F_ENTRY		0x0002
struct {
	short	type;
	short	flags;
	u_int32_t value;
	struct patchlist *patchlist;
	char	*name;
} symbols[MAXSYMBOLS];
int nsymbols;

char	*stypes[] = {"Label", "Absolute", "Relative", "External"};

char	*phases[] = {
	"data_out", "data_in", "cmd", "status",
	"res4", "res5", "msg_out", "msg_in"
};

struct ncrregs {
	char *name;
	int addr[5];
};
#define ARCH700 1
#define ARCH710 2
#define ARCH720 3
#define ARCH810 4
#define ARCH825 5

struct ncrregs 	regs[] = {
	{"scntl0",	{0x00, 0x00, 0x00, 0x00, 0x00}},
	{"scntl1",	{0x01, 0x01, 0x01, 0x01, 0x01}},
	{"sdid",	{0x02, 0x02,   -1,   -1,   -1}},
	{"sien",	{0x03, 0x03,   -1,   -1,   -1}},
	{"scid",	{0x04, 0x04,   -1,   -1,   -1}},
	{"scntl2",	{  -1,   -1, 0x02, 0x02, 0x02}},
	{"scntl3",	{  -1,   -1, 0x03, 0x03, 0x03}},
	{"scid", 	{  -1,   -1, 0x04, 0x04, 0x04}},
	{"sxfer",	{0x05, 0x05, 0x05, 0x05, 0x05}},
	{"sodl",	{0x06, 0x06,   -1,   -1,   -1}},
	{"socl",	{0x07, 0x07,   -1,   -1,   -1}},
	{"sdid",	{  -1,   -1, 0x06, 0x06, 0x06}},
	{"gpreg",	{  -1,   -1, 0x07, 0x07, 0x07}},
	{"sfbr",	{0x08, 0x08, 0x08, 0x08, 0x08}},
	{"sidl",	{0x09, 0x09,   -1,   -1,   -1}},
	{"sbdl",	{0x0a, 0x0a,   -1,   -1,   -1}},
	{"socl",	{  -1,   -1, 0x09, 0x09, 0x09}},
	{"ssid", 	{  -1,   -1, 0x0a, 0x0a, 0x0a}},
	{"sbcl",	{0x0b, 0x0b, 0x0b, 0x0b, 0x0b}}, 
	{"dstat",	{0x0c, 0x0c, 0x0c, 0x0c, 0x0c}},
	{"sstat0",	{0x0d, 0x0d, 0x0d, 0x0d, 0x0d}},
	{"sstat1",	{0x0e, 0x0e, 0x0e, 0x0e, 0x0e}},
	{"sstat2",	{0x0f, 0x0f, 0x0f, 0x0f, 0x0f}},
	{"dsa0",	{  -1, 0x10, 0x10, 0x10, 0x10}},
	{"dsa1",	{  -1, 0x11, 0x11, 0x11, 0x11}},
	{"dsa2",	{  -1, 0x12, 0x12, 0x12, 0x12}},
	{"dsa3",	{  -1, 0x13, 0x13, 0x13, 0x13}},
	{"ctest0",	{0x14, 0x14, 0x18, 0x18, 0x18}},
	{"ctest1",	{0x15, 0x15, 0x19, 0x19, 0x19}},
	{"ctest2",	{0x16, 0x16, 0x1a, 0x1a, 0x1a}},
	{"ctest3",	{0x17, 0x17, 0x1b, 0x1b, 0x1b}},
	{"ctest4",	{0x18, 0x18, 0x21, 0x21, 0x21}},
	{"ctest5",	{0x19, 0x19, 0x22, 0x22, 0x22}},
	{"ctest6",	{0x1a, 0x1a, 0x23, 0x23, 0x23}},
	{"ctest7",	{0x1b, 0x1b,   -1,   -1,   -1}},
	{"temp0",	{0x1c, 0x1c, 0x1c, 0x1c, 0x1c}},
	{"temp1",	{0x1d, 0x1d, 0x1d, 0x1d, 0x1d}},
	{"temp2", 	{0x1e, 0x1e, 0x1e, 0x1e, 0x1e}},
	{"temp3",	{0x1f, 0x1f, 0x1f, 0x1f, 0x1f}},
	{"dfifo",	{0x20, 0x20, 0x20, 0x20, 0x20}},
	{"istat", 	{0x21, 0x21, 0x14, 0x14, 0x14}},
	{"ctest8",	{0x22, 0x22,   -1,   -1,   -1}},
	{"lcrc",	{  -1, 0x23,   -1,   -1,   -1}},
	{"ctest9",	{0x23,   -1,   -1,   -1,   -1}},
	{"dbc0",	{0x24, 0x24, 0x24, 0x24, 0x24}},
	{"dbc1",	{0x25, 0x25, 0x25, 0x25, 0x25}},
	{"dbc2",	{0x26, 0x26, 0x26, 0x26, 0x26}},
	{"dcmd",	{0x27, 0x27, 0x27, 0x27, 0x27}},
	{"dnad0",	{0x28, 0x28, 0x28, 0x28, 0x28}},
	{"dnad1",	{0x29, 0x29, 0x29, 0x29, 0x29}},
	{"dnad2",	{0x2a, 0x2a, 0x2a, 0x2a, 0x2a}},
	{"dnad3",	{0x2b, 0x2b, 0x2b, 0x2b, 0x2b}},
	{"dsp0",	{0x2c, 0x2c, 0x2c, 0x2c, 0x2c}},
	{"dsp1",	{0x2d, 0x2d, 0x2d, 0x2d, 0x2d}},
	{"dsp2",	{0x2e, 0x2e, 0x2e, 0x2e, 0x2e}},
	{"dsp3",	{0x2f, 0x2f, 0x2f, 0x2f, 0x2f}},
	{"dsps0",	{0x30, 0x30, 0x30, 0x30, 0x30}},
	{"dsps1",	{0x31, 0x31, 0x31, 0x31, 0x31}},
	{"dsps2",	{0x32, 0x32, 0x32, 0x32, 0x32}},
	{"dsps3",	{0x33, 0x33, 0x33, 0x33, 0x33}},
	{"scratch0",	{  -1, 0x34,   -1,   -1,   -1}},
	{"scratch1",	{  -1, 0x35,   -1,   -1,   -1}},
	{"scratch2",	{  -1, 0x36,   -1,   -1,   -1}},
	{"scratch3",	{  -1, 0x37,   -1,   -1,   -1}},
	{"scratcha0",	{0x10,   -1, 0x34, 0x34, 0x34}},
	{"scratcha1",	{0x11,   -1, 0x35, 0x35, 0x35}},
	{"scratcha2",	{0x12,   -1, 0x36, 0x36, 0x36}},
	{"scratcha3",	{0x13,   -1, 0x37, 0x37, 0x37}},
	{"dmode",	{0x34, 0x38, 0x38, 0x38, 0x38}},
	{"dien",	{0x39, 0x39, 0x39, 0x39, 0x39}},
	{"dwt",		{0x3a, 0x3a, 0x3a,   -1,   -1}},
	{"sbr",		{  -1,   -1,   -1, 0x3a, 0x3a}},
	{"dcntl",	{0x3b, 0x3b, 0x3b, 0x3b, 0x3b}},
	{"addr0",	{  -1, 0x3c, 0x3c, 0x3c, 0x3c}},
	{"addr1",	{  -1, 0x3d, 0x3d, 0x3d, 0x3d}},
	{"addr2",	{  -1, 0x3e, 0x3e, 0x3e, 0x3e}},
	{"addr3",	{  -1, 0x3f, 0x3f, 0x3f, 0x3f}},
	{"sien0",	{  -1,   -1, 0x40, 0x40, 0x40}},
	{"sien1",	{  -1,   -1, 0x41, 0x41, 0x41}},
	{"sist0",	{  -1,   -1, 0x42, 0x42, 0x42}},
	{"sist1",	{  -1,   -1, 0x43, 0x43, 0x43}},
	{"slpar",	{  -1,   -1, 0x44, 0x44, 0x44}},
	{"swide",	{  -1,   -1, 0x45,   -1, 0x45}},
	{"macntl",	{  -1,   -1, 0x46, 0x46, 0x46}},
	{"gpcntl",	{  -1,   -1, 0x47, 0x47, 0x47}},
	{"stime0",	{  -1,   -1, 0x48, 0x48, 0x48}},
	{"stime1",	{  -1,   -1, 0x49, 0x49, 0x49}},
	{"respid0",	{  -1,   -1, 0x4a, 0x4a, 0x4a}},
	{"respid1",	{  -1,   -1, 0x4b,   -1, 0x4b}},
	{"stest0",	{  -1,   -1, 0x4c, 0x4c, 0x4c}},
	{"stest1",	{  -1,   -1, 0x4d, 0x4d, 0x4d}},
	{"stest2",	{  -1,   -1, 0x4e, 0x4e, 0x4e}},
	{"stest3",	{  -1,   -1, 0x4f, 0x4f, 0x4f}},
	{"sidl0",	{  -1,   -1, 0x50, 0x50, 0x50}},
	{"sidl1",	{  -1,   -1, 0x51,   -1, 0x51}},
	{"sodl0",	{  -1,   -1, 0x54, 0x54, 0x54}},
	{"sodl1",	{  -1,   -1, 0x55,   -1, 0x55}},
	{"sbdl0",	{  -1,   -1, 0x58, 0x58, 0x58}},
	{"sbdl1",	{  -1,   -1, 0x59,   -1, 0x59}},
	{"scratchb0",	{0x3c,   -1, 0x5c, 0x5c, 0x5c}},
	{"scratchb1",	{0x3d,   -1, 0x5d, 0x5d, 0x5d}},
	{"scratchb2",	{0x3e,   -1, 0x5e, 0x5e, 0x5e}},
	{"scratchb3",	{0x3f,   -1, 0x5f, 0x5f, 0x5f}},
	{"scratchc0",	{  -1,   -1,   -1,   -1, 0x60}},
	{"scratchc1",	{  -1,   -1,   -1,   -1, 0x61}},
	{"scratchc2",	{  -1,   -1,   -1,   -1, 0x62}},
	{"scratchc3",	{  -1,   -1,   -1,   -1, 0x63}},
	{"scratchd0",	{  -1,   -1,   -1,   -1, 0x64}},
	{"scratchd1",	{  -1,   -1,   -1,   -1, 0x65}},
	{"scratchd2",	{  -1,   -1,   -1,   -1, 0x66}},
	{"scratchd3",	{  -1,   -1,   -1,   -1, 0x67}},
	{"scratche0",	{  -1,   -1,   -1,   -1, 0x68}},
	{"scratche1",	{  -1,   -1,   -1,   -1, 0x69}},
	{"scratche2",	{  -1,   -1,   -1,   -1, 0x6a}},
	{"scratche3",	{  -1,   -1,   -1,   -1, 0x6b}},
	{"scratchf0",	{  -1,   -1,   -1,   -1, 0x6c}},
	{"scratchf1",	{  -1,   -1,   -1,   -1, 0x6d}},
	{"scratchf2",	{  -1,   -1,   -1,   -1, 0x6e}},
	{"scratchf3",	{  -1,   -1,   -1,   -1, 0x6f}},
	{"scratchg0",	{  -1,   -1,   -1,   -1, 0x70}},
	{"scratchg1",	{  -1,   -1,   -1,   -1, 0x71}},
	{"scratchg2",	{  -1,   -1,   -1,   -1, 0x72}},
	{"scratchg3",	{  -1,   -1,   -1,   -1, 0x73}},
	{"scratchh0",	{  -1,   -1,   -1,   -1, 0x74}},
	{"scratchh1",	{  -1,   -1,   -1,   -1, 0x75}},
	{"scratchh2",	{  -1,   -1,   -1,   -1, 0x7e}},
	{"scratchh3",	{  -1,   -1,   -1,   -1, 0x77}},
	{"scratchi0",	{  -1,   -1,   -1,   -1, 0x78}},
	{"scratchi1",	{  -1,   -1,   -1,   -1, 0x79}},
	{"scratchi2",	{  -1,   -1,   -1,   -1, 0x7a}},
	{"scratchi3",	{  -1,   -1,   -1,   -1, 0x7b}},
	{"scratchj0",	{  -1,   -1,   -1,   -1, 0x7c}},
	{"scratchj1",	{  -1,   -1,   -1,   -1, 0x7d}},
	{"scratchj2",	{  -1,   -1,   -1,   -1, 0x7e}},
	{"scratchj3",	{  -1,   -1,   -1,   -1, 0x7f}},
};

int	lineno;
int	err_listed;
int	arch;
int	partial_flag;

char	inbuf[128];

char	*sourcefile;
char	*outputfile;
char	*listfile;
char	*errorfile;

FILE	*infp;
FILE	*outfp;
FILE	*listfp;
FILE	*errfp;

void	setarch(char *);
void	parse (void);
void	process (void);
void	emit_symbols (void);
void	list_symbols (void);
void	errout (char *);
void	define_symbol (char *, u_int32_t, short, short);
void	patch_label (void);
void	close_script (void);
void	new_script (char *);
void	store_inst (void);
int	expression (int *);
int	evaluate (int);
int	number (char *);
int	lookup (char *);
int	reserved (char *, int);
int	CheckPhase (int);
int	CheckRegister (int);
void	transfer (int, int);
void	select_reselect (int);
void	set_clear (u_int32_t);
void	block_move (void);
void	register_write (void);
void	memory_to_memory (void);
void	loadstore (int);
void	error_line(void);
char	*makefn(char *, char *);
void	usage(void);

int
main (int argc, char *argv[])
{
	int	i;
	struct patchlist *p;

	if (argc < 2 || argv[1][0] == '-')
		usage();
	sourcefile = argv[1];
	infp = fopen (sourcefile, "r");
	if (infp == NULL) {
		perror ("open source");
		fprintf (stderr, "scc: error opening source file %s\n", argv[1]);
		exit (1);
	}
	/*
	 * process options
	 * -l [listfile]
	 * -o [outputfile]
	 * -p [outputfile]
	 * -z [debugfile]
	 * -e [errorfile]
	 * -a arch
	 * -v
	 * -u
	 */
	for (i = 2; i < argc; ++i) {
		if (argv[i][0] != '-')
			usage();
		switch (argv[i][1]) {
		case 'o':
		case 'p':
			partial_flag = argv[i][1] == 'p';
			if (i + 1 >= argc || argv[i + 1][0] == '-')
				outputfile = makefn (sourcefile, "out");
			else {
				outputfile = argv[i + 1];
				++i;
			}
			break;
		case 'l':
			if (i + 1 >= argc || argv[i + 1][0] == '-')
				listfile = makefn (sourcefile, "lis");
			else {
				listfile = argv[i + 1];
				++i;
			}
			break;
		case 'e':
			if (i + 1 >= argc || argv[i + 1][0] == '-')
				errorfile = makefn (sourcefile, "err");
			else {
				errorfile = argv[i + 1];
				++i;
			}
			break;
		case 'a':
			if (i + 1 == argc)
				usage();
			setarch(argv[i +1]);
			if (arch == 0) {
				fprintf(stderr,"%s: bad arch '%s'\n",
					argv[0], argv[i +1]);
				exit(1);
			}
			++i;
			break;
		default:
			fprintf (stderr, "scc: unrecognized option '%c'\n",
			    argv[i][1]);
			usage();
		}
	}
	if (outputfile)
		outfp = fopen (outputfile, "w");
	if (listfile)
		listfp = fopen (listfile, "w");
	if (errorfile)
		errfp = fopen (errorfile, "w");
	else
		errfp = stderr;

	if (outfp) {
		time_t cur_time;

		fprintf(outfp, "/*\t$OpenBSD: ncr53cxxx.c,v 1.7 2005/10/08 15:54:49 krw Exp $\t*/\n");
		fprintf(outfp, "/*\n");
		fprintf(outfp, " *\tDO NOT EDIT - this file is automatically generated.\n");
		time(&cur_time);
		fprintf(outfp, " *\tcreated from %s on %s", sourcefile, ctime(&cur_time));
		fprintf(outfp, " */\n");
	}

	while (fgets (inbuf, sizeof (inbuf), infp)) {
		++lineno;
		if (listfp)
			fprintf (listfp, "%3d:  %s", lineno, inbuf);
		err_listed = 0;
		parse ();
		if (ntokens) {
#ifdef DUMP_TOKENS
			int	i;

			fprintf (listfp, "      %d tokens\n", ntokens);
			for (i = 0; i < ntokens; ++i) {
				fprintf (listfp, "      %d: ", i);
				if (tokens[i].type)
					fprintf (listfp,"'%c'\n", tokens[i].type);
				else
					fprintf (listfp, "%s\n", tokens[i].name);
			}
#endif
			if (ntokens >= 2 && tokens[0].type == 0 &&
			    tokens[1].type == ':') {
			    	define_symbol (tokens[0].name, dsps, S_LABEL, F_DEFINED);
				tokenix += 2;
			}
			if (tokenix < ntokens)
				process ();
		}

	}
	close_script ();
	emit_symbols ();
	if (outfp && !partial_flag) {
		fprintf (outfp, "\nu_int32_t INSTRUCTIONS = 0x%08x;\n", ninsts);
		fprintf (outfp, "u_int32_t PATCHES = 0x%08x;\n", npatches);
		fprintf (outfp, "u_int32_t LABELPATCHES[] = {\n");
		p = patches;
		while (p) {
			fprintf (outfp, "\t0x%08x,\n", p->offset / 4);
			p = p->next;
		}
		fprintf (outfp, "};\n\n");
	}
	list_symbols ();
	exit(0);
}

void setarch(char *val)
{
	switch (atoi(val)) {
	case 700:
		arch = ARCH700;
		break;
	case 710:
		arch = ARCH710;
		break;
	case 720:
		arch = ARCH720;
		break;
	case 810:
		arch = ARCH810;
		break;
	case 825:
		arch = ARCH825;
		break;
	default:
		arch = 0;
	}
}

void emit_symbols ()
{
	int	i;
	struct	patchlist *p;

	if (nsymbols == 0 || outfp == NULL)
		return;

	for (i = 0; i < nsymbols; ++i) {
		char	*code;
		if ((symbols[i].flags & F_DEFINED) == 0 &&
		    symbols[i].type != S_EXTERNAL) {
			fprintf(stderr, "warning: symbol %s undefined\n",
			    symbols[i].name);
		}
		if (symbols[i].type == S_ABSOLUTE)
			code = "A_";
		else if (symbols[i].type == S_RELATIVE)
			code = "R_";
		else if (symbols[i].type == S_EXTERNAL)
			code = "E_";
		else if (symbols[i].flags & F_ENTRY)
			code = "Ent_";
		else
			continue;
		fprintf (outfp, "#define\t%s%s\t0x%08x\n", code, symbols[i].name,
			symbols[i].value);
		if (symbols[i].flags & F_ENTRY || symbols[i].patchlist == NULL)
			continue;
		fprintf (outfp, "u_int32_t %s%s_Used[] = {\n", code, symbols[i].name);
#if 1
		p = symbols[i].patchlist;
		while (p) {
			fprintf (outfp, "\t0x%08x,\n", p->offset / 4);
			p = p->next;
		}
#endif
		fprintf (outfp, "};\n\n");
	}
	/* patches ? */
}

void list_symbols ()
{
	int	i;

	if (nsymbols == 0 || listfp == NULL)
		return;
	fprintf (listfp, "\n\nValue     Type     Symbol\n");
	for (i = 0; i < nsymbols; ++i) {
		fprintf (listfp, "%08x: %-8s %s\n", symbols[i].value,
			stypes[symbols[i].type], symbols[i].name);
	}
}

void errout (char *text)
{
	error_line();
	fprintf (errfp, "*** %s ***\n", text);
}

void parse ()
{
	char *p = inbuf;
	char c;
	char string[64];
	char *s;
	size_t len;

	ntokens = tokenix = 0;
	while (1) {
		while ((c = *p++) && c != '\n' && (c <= ' ' || c == '\t'))
			;
		if (c == '\n' || c == 0 || c == ';')
			break;
		if (ntokens >= MAXTOKENS) {
			errout ("Token table full");
			break;
		}
		if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
		    (c >= 'A' && c <= 'Z') || c == '$' || c == '_') {
		    	s = string;
		    	*s++ = c;
		    	while (((c = *p) >= '0' && c <= '9') ||
		    	    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
		    	    c == '_' || c == '$') {
		    	    	*s++ = *p++;
		    	}
		    	*s = 0;
			len = strlen (string) + 1;
		    	tokens[ntokens].name = malloc (len);
		    	strlcpy (tokens[ntokens].name, string, len);
		    	tokens[ntokens].type = 0;
		}
		else {
			tokens[ntokens].type = c;
		}
		++ntokens;
	}
	return;
}

void	process ()
{
	int	i;

	if (tokens[tokenix].type) {
		error_line();
		fprintf (errfp, "Error: expected directive, found '%c'\n",
			tokens[tokenix].type);
		return;
	}
	for (i = 0; directives[i].name; ++i) {
		if (strcmpi (directives[i].name, tokens[tokenix].name) == 0)
			break;
	}
	if (directives[i].name == NULL) {
		error_line();
		fprintf (errfp, "Error: expected directive, found \"%s\"\n",
			tokens[tokenix].name);
		return;
	}
	if (directives[i].func == NULL) {
		error_line();
		fprintf (errfp, "No function for directive \"%s\"\n", tokens[tokenix].name);
	} else {
#if 0
		fprintf (listfp, "Processing directive \"%s\"\n", directives[i].name);
#endif
		++tokenix;
		(*directives[i].func) ();
	}
}

void define_symbol (char *name, u_int32_t value, short type, short flags)
{
	int	i;
	struct patchlist *p;
	size_t	len;

	for (i = 0; i < nsymbols; ++i) {
		if (symbols[i].type == type && strcmp (symbols[i].name, name) == 0) {
			if (symbols[i].flags & F_DEFINED) {
				error_line();
				fprintf (errfp, "*** Symbol \"%s\" multiply defined\n",
					name);
			} else {
				symbols[i].flags |= flags;
				symbols[i].value = value;
				p = symbols[i].patchlist;
				while (p) {
					if (p->offset > dsps)
						errout ("Whoops\007");
					else
						script[p->offset / 4] += dsps;
					p = p->next;
				}
			}
			return;
		}
	}
	if (nsymbols >= MAXSYMBOLS) {
		errout ("Symbol table full");
		return;
	}
	symbols[nsymbols].type = type;
	symbols[nsymbols].flags = flags;
	symbols[nsymbols].value = value;
	symbols[nsymbols].patchlist = NULL;
	len = strlen (name) + 1;
	symbols[nsymbols].name = malloc (len);
	strlcpy (symbols[nsymbols].name, name, len);
	++nsymbols;
}

void patch_label (void)
{
	struct patchlist *p, **h;

	h = &patches;
	while(*h)
		h = &(*h)->next;
	p = (struct patchlist *) malloc (sizeof (struct patchlist));
	*h = p;
	p->next = NULL;
	p->offset = dsps + 4;
	npatches++;
}

void close_script ()
{
	int	i;

	if (dsps == 0)
		return;
	if (outfp) {
		fprintf (outfp, "const u_int32_t %s[] = {\n", script_name);
		for (i = 0; i < dsps / 4; i += 2) {
			fprintf (outfp, "\t0x%08x, 0x%08x", script[i],
				script[i + 1]);
			/* check for memory move instruction */
			if ((script[i] & 0xe0000000) == 0xc0000000)
				fprintf (outfp, ", 0x%08x,", script[i + 2]);
			else
				if ((i + 2) <= dsps / 4) fprintf (outfp, ",\t\t");
			fprintf (outfp, "\t/* %03x - %3d */\n", i * 4, i * 4);
			if ((script[i] & 0xe0000000) == 0xc0000000)
				++i;
		}
		fprintf (outfp, "};\n\n");
	}
	dsps = 0;
}

void new_script (char *name)
{
	size_t len = strlen (name) + 1;

	close_script ();
	script_name = malloc (len);
	strlcpy (script_name, name, len);
}

int	reserved (char *string, int t)
{
	if (tokens[t].type == 0 && strcmpi (tokens[t].name, string) == 0)
		return (1);
	return (0);
}

int	CheckPhase (int t)
{
	int	i;

	for (i = 0; i < 8; ++i) {
		if (reserved (phases[i], t)) {
			inst0 |= i << 24;
			return (1);
		}
	}
	return (0);
}

int	CheckRegister (int t)
{
	int	i;

	if (arch <= 0) {
		errout("'ARCH' statement missing");
		return -1;
	}
	for (i = 0; i < (sizeof(regs) / sizeof(regs[0])); i++) {
		if (regs[i].addr[arch - 1] >= 0 && reserved(regs[i].name, t))
			return regs[i].addr[arch-1];
	}
	return (-1);
}

int	expression (int *t)
{
	int	value;
	int	i = *t;

	value = evaluate (i++);
	while (i < ntokens) {
		if (tokens[i].type == '+')
			value += evaluate (i + 1);
		else if (tokens[i].type == '-')
			value -= evaluate (i + 1);
		else
			errout ("Unknown identifier");
		i += 2;
	}
	*t = i;
	return (value);
}

int	evaluate (t)
{
	int	value;
	char	*name;

	if (tokens[t].type) {
		errout ("Expected an identifier");
		return (0);
	}
	name = tokens[t].name;
	if (*name >= '0' && *name <= '9')
		value = number (name);
	else
		value = lookup (name);
	return (value);
}

int	number (char *s)
{
	int	value;
	int	n;
	int	radix;

	radix = 10;
	if (*s == '0') {
		++s;
		radix = 8;
		switch (*s) {
		case 'x':
		case 'X':
			radix = 16;
			break;
		case 'b':
		case 'B':
			radix = 2;
		}
		if (radix != 8)
			++s;
	}
	value = 0;
	while (*s) {
		n = *s++;
		if (n >= '0' && n <= '9')
			n -= '0';
		else if (n >= 'a' && n <= 'f')
			n -= 'a' - 10;
		else if (n >= 'A' && n <= 'F')
			n -= 'A' - 10;
		else {
			error_line();
			fprintf (errfp, "*** Expected digit\n");
			n = 0;
		}
		if (n >= radix)
			errout ("Expected digit");
		else
			value = value * radix + n;
	}
	return (value);
}

int	lookup (char *name)
{
	int	i;
	struct patchlist *p;
	size_t	len;

	for (i = 0; i < nsymbols; ++i) {
		if (strcmp (name, symbols[i].name) == 0) {
			if ((symbols[i].flags & F_DEFINED) == 0) {
				p = (struct patchlist *) &symbols[i].patchlist;
				while (p->next)
					p = p->next;
				p->next = (struct patchlist *) malloc (sizeof (struct patchlist));
				p = p->next;
				p->next = NULL;
				p->offset = dsps + 4;
			}
			return ((int) symbols[i].value);
		}
	}
	if (nsymbols >= MAXSYMBOLS) {
		errout ("Symbol table full");
		return (0);
	}
	symbols[nsymbols].type = S_LABEL;	/* assume forward reference */
	symbols[nsymbols].flags = 0;
	symbols[nsymbols].value = 0;
	p = (struct patchlist *) malloc (sizeof (struct patchlist));
	symbols[nsymbols].patchlist = p;
	p->next = NULL;
	p->offset = dsps + 4;
	len = strlen (name) + 1;
	symbols[nsymbols].name = malloc (len);
	strlcpy (symbols[nsymbols].name, name, len);
	++nsymbols;
	return (0);
}

void	f_arch (void)
{
	int i, archsave;

	i = tokenix;

	archsave = arch;
	setarch(tokens[i].name);
	if( arch == 0) {
		errout("Unrecognized ARCH");
		arch = archsave;
	}
}

void	f_proc (void)
{
	if (tokens[tokenix].type != 0 || tokens[tokenix + 1].type != ':')
		errout ("Invalid PROC statement");
	else
		new_script (tokens[tokenix].name);
}

void	f_pass (void)
{
	errout ("PASS option not implemented");
}

/*
 *	f_list:  process list of symbols for the ENTRY and EXTERNAL directive
 */

void	f_list (void)
{
	int	i;
	short	type;
	short	flags;

	type = strcmpi (tokens[tokenix-1].name, "ENTRY") ? S_EXTERNAL : S_LABEL;
	flags = type == S_LABEL ? F_ENTRY : 0;
	for (i = tokenix; i < ntokens; ++i) {
		if (tokens[i].type != 0) {
			errout ("Expected an identifier");
			return;
		}
		define_symbol (tokens[i].name, 0, type, flags);
		if (i + 1 < ntokens) {
			if (tokens[++i].type == ',')
				continue;
			errout ("Expected a separator");
			return;
		}
	}
}

/*
 *	f_define:	process list of definitions for ABSOLUTE and RELATIVE directive
 */

void	f_define (void)
{
	int	i;
	char	*name;
	u_int32_t value;
	int	type;

	type = strcmpi (tokens[tokenix-1].name, "ABSOLUTE") ? S_RELATIVE : S_ABSOLUTE;
	i = tokenix;
	while (i < ntokens) {
		if (tokens[i].type) {
			errout ("Expected an identifier");
			return;
		}
		if (tokens[i + 1].type != '=') {
			errout ("Expected a separator");
			return;
		}
		name = tokens[i].name;
		i += 2;
		value = expression (&i);
		define_symbol (name, value, type, F_DEFINED);
	}
}

void	store_inst ()
{
	int	i = dsps / 4;
	int	l = 8;

	if ((inst0 & 0xe0000000) == 0xc0000000)
		l = 12;			/* Memory to memory move is 12 bytes */
	if ((dsps + l) / 4 > MAXINST) {
		errout ("Instruction table overflow");
		return;
	}
	script[i++] = inst0;
	script[i++] = inst1;
	if (l == 12)
		script[i++] = inst2;
	if (listfp) {
		fprintf (listfp, "\t%04x: %08x %08x", dsps, inst0, inst1);
		if (l == 12)
			fprintf (listfp, " %08x", inst2);
		fprintf (listfp, "\n");
	}
	dsps += l;
	inst0 = inst1 = inst2 = 0;
	++ninsts;
}

void	f_move (void)
{
	if (reserved ("memory", tokenix))
		memory_to_memory ();
	else if (reserved ("from", tokenix) || tokens[tokenix+1].type == ',')
		block_move ();
	else
		register_write ();
	store_inst ();
}

void	f_jump (void)
{
	transfer (0x80000000, 0);
}

void	f_call (void)
{
	transfer (0x88000000, 0);
}

void	f_return (void)
{
	transfer (0x90000000, 1);
}

void	f_int (void)
{
	transfer (0x98000000, 2);
}

void	f_intfly (void)
{
	transfer (0x98100000, 2);
}

void	f_select (void)
{
	int	t = tokenix;

	if (reserved ("atn", t)) {
		inst0 = 0x01000000;
		++t;
	}
	select_reselect (t);
}

void	f_reselect (void)
{
	select_reselect (tokenix);
}

void	f_wait (void)
{
	int	i = tokenix;

	inst1 = 0;
	if (reserved ("disconnect", i)) {
		inst0 = 0x48000000;
	}
	else {
		if (reserved ("reselect", i))
			inst0 = 0x50000000;
		else if (reserved ("select", i))
			inst0 = 0x50000000;
		else
			errout ("Expected SELECT or RESELECT");
		++i;
		if (reserved ("rel", i)) {
#if 0 /* driver will fix relative dsps to absolute */
			if (arch < ARCH710) {
				errout ("Wrong arch for relative dsps");
			}
#endif
			i += 2;
			inst1 = evaluate (i) - dsps - 8;
			inst0 |= 0x04000000;
		}
		else {
			inst1 = evaluate (i);
			patch_label();
		}
	}
	store_inst ();
}

void	f_disconnect (void)
{
	inst0 = 0x48000000;
	store_inst ();
}

void	f_set (void)
{
	set_clear (0x58000000);
}

void	f_clear (void)
{
	set_clear (0x60000000);
}

void	f_load (void)
{
	inst0 = 0xe1000000;
	if (arch < ARCH810) {
		errout ("Wrong arch for load/store");
		return;
	}
	loadstore(tokenix);
}

void	f_store (void)
{
	int i;
	inst0 = 0xe0000000;
	if (arch < ARCH810) {
		errout ("Wrong arch for load/store");
		return;
	}
	i = tokenix;
	if (reserved("noflush", i)) {
		inst0 |= 0x2000000;
		i++;
	}
	loadstore(i);
}

void	f_nop (void)
{
	inst0 = 0x80000000;
	inst1 = 0x00000000;
	store_inst ();
}

void loadstore(int i)
{
	int reg, size;

	reg = CheckRegister(i);
	if (reg < 0)	
		errout ("Expected register");
	else
		inst0 |= reg <<  16;
	if (reg == 8)
		errout ("Register can't be SFBR");
	i++;
	if (tokens[i].type == ',')
		i++;
	else
		errout ("expected ','");
	size = evaluate(i);
	if (i < 1 || i > 4)
		errout("wrong size");
	if ((reg & 0x3) + size > 4)
		errout("size too big for register");
	inst0 |= size;
	i++;
	if (tokens[i].type == ',')
		i++;
	else
		errout ("expected ','");
	if (reserved("from", i) || reserved("dsarel", i)) {
		if (arch < ARCH710) {
			errout ("Wrong arch for table indirect");
			return;
		}
		i++;
		inst0 |= 0x10000000;
	}
	inst1 = evaluate(i);
	store_inst ();
}

void	transfer (int word0, int type)
{
	int	i;

	i = tokenix;
	inst0 = word0;
	if (type == 0 && reserved ("rel", i)) {
#if 0 /* driver will fix relative dsps to absolute */
		if (arch < ARCH710) {
			errout ("Wrong arch for relative dsps");
		}
#endif
		inst1 = evaluate (i + 2) - dsps - 8;
		i += 4;
		inst0 |= 0x00800000;
	}
	else if (type != 1) {
		inst1 = evaluate (i);
		++i;
		if (type == 0)
			patch_label();
	}
	if (i >= ntokens) {
		inst0 |= 0x00080000;
		store_inst ();
		return;
	}
	if (tokens[i].type != ',')
		errout ("Expected a separator, ',' assumed");
	else
		++i;
	if (reserved("when", i))
		inst0 |= 0x00010000;
	else if (reserved ("if", i) == 0) {
		errout ("Expected a reserved word");
		store_inst ();
		return;
	}
	i++;
	if (reserved("false", i)) {
		store_inst ();
		return;
	}
	if (reserved ("not", i))
		++i;
	else
		inst0 |= 0x00080000;
	if (reserved ("atn", i)) {
		inst0 |= 0x00020000;
		++i;
	} else if (CheckPhase (i)) {
		inst0 |= 0x00020000;
		++i;
	}
	if (i < ntokens && tokens[i].type != ',') {
		if (inst0 & 0x00020000) {
			if (inst0 & 0x00080000 && reserved ("and", i)) {
				++i;
			}
			else if ((inst0 & 0x00080000) == 0 && reserved ("or", i)) {
				++i;
			}
			else
				errout ("Expected a reserved word");
		}
		inst0 |= 0x00040000 + (evaluate (i++) & 0xff);
	}
	if (i < ntokens) {
		if (tokens[i].type == ',')
			++i;
		else
			errout ("Expected a separator, ',' assumed");
		if (reserved ("and", i) && reserved ("mask", i + 1))
			inst0 |= ((evaluate (i + 2) & 0xff) << 8);
		else
			errout ("Expected , AND MASK");
	}
	store_inst ();
}

void 	select_reselect (int t)
{
	inst0 |= 0x40000000;		/* ATN may be set from SELECT */
	if (reserved ("from", t)) {
		if (arch < ARCH710) {
			errout ("Wrong arch for table indirect");
			return;
		}
		++t;
		inst0 |= 0x02000000 | evaluate (t++);
	}
	else
		inst0 |= (evaluate (t++) & 0xff) << 16;
	if (tokens[t++].type == ',') {
		if (reserved ("rel", t)) {
#if 0 /* driver will fix relative dsps to absolute */
			if (arch < ARCH710) {
				errout ("Wrong arch for relative dsps");
			}
#endif
			inst0 |= 0x04000000;
			inst1 = evaluate (t + 2) - dsps - 8;
		}
		else {
			inst1 = evaluate (t);
			patch_label();
		}
	}
	else
		errout ("Expected separator");
	store_inst ();
}

void	set_clear (u_int32_t code)
{
	int	i = tokenix;
	short	need_and = 0;

	inst0 = code;
	while (i < ntokens) {
		if (need_and) {
			if (reserved ("and", i))
				++i;
			else
				errout ("Expected AND");
		}
		if (reserved ("atn", i)) {
			inst0 |= 0x0008;
			++i;
		}
		else if (reserved ("ack", i)) {
			inst0 |= 0x0040;
			++i;
		}
		else if (reserved ("target", i)) {
			inst0 |= 0x0200;
			++i;
		}
		else if (reserved ("carry", i)) {
			inst0 |= 0x0400;
			++i;
		}
		else
			errout ("Expected ATN, ACK, TARGET or CARRY");
		need_and = 1;
	}
	store_inst ();
}

void	block_move ()
{
	if (reserved ("from", tokenix)) {
		if (arch < ARCH710) {
			errout ("Wrong arch for table indirect");
			return;
		}
		inst1 = evaluate (tokenix+1);
		inst0 |= 0x10000000 | inst1;	/*** ??? to match Zeus script */
		tokenix += 2;
	}
	else {
		inst0 |= evaluate (tokenix++);	/* count */
		tokenix++;			/* skip ',' */
		if (reserved ("ptr", tokenix)) {
			++tokenix;
			inst0 |= 0x20000000;
		}
		inst1 = evaluate (tokenix++);	/* address */
	}
	if (tokens[tokenix].type != ',')
		errout ("Expected separator");
	if (reserved ("when", tokenix + 1)) {
		inst0 |= 0x08000000;
		CheckPhase (tokenix + 2);
	}
	else if (reserved ("with", tokenix + 1)) {
		CheckPhase (tokenix + 2);
	}
	else
		errout ("Expected WITH or WHEN");
}

void	register_write ()
{
	/*
	 * MOVE reg/data8 TO reg			register write
	 * MOVE reg <op> data8 TO reg			register write
	 * MOVE reg + data8 TO reg WITH CARRY		register write
	 */
	int	op;
	int	reg;
	int	data;

	if (reserved ("to", tokenix+1))
		op = 0;
	else if (reserved ("shl", tokenix+1))
		op = 1;
	else if (reserved ("shr", tokenix+1))
		op = 5;
	else if (tokens[tokenix+1].type == '|')
		op = 2;
	else if (reserved ("xor", tokenix+1))
		op = 3;
	else if (tokens[tokenix+1].type == '&')
		op = 4;
	else if (tokens[tokenix+1].type == '+')
		op = 6;
	else if (tokens[tokenix+1].type == '-')
		op = 8;
	else
		errout ("Unknown register operator");
	switch (op) {
	case 2:
	case 3:
	case 4:
	case 6:
	case 8:
		if (reserved ("to", tokenix+3) == 0)
			errout ("Register command expected TO");
	}
	reg = CheckRegister (tokenix);
	if (reg < 0) {			/* Not register, must be data */
		data = evaluate (tokenix);
		if (op)
			errout ("Register operator not move");
		reg = CheckRegister (tokenix+2);
		if (reg < 0)
			errout ("Expected register");
		inst0 = 0x78000000 | (data << 8) | reg << 16;
#if 0
fprintf (listfp, "Move data to register: %02x %d\n", data, reg);
#endif
	} else if (op) {
		switch (op) {
		case 2:
		case 3:
		case 4:
		case 6:
		case 8:
			inst0 = 0;
			/* A register read/write operator */
			if (reserved("sfbr", tokenix+2)) {
				if (arch < ARCH825)
					errout("wrong arch for add with SFBR");
				if (op == 8)
					errout("can't substract SFBR");
				inst0 |= 0x00800000;
				data = 0;
			} else
				data = evaluate (tokenix+2);
			if (tokenix+5 < ntokens) {
				if (!reserved("with", tokenix+5) ||
				    !reserved("carry", tokenix+6)) {
					errout("Expected 'WITH CARRY'");
				} else if (op != 6) {
					errout("'WITH CARRY' only valide "
					    "with '+'");
				}
				op = 7;
			}
			if (op == 8) {
				data = -data;
				op = 6;
			}
			inst0 |= (data & 0xff) << 8;
			data = CheckRegister (tokenix+4);
			break;
		default:
			data = CheckRegister (tokenix+2);
			break;
		}
		if (data < 0)
			errout ("Expected register");
		if (reg != data && reg != 8 && data != 8)
			errout ("One register MUST be SBFR");
		if (reg == data) {	/* A register read/modify/write */
#if 0
fprintf (listfp, "Read/modify register: %02x %d %d\n", inst0 >> 8, op, reg);
#endif
			inst0 |= 0x78000000 | (op << 24) | (reg << 16);
		}
		else {			/* A move to/from SFBR */
			if (reg == 8) {	/* MOVE SFBR <> TO reg */
#if 0
fprintf (listfp, "Move SFBR to register: %02x %d %d\n", inst0 >> 8, op, data);
#endif
				inst0 |= 0x68000000 | (op << 24) | (data << 16);
			}
			else {
#if 0
fprintf (listfp, "Move register to SFBR: %02x %d %d\n", inst0 >> 8, op, reg);
#endif
				inst0 |= 0x70000000 | (op << 24) | (reg << 16);
			}
		}
	} else {				/* register to register */
		data = CheckRegister (tokenix+2);
		if (data < 0)
			errout ("Expected register");
		if (reg == 8)		/* move SFBR to reg */
			inst0 = 0x6a000000 | (data << 16);
		else if (data == 8)	/* move reg to SFBR */
			inst0 = 0x72000000 | (reg << 16);
		else
			errout ("One register must be SFBR");
	}
}

void	memory_to_memory ()
{
	inst0 = 0xc0000000 + evaluate (tokenix+1);
	inst1 = evaluate (tokenix+3);
	/*
	 * need to hack dsps, otherwise patch offset will be wrong for
	 * second pointer
	 */
	dsps += 4;
	inst2 = evaluate (tokenix+5);
	dsps -= 4;
}

void	error_line()
{
	if (errfp != listfp && errfp && err_listed == 0) {
		fprintf (errfp, "%3d:  %s", lineno, inbuf);
		err_listed = 1;
	}
}

char *	makefn (base, sub)
	char *base;
	char *sub;
{
	char *fn;
	size_t len = strlen (base) + strlen (sub) + 2; 

	fn = malloc (len);
	strlcpy (fn, base, len);
	base = strrchr(fn, '.');
	if (base)
		*base = 0;
	strlcat (fn, ".", len);
	strlcat (fn, sub, len);
	return (fn);
}

void	usage()
{
	fprintf (stderr, "usage: scc sourcfile [options]\n");
	exit(1);
}
