#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <stdarg.h>
#include "pdp6common.h"
#include "pdp6bin.h"
#include "args.h"

// TODO: A,,B

#define nil NULL

enum
{
	FW = 0777777777777
};

enum
{
	MAXSYM  = 4000,
	MAXLINE = 256,
	MAXOPND = 20
};

enum Toktype
{
	Unused = 0,
	Symbol,
	Word,

	/* These must be kept as a block for expression parsing */
	Plus,
	Minus,
	Star,
	Slash,
	Exclam,
	Amper,
	Shift,

	Radix10,
	Radix8,
	Radix2,

	Eol,

	/* char classes */
	Letter,
	Digit,
	Ignore,
};

enum Symtype
{
	Undef  = 000,

	Local  = 001,	/* defined, Intern may modify this */
	Intern = 002,	/* exported */
	Extern = 004,	/* defined in other module */

	Def    = 010,
	Hide   = 020,	/* not visible to debugger */
	Label  = 040,	/* to forbid redefinition */

	Operator   = 0100,	/* primary instruction and opdef */
	IoOperator,		/* io instruction */
	Pseudo
	/* Macro etc... */
};

typedef struct Value Value;
struct Value
{
	word val;
	int rel;	/* 0 = absolute, else relative */
};

typedef struct Additive Additive;
struct Additive
{
	int l;
	Value v;
	Additive *next;
};

typedef struct Sym Sym;
struct Sym
{
	word name;	/* sixbit */
	int type;	/* Symtype */
	union {
		Value v;
		void (*f)(void);
	};
	Additive *add;
};

typedef struct Token Token;
struct Token
{
	int type;
	union {
		word w;
		Sym *s;
	};
};

/* Expressions */

enum Xtype
{
	Xval, Xsym, Xunop, Xbiop
};

/* This holds an expression AST */
typedef struct Expr Expr;
struct Expr
{
	int type;	/* Xtype */
	Value v;	/* Xval */
	Sym *s;		/* Xsym */
	int op;		/* Xunop (unary), Xbiop (binary) */
	Expr *l, *r;
};

typedef struct Litword Litword;
struct Litword
{
	Value v;
	Litword *next;
};

int ctab[] = {
	Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,
	Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,
	Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,
	Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,

	Ignore, Exclam,    '"', Unused, Letter, Letter,  Amper,   '\'',
	   '(',    ')',   Star,   Plus,    ',',  Minus, Letter,  Slash,
	 Digit,  Digit,  Digit,  Digit,  Digit,  Digit,  Digit,  Digit,
	 Digit,  Digit, Unused,    ';',    '<', Unused,    '>', Unused,

	Unused, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter,    '[', Unused,    ']',    '^',  Shift,

	Unused, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
	Letter, Letter, Letter, Unused, Unused, Unused, Unused, Unused,
};

char *argv0;
char *filename;
char **files;
int nfiles;
FILE *infp, *tmpfp, *relfp, *lstfp;
char progtitle[MAXLINE];
char progsubtitle[MAXLINE];
int hadstart;
Value start;
char line[MAXLINE];
char curline[MAXLINE];	/* unmodified line for listing */
char *lp;	/* pointer to input */
char *ops[MAXOPND];	/* tokenized operands */
int numops;
int lineno;
int pass2;
int error;
Sym symtab[MAXSYM];
Sym *dot;
Value absdot, reldot;	/* saved values of dot */
int radix = 8;
Token peekt;

Value littabs[9];	/* memory locations of literal tables */
int nlit;		/* current literal table */
int litlevel;		/* literal nesting level; 0 = not inside literal */
Litword *curlit;	/* currently active literal */
Litword *litlist;	/* list of literals seen */

/*
 * for REL output
 */
word item[01000000];
hword itemp;
hword itemtype, itemsz;

hword relocp;
word blockreloc;
int blocksz;


void statement(void);

/* Helpers */

void
err(int n, char *fmt, ...)
{
	if(n == 0 && pass2)
		return;
	va_list ap;
	va_start(ap, fmt);
	fprintf(stderr, "%s:%d: ", filename, lineno);
	vfprintf(stderr, fmt, ap);
	fprintf(stderr, "\n");
	va_end(ap);
	error |= n;
}

void
panic(char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	fprintf(stderr, "\n");
	va_end(ap);
	exit(1);
}

FILE*
mustopen(const char *name, const char *mode)
{
	FILE *f;
	if(f = fopen(name, mode), f == nil)
		panic("couldn't open file: %s", name);
	return f;
}

/* Append to literal and return position in list */
int
addw(Litword **list, Litword *lw)
{
	int n;
	Litword **p;
	n = 0;
	for(p = list; *p; p = &(*p)->next)
		n++;
	*p = lw;
	return n;
}

/* Add literal */
/* TODO: don't add duplicates */
Value
addlit(Litword *l)
{
	Value loc;
	loc = littabs[nlit];
	loc.val += addw(&litlist, l);
	return loc;
}

/* Output/listing */

void
writeout(void)
{
	hword i;
	for(i = 0; i < itemp-1; i++){
		writew(item[i], relfp);
	//	printf("\t\tDUMP: %06o %06o\n", left(item[i]), right(item[i]));
	}
	//printf("\n");
}

/* Start a REL block */
void
startblock(void)
{
	if(blocksz){
		blockreloc <<= 2*(18-blocksz);
		item[relocp] = blockreloc;
		relocp = itemp++;
		blockreloc = 0;
		blocksz = 0;
	}
}

/* Put a relocated word into REL block */
void
putword(word w, int reloc)
{
	item[itemp++] = w;
	blockreloc = blockreloc<<2 | reloc;
	blocksz++;
	itemsz++;
	if(blocksz == 18)
		startblock();
}

/* Start a REL item and write out the last one */
void
startitem(hword it)
{
	startblock();
	if(itemsz){
		item[0] = fw(itemtype, itemsz);
		writeout();
	}

	itemtype = it;
	itemsz = 0;
	itemp = 1;
	relocp = itemp++;
}

int lastline;	/* last line number printed in listing */
void
listline(void)
{
	if(lastline != lineno)
		fprintf(lstfp, "%05d\t%s\n", lineno, curline);
	else
		fprintf(lstfp, "\n");
	lastline = lineno;
}

word relbreak, absbreak;
Value lastdot;

/* Emit a relocated word at dot and advance dot */
void
putv(word w, int rel)
{
	Litword *lw;
	if(litlevel){
		lw = malloc(sizeof(Litword));
		lw->v = (Value){ w, rel };
		lw->next = nil;
		addw(&curlit, lw);
		return;
	}
	if(pass2){
		fprintf(lstfp, "%06o%c\t%06o%c%06o%c\t",
			right(dot->v.val), dot->v.rel ? '\'' : ' ',
			left(w), rel & 2 ? '\'' : ' ',
			right(w), rel & 1 ? '\'' : ' ');
		listline();

		if(dot->v.rel != lastdot.rel ||
		   dot->v.val != lastdot.val+1){
			startitem(Code);
			putword(dot->v.val, dot->v.rel);
		}
		putword(w, rel);
	}else{
//		printf("\t%06o%c\t%06o%c%06o%c\n",
//			right(dot->v.val), dot->v.rel ? '\'' : ' ',
//			left(w), rel & 2 ? '\'' : ' ',
//			right(w), rel & 1 ? '\'' : ' ');
	}
	lastdot = dot->v;
	dot->v.val++;
	if(dot->v.rel){
		if(dot->v.val > relbreak)
			relbreak = dot->v.val;
	}else{
		if(dot->v.val > absbreak)
			absbreak = dot->v.val;
	}
}

/* Input */

char*
getln(void)
{
	int c;
	char *s;
	static char filen[MAXLINE];

	if(pass2)
		for(;;){
			/* if no words were produced,
			 * list the original line here */
			if(lastline == lineno-1){
				fprintf(lstfp, "\t%14s\t", "");
				listline();
			}

			c = getc(tmpfp);
			if(c == 1){		/* filename */
				s = filen;
				while(c = getc(tmpfp), c)
					*s++ = c;
				*s = '\0';
				filename = filen;
				fprintf(lstfp, "\n\t%s\n\n", filename);
				lineno = 0;
				lastline = 0;
			}else if(c == 2){	/* line */
				s = line;
				while(c = getc(tmpfp), c)
					*s++ = c;
				*s = '\0';
				lineno++;
				strcpy(curline, line);
				return line;
			}else if(c == 3)	/* EOF */
				return nil;
		}

	s = line;
	lineno++;
	while(c = getc(infp), c != EOF){
		if(c == 037){	/* line continuation */
			lineno++;
			getc(infp);
			continue;
		}
		if(c == '\n')
			break;
		if(s < &line[MAXLINE])
			*s++ = c & 0177;	/* force 7 bit */
	}
	if(s >= &line[MAXLINE])
		err(0, "warning: line too long");
	if(c == EOF)
		return nil;
	*s = '\0';
	putc(2, tmpfp);
	fputs(line, tmpfp);
	putc(0, tmpfp);
	return line;
}

void
skipwhite(void)
{
	while(isspace(*lp))
		lp++;
}

/* Symbols */

/* Just find symbol, or return nil.
 * TODO: improve linear search... */
Sym*
findsym(word name)
{
	Sym *s;
	for(s = symtab; s < &symtab[MAXSYM]; s++)
		if(s->name == name)
			return s;
	return nil;
}

/* Get symbol, add to table if not present */
Sym*
getsym(word name)
{
	Sym *s;
	s = findsym(name);
	if(s == nil){
		for(s = symtab; s < &symtab[MAXSYM]; s++)
			if(s->name == 0)
				goto found;
		panic("symbol table full");
found:
		s->name = name;
		s->type = Undef;
		s->v = (Value){ 0, 0 };
	}
	return s;
}

void
updateext(Sym *s, Value *v, int l)
{
	Additive *a;

	if(!pass2 || s == nil)
		return;

	if(l){
		a = malloc(sizeof(Additive));
		a->l = 1;
		a->v = dot->v;
		a->next = s->add;
		s->add = a;
	}else{
		if(v->val == 0 && v->rel == 0){
			/* add to request chain */
			*v = s->v;
			s->v = dot->v;
		}else{
			a = malloc(sizeof(Additive));
			a->l = 0;
			a->v = dot->v;
			a->next = s->add;
			s->add = a;
		}
	}
}

/* Token parsing */

/* shift uint64 by any amount */
uint64_t
shift(uint64_t i, int sh)
{
	int left;
	left = sh < 0;
	if(left) sh = -sh;
	if(sh > 64)
		return 0;
	else{
		if(left)
			return i << sh;
		else
			return i >> sh;
	}
}

/* combined shift of two 36 bit words */
void
shiftc(word *a, word *b, int sh)
{
	word aa, bb;

	aa = shift(*a, sh) & FW;
	bb = shift(*b, sh) & FW;
	if(sh < 0){
		*a = aa | shift(*b, sh+36) & FW;
		*b = bb;
	}else{
		*b = bb | shift(*a, sh-36) & FW;
		*a = aa;
	}
}

/* get the integer and fractional parts of a double */
void
splitd(double d, word *integ, word *fract)
{
	int s;
	word e;
	uint64_t m;

	decompdbl(d, &s, &e, &m);
	*integ = shift(m, 52 + 1023-e) & FW;
	*fract = shift(m, 16 + 1023-e) & FW;
}

/* parse a potential number, fall back to symbol, advance line pointer */
Token
symnum(void)
{
	Token t;
	int neg, exp;
	word n;
	double f, e;
	word fract, integ;
	int isnum;
	char *symp, symbuf[7];
	int fixed;
	int sgn;
	int sh;

	n = 0;
	f = 0.0;
	isnum = 0;
	fixed = 0;
	fract = 0;
	if(lp[0] == '^' && lp[1] == 'F'){
		fixed = 1;
		lp += 2;
	}
	while(isdigit(*lp)){
		n = n*radix + *lp-'0';
		f = f*10.0 + *lp-'0';
		isnum = 1;
		lp++;
	}
	/* ignore leading digits if it is a symbol */
	symp = lp;
	/* try to parse a fractional number */
	if(*lp == '.'){
		lp++;
		e = 1.0/10.0;
		while(isdigit(*lp)){
			f += (*lp-'0')*e;
			e /= 10.0;
			isnum = 1;
			lp++;
		}
		if(isnum){
			if(*lp == 'e' || *lp == 'E'){
				lp++;
				neg = 0;
				if(*lp == '+')
					lp++;
				else if(*lp == '-'){
					neg = 1;
					lp++;
				}
				exp = 0;
				while(isdigit(*lp)){
					exp = exp*10 + *lp-'0';
					lp++;
				}
				while(exp--)
					if(neg)
						f /= 10.0;
					else
						f *= 10.0;
			}
			splitd(f, &integ, &fract);
			n = fixed ? integ : dtopdp(f);
		}
	}
	/* bit shift */
	if(isnum && *lp == 'B'){
		/* TODO: actually a numeric expression */
		lp++;
		sh = 0;
		sgn = 1;
		if(*lp == '-'){
			sgn = -1;
			lp++;
		}else if(*lp == '+')
			lp++;
		while(isdigit(*lp)){
			sh = sh*10 + *lp-'0';
			lp++;
		}
		sh *= sgn;
		shiftc(&n, &fract, sh-35);
	}

	/* A number would end here, so it's a symbol */
	if(!isnum || *lp && !isspace(*lp) && israd50(*lp)){
		lp = symp;
		symp = symbuf;
		while(*lp && !isspace(*lp) && israd50(*lp)){
			if(symp < &symbuf[6])
				*symp++ = *lp;
			lp++;
		}
		*symp = '\0';
		t.type = Symbol;
		t.s = getsym(sixbit(symbuf));
	}else{
		t.type = Word;
		t.w = n;
	}
	return t;
}

word
character(char delim, int sz)
{
	int mask;
	word w;
	char c;

	w = 0;
	mask = (1<<sz) - 1;
	while(c = *lp++, c != delim){
		if(c == '\\')
			c = *lp++;
		if(sz == 6)
			c = ascii2sixbit(c);
		if(c < 0)
			continue;
		c &= mask;
		w = (w<<sz) | c;
	}
	if(sz == 7)
		w &= 0377777777777;
	return w;
}

Token
token(void)
{
	Token t;

	if(peekt.type != Unused){
		t = peekt;
		peekt.type = Unused;
		return t;
	}

	skipwhite();
	while(*lp)
		switch(ctab[*lp]){
		case Letter:
		case Digit:
			return symnum();

		case '"':
			lp++;
			t.type = Word;
			t.w = character('"', 7);
			return t;

		case '\'':
			lp++;
			t.type = Word;
			t.w = character('\'', 6);
			return t;

		case ';':
			while(*lp)
				lp++;
			t.type = Eol;
			return t;

		case '^':
			lp++;
			if(*lp == 'D')
				t.type = Radix10;
			else if(*lp == 'O')
				t.type = Radix8;
			else if(*lp == 'B')
				t.type = Radix2;
			else if(*lp == 'F'){
				lp--;
				return symnum();
			}else{
				err(1, "error: unknown Radix ^%c\n", *lp);
				t.type = Radix8;
			}
			lp++;
			return t;

		case Unused:
			err(0, "warning: unknown char %c", *lp);
			/* fallthrough */
		case Ignore:
			lp++;
			break;

		default:
			t.type = ctab[*lp++];
			return t;
		}
	t.type = Eol;
	return t;
}

/* Expressions */

Expr*
newexpr(int type)
{
	Expr *e;
	e = malloc(sizeof(Expr));
	memset(e, 0, sizeof(Expr));
	e->type = type;
	return e;
}

void
freeexpr(Expr *e)
{
	if(e->type >= Xunop)
		freeexpr(e->r);
	if(e->type == Xbiop)
		freeexpr(e->l);
	free(e);
}

int
isop(int op)
{
	static int prectab[] = {
		0, 0,	/* Plus, Minus */
		1, 1,	/* Star, Slash */
		2, 2,	/* Exclam, Amper */
		3,	/* Shift */
	};
	if(op >= Plus && op <= Shift)
		return prectab[op-Plus];
	return -1;
}

/* Evaluate an expression AST, also record an additive external symbol.
 * If pass is 1 or 2, the expression must be defined in pass 1 or 2 resp. */
Value
eval(Expr *e, Sym **ext, int pass)
{
	int shft;
	Value v, v2;
	Sym *ex1, *ex2;
	char name[8];

	switch(e->type){
	case Xval:
		return e->v;

	case Xsym:
		if(!pass2 && pass == 1 && (e->s->type & Def) == 0 ||
		    pass2 && pass == 2 && e->s->type & Extern){
			unsixbit(e->s->name, name);
			err(1, "error: symbol %s must be defined", name);
			return (Value){ 0, 0 };
		}
		if(e->s->type & Extern){
			*ext = e->s;
			return (Value){ 0, 0 };
		}
		return e->s->v;

	case Xunop:
		if(e->op == Minus){
			v = eval(e->r, ext, pass);
			if(*ext)
				err(1, "error: Invalid use of external");
			v.val = negw(v.val);
			return v;
		}
		panic("expression operator");

	case Xbiop:
		ex1 = nil;
		ex2 = nil;
		v = eval(e->l, &ex1, pass);
		v2 = eval(e->r, &ex2, pass);
		if(ex1 == nil)
			*ext = ex2;
		else if(ex2 == nil)
			*ext = ex1;
		else
			err(1, "error: Invalid use of external");

		shft = v2.val;
		if(isneg(v2.val))
			shft = -negw(v2.val);

		switch(e->op){
		case Shift:
			if(shft < 0)
				v.val >>= -shft;
			else
				v.val <<= shft;
			break;
		case Exclam:
			v.val |= v2.val;
			break;
		case Amper:
			v.val &= v2.val;
			break;
		case Star:
			v.val *= v2.val;
			break;
		case Slash:
			v.val /= v2.val;
			break;
		case Plus:
			v.val += v2.val;
			break;

		case Minus:
			v.val -= v2.val;
			if(v.rel == v2.rel)
				v.rel = 0;
			goto ret;
		default:
			panic("expression operator");
		}
		v.rel += v2.rel;
		if(v.rel > 1){
			err(1, "error: invalid expression type");
			return (Value){ 0, 0 };
		}
	ret:
		v.val &= 0777777777777;
		return v;
	}
	panic("Doesn't happen\n");
}

/* Build an expression AST */
Expr*
parseexpr(int i)
{
	Expr *e, *e2;
	Token t;
	Sym *s;
	int oldradix;
	Litword *litsav;

	t = token();
	if(t.type == Eol || t.type == ','){
		peekt = t;
		e = newexpr(Xval);
		e->v = (Value){ 0, 0 };
		return e;
	}

	/* unary and atoms */
	if(i == 4){
		if(t.type == Word){
			e = newexpr(Xval);
			e->v.val = t.w;
			e->v.rel = 0;
		}else if(t.type == Symbol){
			e = newexpr(Xsym);
			e->s = t.s;
		}else if(t.type == Minus){
			e = newexpr(Xunop);
			e->op = Minus;
			e->r = parseexpr(i);
		}else if(t.type == Radix10 || t.type == Radix8 || t.type == Radix2){
			oldradix = radix;
			if(t.type == Radix10)
				radix = 10;
			else if(t.type == Radix8)
				radix = 8;
			else if(t.type == Radix2)
				radix = 2;
			e = parseexpr(i);
			radix = oldradix;
		}else if(t.type == '<'){
			e = parseexpr(0);
			t = token();
			if(t.type != '>'){
				err(1, "error: '>' expected");
				peekt = t;
			}
		}else if(t.type == '['){
			litlevel++;
			litsav = curlit;
			curlit = nil;

			// TODO: more than one line
			statement();

			t = token();
			if(t.type != ']'){
				err(1, "error: ']' expected");
				peekt = t;
			}
			e = newexpr(Xval);
			e->v = addlit(curlit);

			curlit = litsav;
			litlevel--;
		}else{
			err(1, "error: expression syntax %o", t.type);
			return nil;
		}
		return e;
	}

	/* n-ary expressions */
	if(t.type == Word || t.type == Symbol ||
	   t.type == Minus || t.type == '<' || t.type == '[' ||
	   t.type == Radix10 || t.type == Radix8 || t.type == Radix2 ){
		peekt = t;
		e = parseexpr(i+1);
	}else{
		err(1, "error: expression syntax %o", t.type);
		return nil;
	}

	for(t = token(); isop(t.type) == i; t = token()){
		e2 = newexpr(Xbiop);
		e2->op = t.type;
		e2->l = e;
		e2->r = parseexpr(i+1);
		e = e2;
	}
	peekt = t;
	return e;
}

void
printexpr(Expr *e)
{
	char name[8];
	char *opnames[] = {
		"+", "-", "*", "/", "!", "&", "_", "^D", "^O", "^B"
	};
	if(e->type == Xval)
		printf("%lo %d", e->v.val, e->v.rel);
	else if(e->type == Xsym){
		unsixbit(e->s->name, name);
		printf("%s", name);
	}else if(e->type == Xunop){
		printf("(%s ", opnames[e->op - Plus]);
		printexpr(e->r);
		printf(")");
	}else if(e->type == Xbiop){
		printf("(");
		printexpr(e->l);
		printf(" %s ", opnames[e->op - Plus]);
		printexpr(e->r);
		printf(")");
	}
}

/* Parse and evaluate an expression */
Value
expr(int pass, Sym **s)
{
	/* -			5
	 * ^D ^O ^B ^L		4
	 * B _			3
	 * logical	! &	2
	 * mul/div	* /	1
	 * add/sub	+ -	0
	 * <>
	 */

	Expr *e;
	Sym *ext;
	Value v;

	ext = nil;
	e = parseexpr(0);
	v = eval(e, &ext, pass);
	if(s)
		*s = ext;
	freeexpr(e);
	return v;
}

/* Statements */

/* split operands separated by ',' starting at lp; remove whitespace */
void
splitop(void)
{
	int nparn;

	numops = 0;
	skipwhite();
	if(*lp == ';' || *lp == '\0')
		return;

	/* this routine is terrible, null everything
	 * we don't care about and drag along pointers
	 * to the beginnig of operands */
	ops[numops++] = lp;
	for(; *lp && *lp != ';'; lp++){
		if(isspace(*lp))
			*lp = '\0';
		else if(*lp == ','){
			*lp = '\0';
			ops[numops++] = lp;
			if(numops >= MAXOPND){
				err(0, "warning: operand overflow");
				return;
			}
		}else if(*lp == '('){
			/* read parenthesized operands as one */
			if(*ops[numops-1] == '\0')
				ops[numops-1] = lp;
			lp++;
			nparn = 1;
			for(; *lp && *lp != ';' && nparn != 0; lp++)
				if(*lp == '(')
					nparn++;
				else if(*lp == ')')
					nparn--;
			lp--;
		}else if(*lp == '['){
			/* same as above */
			if(*ops[numops-1] == '\0')
				ops[numops-1] = lp;
			lp++;
			nparn = 1;
			for(; *lp && *lp != ';' && nparn != 0; lp++)
				if(*lp == '[')
					nparn++;
				else if(*lp == ']')
					nparn--;
			lp--;
		}else{
			if(*ops[numops-1] == '\0')
				ops[numops-1] = lp;
		}
	}
	*lp = '\0';
}

/* parse an effective address of the form @Y(X) */
Value
ea(void)
{
	word w;
	Token t;
	Sym *ext;
	Value x, y;

	w = 0;
	if(*lp == '@'){
		lp++;
		w = 0000020000000;
	}
	ext = nil;
	y = expr(0, &ext);
	updateext(ext, &y, 0);
	if(left(y.val))
		err(0, "warning: address truncated");
	w |= right(y.val);

	t = token();
	if(t.type == '('){
		/* TODO: this is probably a statement */
		x = expr(2, nil);
		if(t = token(), t.type != ')'){
			err(1, "error: ')' expected");
			peekt = t;
		}
		if(x.rel)
			err(0, "warning: index relocation ignored");
		w |= fw(right(x.val) & 017, left(x.val));
	}else
		peekt = t;
	return (Value){ w, y.rel };
}

/* Primary and IO instruction statement */
void
opline(word w, int io)
{
	char *acp, *ep;
	Sym *ext;
	Value ac, x, y;
	Token t;
	int i;

	splitop();
	/*
	 * OP AC,
	 * OP   ,E
	 * OP    E
	 * E: @Y(X)
	 */
	switch(numops){
	case 0:
		acp = "";
		ep = "";
		break;
	case 1:
		acp = "";
		ep = ops[0];
		break;
	default:
		err(0, "warning: too many operands: %d", numops);
	case 2:
		acp = ops[0];
		ep = ops[1];
	}

	lp = acp;
	peekt.type = Unused;
	ac = expr(2, nil);

	i = 0;
	lp = ep;
	peekt.type = Unused;
	if(*lp == '@'){
		lp++;
		i = 1;
	}
	t = token();
	peekt = t;
	if(peekt.type != '('){
		ext = nil;
		y = expr(0, &ext);
		updateext(ext, &y, 0);
	}else
		y = (Value){ 0, 0 };

	x = (Value){ 0, 0 };
	t = token();
	if(t.type == '('){
		/* TODO: this is probably a statement */
		x = expr(2, nil);
		t = token();
		if(t.type != ')')
			err(1, "error: ')' expected");
		x.val = fw(right(x.val), left(x.val));
	}

	/* TODO: is this correct or should we mask out some stuff? */
	if(io){
		w += (ac.val&0774) << 24;
	}else
		w += (ac.val&017) << 23;
	if(ac.rel)
		err(0, "warning: AC relocation ignored");
	if(i)
		w |= 0000020000000;
	w = fw(left(w), right(w)+right(y.val));
	/* TODO: really warn about this? */
//	if(left(y.val))
//		err(0, "warning: address too large");
	w = fw(left(w)+left(x.val), right(w)+right(x.val));
	if(x.rel)
		err(0, "warning: X relocation ignored");
	putv(w, y.rel);
}

/*
 * Pseudo Ops
 */

void
internOp(void)
{
	Sym *s;
	int i;
	if(pass2)
		return;
	splitop();
	for(i = 0; i < numops; i++){
		s = getsym(sixbit(ops[i]));
		if(s->type >= Operator || s->type & Extern)
			err(0, "warning: can't INTERN symbol %s", ops[i]);
		else
			s->type |= Intern;
	}
}

void
externOp(void)
{
	Sym *s;
	int i;
	if(pass2)
		return;
	splitop();
	for(i = 0; i < numops; i++){
		s = getsym(sixbit(ops[i]));
		if(s->type >= Operator || s->type & Intern)
			err(0, "warning: can't EXTERN symbol %s", ops[i]);
		else
			s->type |= Extern;
	}
}

void
title(void)
{
	skipwhite();
	strncpy(progtitle, lp, MAXLINE);
}

void
subttl(void)
{
	skipwhite();
	strncpy(progsubtitle, lp, MAXLINE);
}

void
radixOp(void)
{
	int r;
	Value v;

	skipwhite();
	if(*lp == '\0' || *lp == ';')
		return;
	r = radix;
	radix = 10;
	v = expr(1, nil);
	radix = r;
	if(v.val < 2 || v.val > 10)
		err(1, "error: invalid radix %d", v.val);
	else
		radix = v.val;
}

void
endOp(void)
{
	skipwhite();
	start = expr(2, nil);
	hadstart = 1;
}

void
litOp(void)
{
	Litword *l, *next;
	if(litlist)
		littabs[nlit] = dot->v;
	for(l = litlist; l; l = next){
		next = l->next;
//		printf("L: %012lo %d\n", l->v.val, l->v.rel);
		putv(l->v.val, l->v.rel);
		free(l);
	}
	if(litlist)
		nlit++;
	litlist = nil;
}

void
expOp(void)
{
	Value v;
	Token t;
	Sym *ext;

	do{
		ext = nil;
		v = expr(0, &ext);
		updateext(ext, &v, 0);
		putv(v.val, v.rel);
	}while(t = token(), t.type == ',');
}

void
decOp(void)
{
	int r;
	r = radix;
	radix = 10;
	expOp();
	radix = r;
}

void
octOp(void)
{
	int r;
	r = radix;
	radix = 8;
	expOp();
	radix = r;
}

void
byteOp(void)
{
	Token t;
	word b, w;
	int sz;
	int mask;
	int r;
	int pos;

	w = 0;
	pos = 36;
	sz = 6;
	mask = 077;
	for(;;){
		/* byte size change */
		if(t = token(), t.type == '('){
			r = radix;
			radix = 10;
			sz = expr(0, nil).val;
			if(t = token(), t.type != ')'){
				err(1, "error: ')' expected");
				peekt = t;
			}
			radix = r;
			mask = (1<<sz) - 1;
		}else
			peekt = t;

		/* byte */
		b = expr(2, nil).val & mask;
		pos -= sz;
		if(pos < 0){
			putv(w, 0);
			w = 0;
			pos = 36 - sz;
		}
		w |= b << pos;

		t = token();
		if(t.type == '(')
			peekt = t;
		else if(t.type != ',')
			break;
	}
	putv(w, 0);
}

void
xwd(void)
{
	Token t;
	Value l, r;
	Sym *exl, *exr;

	exl = nil;
	exr = nil;
	l = (Value){ 0, 0 };
	r = (Value){ 0, 0 };

	l = expr(0, &exl);
	updateext(exl, &l, 1);
	if(t = token(), t.type == ','){
		r = expr(0, &exr);
		updateext(exr, &r, 0);
	}else
		err(0, "warning: right operand missing");

	putv(fw(right(l.val), right(r.val)), (l.rel<<1) | r.rel);
}

void
iowd(void)
{
	Token t;
	Value l, r;
	Sym *exl, *exr;

	exl = nil;
	exr = nil;
	l = (Value){ 0, 0 };
	r = (Value){ 0, 0 };

	l = expr(2, &exl);
	if(t = token(), t.type == ','){
		r = expr(0, &exr);
		updateext(exr, &r, 0);
	}else
		err(0, "warning: right operand missing");

	putv(fw(right(-l.val), right(r.val-1)), (l.rel<<1) | r.rel);
}

void
pointOp(void)
{
	Value v;
	Token t;
	word sz, pos;
	int r;

	peekt.type = Unused;

	r = radix;
	radix = 10;
	sz = expr(2, nil).val & 077;
	radix = r;

	if(t = token(), t.type != ','){
		peekt = t;
		err(2, "error: ',' expected");
	}

	v = ea();

	pos = 044;
	if(t = token(), t.type == ','){
		r = radix;
		radix = 10;
		pos = expr(2, nil).val & 077;
		radix = r;
	}else
		peekt = t;

	v.val |= sz<<24 | pos<<30;
	putv(v.val, v.rel);
}

int asciiz;

void
ascii(void)
{
	char delim;
	word w;
	int i;

	skipwhite();
	delim = *lp++;
	if(delim == ';' || delim == '\0'){
		putv(0, 0);
		return;
	}
	w = 0;
	i = 0;
	for(; *lp != delim; lp++){
		/* This is a hack since LF is not part of the line
		 * but we have to output it in multiline strings */
		if(*lp == '\0')
			*lp = '\n';

		w = (w<<7) | *lp;
		if(++i == 5){
			w <<= 1;
			putv(w, 0);
			i = 0;
			w = 0;
		}

		/* load the next line when we recognized EOL earlier */
		if(*lp == '\n'){
			lp = getln();
			if(lp == nil){
				err(1, "error: unexpected EOF");
				break;
			}
			lp--;
		}
	}
	if(asciiz){
		i++;
		w <<= 7;
	}
	w <<= 36-i*7;
	if(w || asciiz)
		putv(w, 0);
}

void
asciz(void)
{
	asciiz = 1;
	ascii();
	asciiz = 0;
}

void
sixbitOp(void)
{
	char delim;
	char c;
	word w;
	int i;

	skipwhite();
	delim = *lp++;
	if(delim == ';' || delim == '\0'){
		putv(0, 0);
		return;
	}
	w = 0;
	i = 0;
	for(; *lp && *lp != delim; lp++){
		c = ascii2sixbit(*lp);
		if(c < 0){
			err(0, "warning: ignoring non-SIXBIT char %c", *lp);
			continue;
		}
		w = (w<<6) | c;
		if(++i == 6){
			putv(w, 0);
			i = 0;
			w = 0;
		}
	}
	if(*lp != delim)
		err(1, "error: '%c' expected", delim);
	w <<= 36-i*6;
	if(w)
		putv(w, 0);
}

void
blockOp(void)
{
	Value v;
	skipwhite();
	v = expr(1, nil);
	if(v.rel)
		err(1, "error: non-absolute block size");
	else
		dot->v.val += v.val;
}

void
locOp(void)
{
	skipwhite();
	if(dot->v.rel) reldot = dot->v;
	else absdot = dot->v;
	if(*lp && *lp != ';'){
		dot->v = expr(1, nil);
		dot->v.val &= 0777777;
		dot->v.rel = 0;
	}else
		dot->v = absdot;
}

void
relocOp(void)
{
	skipwhite();
	if(dot->v.rel) reldot = dot->v;
	else absdot = dot->v;
	if(*lp && *lp != ';'){
		dot->v = expr(1, nil);
		dot->v.val &= 0777777;
		dot->v.rel = 1;
	}else
		dot->v = reldot;
}

void
statement(void)
{
	Token t;
	Value val;
	int type;
	Sym *s, *ext;
	char name[8];

	while(t = token(), t.type != Eol)
		switch(t.type){
		case Symbol:
			s = t.s;
			unsixbit(s->name, name);
			if(*lp == ':'){
				/* label:
				 * :   label
				 * :!  label          hide
				 * ::  label internal
				 * ::! label internal hide
				 */
				lp++;
				type = Label | Local | Def;
				if(*lp == ':'){
					lp++;
					type |= Intern;
				}
				if(*lp == '!'){
					lp++;
					type |= Hide;
				}
				if(!pass2){
					if(s->type & Def){
						err(1, "error: redefinition of %s", name);
						return;
					}
					s->type = (s->type&(Intern|Extern|Hide)) | type;
					s->v = dot->v;
				}
			}else if(*lp == '='){
				/* assignment:
				 * =   assign
				 * ==  assign          hide
				 * =:  assign internal
				 * ==: assign internal hide
				 */
				lp++;
				type = Local | Def;
				if(*lp == '='){
					lp++;
					type |= Hide;
				}
				if(*lp == ':'){
					lp++;
					type |= Intern;
				}
				val = expr(1, nil);
				if(!pass2){
					if(s->type & Label){
						err(1, "error: redefinition of %s", name);
						return;
					}
					s->type = (s->type&(Intern|Extern|Hide)) | type;
					s->v = val;
				}
			}else if(s->type == Operator){
				unsixbit(s->name, name);
				opline(s->v.val, 0);
				return;
			}else if(s->type == IoOperator){
				unsixbit(s->name, name);
				opline(s->v.val, 1);
				return;
			}else if(s->type == Pseudo){
				s->f();
				return;
			}else{
				err(1, "error: unknown operator %s", name);
				return;
			}
			break;

		case ',':
			return;

		case Word:
		case Radix10: case Radix8: case Radix2:
		case Minus:
			peekt = t;
			ext = nil;
			val = expr(0, &ext);
			updateext(ext, &val, 0);
			putv(val.val, val.rel);
			break;

		case ']':
			peekt = t;
			return;

		default:
			err(0, "unknown token %c(%o)", t.type, t.type);
		}
}

void
assemble(void)
{
	while(lp = getln()){
		peekt.type = Unused;
		statement();
	}
}

void
resetdot(void)
{
	dot = getsym(sixbit("."));
	dot->type = Local | Def | Hide;
	absdot = (Value){ 0, 0 };
	reldot = (Value){ 0, 1 };
	dot->v = reldot;
	lastdot = dot->v;
	/* invalidate lastdot */
	lastdot.val = 0777777777777;
}

void
checkundef(int glob)
{
	char name[8];
	Sym *s;
	for(s = symtab; s < &symtab[MAXSYM]; s++)
		if(s->name &&
		   s->type != Operator && s->type != IoOperator && s->type != Pseudo){
			unsixbit(s->name, name);
			if(s->type == Undef){
//				if(glob)
//					s->type |= Extern;
//				else
					err(1, "undefined symbol: %s\n", name);
			}
		}
}

int
symcmp(const void *a, const void *b)
{
	char s1[8], s2[8];
	unsixbit((*(Sym**)a)->name, s1);
	unsixbit((*(Sym**)b)->name, s2);
	return strcmp(s1, s2);
}

void
writesymtab(void)
{
	hword l, r;
	char name[8];
	Sym *s;
	Sym *sortlist[MAXSYM];
	Additive *add;
	word rad;
	int i, nsym;
	int type;

	nsym = 0;
	for(s = symtab; s < &symtab[MAXSYM]; s++){
		if(s->name == 0 || s == dot ||
		   s->type == Operator || s->type == IoOperator || s->type == Pseudo)
			continue;
		sortlist[nsym++] = s;
	}
	qsort(sortlist, nsym, sizeof(Sym*), symcmp);


	startitem(Symbols);
	fprintf(lstfp, "\nSYMBOL TABLE\n\n");
	for(i = 0; i < nsym; i++){
		s = sortlist[i];

		if(s->type & Extern &&
		   s->v.val == 0 && s->v.rel == 0)
			continue;

		type = 0;
		if(s->type & Intern)
			type = SymGlobal;
		else if(s->type & Local)
			type = SymLocal;
		if(s->type & Hide)
			type |= 040;
		if(s->type & Extern)
			type = SymUndef;

		unsixbit(s->name, name);
		l = left(s->v.val);
		r = right(s->v.val);
		fprintf(lstfp, "%s%6s", name, "");
		if(l)
			fprintf(lstfp, "%06o", l);
		else
			fprintf(lstfp, "%6s", "");
		fprintf(lstfp, "%06o%c ", r, s->v.rel ? '\'' : ' ');
		if(s->type & Extern)
			fprintf(lstfp, "EXT");
		if(s->type & Intern)
			fprintf(lstfp, "INT");
		fprintf(lstfp, "\n");

		rad = rad50(type, name);
		putword(rad, 0);
		putword(fw(0, right(s->v.val)), s->v.rel);

		//printf("%s %3o: %012lo %o\n", name, s->type, s->v.val, s->v.rel);
		for(add = s->add; add; add = add->next){
			//printf(" %c %06o\n", "rl"[add->l], right(add->v.val));

			putword(rad, 0);
			putword(fw(0400000 | (add->l ? 0200000 : 0), right(add->v.val)),
			        add->v.rel);
		}
	}
}

/* Basic program flow */

void
cleanup(void)
{
	remove("a.tmp");
}

void
usage(void)
{
	panic("usage: %s", argv0);
}

void initsymtab(void);

int
main(int argc, char *argv[])
{
	int i;
	char *outfile;
	char *lstfile;


	outfile = "a.rel";
	lstfile = "/dev/null";

	ARGBEGIN{
	case 'o':
		outfile = EARGF(usage());
		break;
	case 'l':
		lstfile = EARGF(usage());
		break;
	default:
		usage();
		break;
	}ARGEND;

	nfiles = argc;
	files = argv;

	tmpfp = mustopen("a.tmp", "w");
	atexit(cleanup);

	initsymtab();

	resetdot();
	pass2 = 0;
	if(argc){
		for(i = 0; i < argc; i++){
			filename = argv[i];
			infp = mustopen(filename, "r");
			putc(1, tmpfp);
			fputs(filename, tmpfp);
			putc(0, tmpfp);
			lineno = 0;

			assemble();
			fclose(infp);
		}
	}else{
		filename = "stdin";
		infp = stdin;
		putc(1, tmpfp);
		fputs(filename, tmpfp);
		putc(0, tmpfp);
		lineno = 0;

		assemble();
	}
	litOp();
	putc(3, tmpfp);

	fclose(tmpfp);
	checkundef(1);

	if(error & 1)
		panic("Error\n*****");

	lstfp = mustopen(lstfile, "w");
	relfp = mustopen(outfile, "w");
	tmpfp = mustopen("a.tmp", "r");

	resetdot();
	nlit = 0;


	pass2 = 1;
	radix = 8;
//	printf("\n   PASS2\n\n");

	startitem(Name);
	putword(rad50(0, progtitle), 0);

	assemble();
	litOp();

	writesymtab();

	if(hadstart){
		startitem(Start);
		putword(right(start.val), start.rel);
	}

	startitem(End);
	putword(right(relbreak), 1);
	putword(right(absbreak), 0);
	startitem(Nothing);

	fclose(tmpfp);
	fclose(relfp);
	fclose(lstfp);

	return 0;
}






typedef struct Op Op;
struct Op
{
	const char *name;
	int type;
	word val;
};

typedef struct Ps Ps;
struct Ps
{
	const char *name;
	void (*f)(void);
};

extern Op oplist[];
extern Ps pslist[];
void
initsymtab(void)
{
	Op *op;
	Ps *ps;
	Sym *s;

	for(op = oplist; op->name[0]; op++){
		s = getsym(sixbit(op->name));
		s->type = op->type;
		s->v.val = op->val;
		s->v.rel = 0;
	}
	for(ps = pslist; ps->name[0]; ps++){
		s = getsym(sixbit(ps->name));
		s->type = Pseudo;
		s->f = ps->f;
	}
}

Ps pslist[] = {
	{ "INTERN",   internOp },
	{ "EXTERN",   externOp },
	{ "TITLE",    title },
	{ "SUBTTL",   subttl },
	{ "RADIX",    radixOp },
	{ "END",      endOp },
	{ "LIT",      litOp },

	{ "EXP",      expOp },
	{ "DEC",      decOp },
	{ "OCT",      octOp },
	{ "BYTE",     byteOp },
	{ "XWD",      xwd },
	{ "IOWD",     iowd },
	{ "POINT",    pointOp },

	{ "ASCII",    ascii },
	{ "ASCIZ",    asciz },
	{ "SIXBIT",   sixbitOp },
	{ "BLOCK",    blockOp },

	{ "LOC",      locOp },
	{ "RELOC",    relocOp },

/*
   RADIX50 SQUOZE
    INTEGER
    ARRAY
   ENTRY
   OPDEF
*/
	{ "", nil }
};

Op oplist[] = {
	{ "Z",   Operator, 0 },

	{ "UUO1",   Operator, 0001000000000 },
	{ "UUO2",   Operator, 0002000000000 },

	{ "FSC",   Operator, 0132000000000 },
	{ "IBP",   Operator, 0133000000000 },
	{ "ILDB",  Operator, 0134000000000 },
	{ "LDB",   Operator, 0135000000000 },
	{ "IDPB",  Operator, 0136000000000 },
	{ "DPB",   Operator, 0137000000000 },

	{ "FAD",   Operator, 0140000000000 },
	{ "FADL",  Operator, 0141000000000 },
	{ "FADM",  Operator, 0142000000000 },
	{ "FADB",  Operator, 0143000000000 },
	{ "FADR",  Operator, 0144000000000 },
	{ "FADRL", Operator, 0145000000000 },
	{ "FADRM", Operator, 0146000000000 },
	{ "FADRB", Operator, 0147000000000 },
	{ "FSB",   Operator, 0150000000000 },
	{ "FSBL",  Operator, 0151000000000 },
	{ "FSBM",  Operator, 0152000000000 },
	{ "FSBB",  Operator, 0153000000000 },
	{ "FSBR",  Operator, 0154000000000 },
	{ "FSBRL", Operator, 0155000000000 },
	{ "FSBRM", Operator, 0156000000000 },
	{ "FSBRB", Operator, 0157000000000 },
	{ "FMP",   Operator, 0160000000000 },
	{ "FMPL",  Operator, 0161000000000 },
	{ "FMPM",  Operator, 0162000000000 },
	{ "FMPB",  Operator, 0163000000000 },
	{ "FMPR",  Operator, 0164000000000 },
	{ "FMPRL", Operator, 0165000000000 },
	{ "FMPRM", Operator, 0166000000000 },
	{ "FMPRB", Operator, 0167000000000 },
	{ "FDV",   Operator, 0170000000000 },
	{ "FDVL",  Operator, 0171000000000 },
	{ "FDVM",  Operator, 0172000000000 },
	{ "FDVB",  Operator, 0173000000000 },
	{ "FDVR",  Operator, 0174000000000 },
	{ "FDVRL", Operator, 0175000000000 },
	{ "FDVRM", Operator, 0176000000000 },
	{ "FDVRB", Operator, 0177000000000 },

	{ "MOVE",  Operator, 0200000000000 },
	{ "MOVEI", Operator, 0201000000000 },
	{ "MOVEM", Operator, 0202000000000 },
	{ "MOVES", Operator, 0203000000000 },
	{ "MOVS",  Operator, 0204000000000 },
	{ "MOVSI", Operator, 0205000000000 },
	{ "MOVSM", Operator, 0206000000000 },
	{ "MOVSS", Operator, 0207000000000 },
	{ "MOVN",  Operator, 0210000000000 },
	{ "MOVNI", Operator, 0211000000000 },
	{ "MOVNM", Operator, 0212000000000 },
	{ "MOVNS", Operator, 0213000000000 },
	{ "MOVM",  Operator, 0214000000000 },
	{ "MOVMI", Operator, 0215000000000 },
	{ "MOVMM", Operator, 0216000000000 },
	{ "MOVMS", Operator, 0217000000000 },

	{ "IMUL",  Operator, 0220000000000 },
	{ "IMULI", Operator, 0221000000000 },
	{ "IMULM", Operator, 0222000000000 },
	{ "IMULB", Operator, 0223000000000 },
	{ "MUL",   Operator, 0224000000000 },
	{ "MULI",  Operator, 0225000000000 },
	{ "MULM",  Operator, 0226000000000 },
	{ "MULB",  Operator, 0227000000000 },
	{ "IDIV",  Operator, 0230000000000 },
	{ "IDIVI", Operator, 0231000000000 },
	{ "IDIVM", Operator, 0232000000000 },
	{ "IDIVB", Operator, 0233000000000 },
	{ "DIV",   Operator, 0234000000000 },
	{ "DIVI",  Operator, 0235000000000 },
	{ "DIVM",  Operator, 0236000000000 },
	{ "DIVB",  Operator, 0237000000000 },

	{ "ASH",   Operator, 0240000000000 },
	{ "ROT",   Operator, 0241000000000 },
	{ "LSH",   Operator, 0242000000000 },
	{ "ASHC",  Operator, 0244000000000 },
	{ "ROTC",  Operator, 0245000000000 },
	{ "LSHC",  Operator, 0246000000000 },

	{ "EXCH",  Operator, 0250000000000 },
	{ "BLT",   Operator, 0251000000000 },
	{ "AOBJP", Operator, 0252000000000 },
	{ "AOBJN", Operator, 0253000000000 },
	{ "JRST",  Operator, 0254000000000 },
	{ "JFCL",  Operator, 0255000000000 },
	{ "XCT",   Operator, 0256000000000 },

	{ "PUSHJ", Operator, 0260000000000 },
	{ "PUSH",  Operator, 0261000000000 },
	{ "POP",   Operator, 0262000000000 },
	{ "POPJ",  Operator, 0263000000000 },
	{ "JSR",   Operator, 0264000000000 },
	{ "JSP",   Operator, 0265000000000 },
	{ "JSA",   Operator, 0266000000000 },
	{ "JRA",   Operator, 0267000000000 },

	{ "ADD",   Operator, 0270000000000 },
	{ "ADDI",  Operator, 0271000000000 },
	{ "ADDM",  Operator, 0272000000000 },
	{ "ADDB",  Operator, 0273000000000 },
	{ "SUB",   Operator, 0274000000000 },
	{ "SUBI",  Operator, 0275000000000 },
	{ "SUBM",  Operator, 0276000000000 },
	{ "SUBB",  Operator, 0277000000000 },

	{ "CAI",   Operator, 0300000000000 },
	{ "CAIL",  Operator, 0301000000000 },
	{ "CAIE",  Operator, 0302000000000 },
	{ "CAILE", Operator, 0303000000000 },
	{ "CAIA",  Operator, 0304000000000 },
	{ "CAIGE", Operator, 0305000000000 },
	{ "CAIN",  Operator, 0306000000000 },
	{ "CAIG",  Operator, 0307000000000 },
	{ "CAM",   Operator, 0310000000000 },
	{ "CAML",  Operator, 0311000000000 },
	{ "CAME",  Operator, 0312000000000 },
	{ "CAMLE", Operator, 0313000000000 },
	{ "CAMA",  Operator, 0314000000000 },
	{ "CAMGE", Operator, 0315000000000 },
	{ "CAMN",  Operator, 0316000000000 },
	{ "CAMG",  Operator, 0317000000000 },
	{ "JUMP",  Operator, 0320000000000 },
	{ "JUMPL", Operator, 0321000000000 },
	{ "JUMPE", Operator, 0322000000000 },
	{ "JUMPLE",Operator, 0323000000000 },
	{ "JUMPA", Operator, 0324000000000 },
	{ "JUMPGE",Operator, 0325000000000 },
	{ "JUMPN", Operator, 0326000000000 },
	{ "JUMPG", Operator, 0327000000000 },
	{ "SKIP",  Operator, 0330000000000 },
	{ "SKIPL", Operator, 0331000000000 },
	{ "SKIPE", Operator, 0332000000000 },
	{ "SKIPLE",Operator, 0333000000000 },
	{ "SKIPA", Operator, 0334000000000 },
	{ "SKIPGE",Operator, 0335000000000 },
	{ "SKIPN", Operator, 0336000000000 },
	{ "SKIPG", Operator, 0337000000000 },
	{ "AOJ",   Operator, 0340000000000 },
	{ "AOJL",  Operator, 0341000000000 },
	{ "AOJE",  Operator, 0342000000000 },
	{ "AOJLE", Operator, 0343000000000 },
	{ "AOJA",  Operator, 0344000000000 },
	{ "AOJGE", Operator, 0345000000000 },
	{ "AOJN",  Operator, 0346000000000 },
	{ "AOJG",  Operator, 0347000000000 },
	{ "AOS",   Operator, 0350000000000 },
	{ "AOSL",  Operator, 0351000000000 },
	{ "AOSE",  Operator, 0352000000000 },
	{ "AOSLE", Operator, 0353000000000 },
	{ "AOSA",  Operator, 0354000000000 },
	{ "AOSGE", Operator, 0355000000000 },
	{ "AOSN",  Operator, 0356000000000 },
	{ "AOSG",  Operator, 0357000000000 },
	{ "SOJ",   Operator, 0360000000000 },
	{ "SOJL",  Operator, 0361000000000 },
	{ "SOJE",  Operator, 0362000000000 },
	{ "SOJLE", Operator, 0363000000000 },
	{ "SOJA",  Operator, 0364000000000 },
	{ "SOJGE", Operator, 0365000000000 },
	{ "SOJN",  Operator, 0366000000000 },
	{ "SOJG",  Operator, 0367000000000 },
	{ "SOS",   Operator, 0370000000000 },
	{ "SOSL",  Operator, 0371000000000 },
	{ "SOSE",  Operator, 0372000000000 },
	{ "SOSLE", Operator, 0373000000000 },
	{ "SOSA",  Operator, 0374000000000 },
	{ "SOSGE", Operator, 0375000000000 },
	{ "SOSN",  Operator, 0376000000000 },
	{ "SOSG",  Operator, 0377000000000 },

	{ "SETZ",  Operator, 0400000000000 },
	{ "SETZI", Operator, 0401000000000 },
	{ "SETZM", Operator, 0402000000000 },
	{ "SETZB", Operator, 0403000000000 },
	{ "AND",   Operator, 0404000000000 },
	{ "ANDI",  Operator, 0405000000000 },
	{ "ANDM",  Operator, 0406000000000 },
	{ "ANDB",  Operator, 0407000000000 },
	{ "ANDCA", Operator, 0410000000000 },
	{ "ANDCAI",Operator, 0411000000000 },
	{ "ANDCAM",Operator, 0412000000000 },
	{ "ANDCAB",Operator, 0413000000000 },
	{ "SETM",  Operator, 0414000000000 },
	{ "SETMI", Operator, 0415000000000 },
	{ "SETMM", Operator, 0416000000000 },
	{ "SETMB", Operator, 0417000000000 },
	{ "ANDCM", Operator, 0420000000000 },
	{ "ANDCMI",Operator, 0421000000000 },
	{ "ANDCMM",Operator, 0422000000000 },
	{ "ANDCMB",Operator, 0423000000000 },
	{ "SETA",  Operator, 0424000000000 },
	{ "SETAI", Operator, 0425000000000 },
	{ "SETAM", Operator, 0426000000000 },
	{ "SETAB", Operator, 0427000000000 },
	{ "XOR",   Operator, 0430000000000 },
	{ "XORI",  Operator, 0431000000000 },
	{ "XORM",  Operator, 0432000000000 },
	{ "XORB",  Operator, 0433000000000 },
	{ "IOR",   Operator, 0434000000000 },
	{ "IORI",  Operator, 0435000000000 },
	{ "IORM",  Operator, 0436000000000 },
	{ "IORB",  Operator, 0437000000000 },
	{ "ANDCB", Operator, 0440000000000 },
	{ "ANDCBI",Operator, 0441000000000 },
	{ "ANDCBM",Operator, 0442000000000 },
	{ "ANDCBB",Operator, 0443000000000 },
	{ "EQV",   Operator, 0444000000000 },
	{ "EQVI",  Operator, 0445000000000 },
	{ "EQVM",  Operator, 0446000000000 },
	{ "EQVB",  Operator, 0447000000000 },
	{ "SETCA", Operator, 0450000000000 },
	{ "SETCAI",Operator, 0451000000000 },
	{ "SETCAM",Operator, 0452000000000 },
	{ "SETCAB",Operator, 0453000000000 },
	{ "ORCA",  Operator, 0454000000000 },
	{ "ORCAI", Operator, 0455000000000 },
	{ "ORCAM", Operator, 0456000000000 },
	{ "ORCAB", Operator, 0457000000000 },
	{ "SETCM", Operator, 0460000000000 },
	{ "SETCMI",Operator, 0461000000000 },
	{ "SETCMM",Operator, 0462000000000 },
	{ "SETCMB",Operator, 0463000000000 },
	{ "ORCM",  Operator, 0464000000000 },
	{ "ORCMI", Operator, 0465000000000 },
	{ "ORCMM", Operator, 0466000000000 },
	{ "ORCMB", Operator, 0467000000000 },
	{ "ORCB",  Operator, 0470000000000 },
	{ "ORCBI", Operator, 0471000000000 },
	{ "ORCBM", Operator, 0472000000000 },
	{ "ORCBB", Operator, 0473000000000 },
	{ "SETO",  Operator, 0474000000000 },
	{ "SETOI", Operator, 0475000000000 },
	{ "SETOM", Operator, 0476000000000 },
	{ "SETOB", Operator, 0477000000000 },

	{ "HLL",   Operator, 0500000000000 },
	{ "HLLI",  Operator, 0501000000000 },
	{ "HLLM",  Operator, 0502000000000 },
	{ "HLLS",  Operator, 0503000000000 },
	{ "HRL",   Operator, 0504000000000 },
	{ "HRLI",  Operator, 0505000000000 },
	{ "HRLM",  Operator, 0506000000000 },
	{ "HRLS",  Operator, 0507000000000 },
	{ "HLLZ",  Operator, 0510000000000 },
	{ "HLLZI", Operator, 0511000000000 },
	{ "HLLZM", Operator, 0512000000000 },
	{ "HLLZS", Operator, 0513000000000 },
	{ "HRLZ",  Operator, 0514000000000 },
	{ "HRLZI", Operator, 0515000000000 },
	{ "HRLZM", Operator, 0516000000000 },
	{ "HRLZS", Operator, 0517000000000 },
	{ "HLLO",  Operator, 0520000000000 },
	{ "HLLOI", Operator, 0521000000000 },
	{ "HLLOM", Operator, 0522000000000 },
	{ "HLLOS", Operator, 0523000000000 },
	{ "HRLO",  Operator, 0524000000000 },
	{ "HRLOI", Operator, 0525000000000 },
	{ "HRLOM", Operator, 0526000000000 },
	{ "HRLOS", Operator, 0527000000000 },
	{ "HLLE",  Operator, 0530000000000 },
	{ "HLLEI", Operator, 0531000000000 },
	{ "HLLEM", Operator, 0532000000000 },
	{ "HLLES", Operator, 0533000000000 },
	{ "HRLE",  Operator, 0534000000000 },
	{ "HRLEI", Operator, 0535000000000 },
	{ "HRLEM", Operator, 0536000000000 },
	{ "HRLES", Operator, 0537000000000 },
	{ "HRR",   Operator, 0540000000000 },
	{ "HRRI",  Operator, 0541000000000 },
	{ "HRRM",  Operator, 0542000000000 },
	{ "HRRS",  Operator, 0543000000000 },
	{ "HLR",   Operator, 0544000000000 },
	{ "HLRI",  Operator, 0545000000000 },
	{ "HLRM",  Operator, 0546000000000 },
	{ "HLRS",  Operator, 0547000000000 },
	{ "HRRZ",  Operator, 0550000000000 },
	{ "HRRZI", Operator, 0551000000000 },
	{ "HRRZM", Operator, 0552000000000 },
	{ "HRRZS", Operator, 0553000000000 },
	{ "HLRZ",  Operator, 0554000000000 },
	{ "HLRZI", Operator, 0555000000000 },
	{ "HLRZM", Operator, 0556000000000 },
	{ "HLRZS", Operator, 0557000000000 },
	{ "HRRO",  Operator, 0560000000000 },
	{ "HRROI", Operator, 0561000000000 },
	{ "HRROM", Operator, 0562000000000 },
	{ "HRROS", Operator, 0563000000000 },
	{ "HLRO",  Operator, 0564000000000 },
	{ "HLROI", Operator, 0565000000000 },
	{ "HLROM", Operator, 0566000000000 },
	{ "HLROS", Operator, 0567000000000 },
	{ "HRRE",  Operator, 0570000000000 },
	{ "HRREI", Operator, 0571000000000 },
	{ "HRREM", Operator, 0572000000000 },
	{ "HRRES", Operator, 0573000000000 },
	{ "HLRE",  Operator, 0574000000000 },
	{ "HLREI", Operator, 0575000000000 },
	{ "HLREM", Operator, 0576000000000 },
	{ "HLRES", Operator, 0577000000000 },

	{ "TRN",   Operator, 0600000000000 },
	{ "TLN",   Operator, 0601000000000 },
	{ "TRNE",  Operator, 0602000000000 },
	{ "TLNE",  Operator, 0603000000000 },
	{ "TRNA",  Operator, 0604000000000 },
	{ "TLNA",  Operator, 0605000000000 },
	{ "TRNN",  Operator, 0606000000000 },
	{ "TLNN",  Operator, 0607000000000 },
	{ "TDN",   Operator, 0610000000000 },
	{ "TSN",   Operator, 0611000000000 },
	{ "TDNE",  Operator, 0612000000000 },
	{ "TSNE",  Operator, 0613000000000 },
	{ "TDNA",  Operator, 0614000000000 },
	{ "TSNA",  Operator, 0615000000000 },
	{ "TDNN",  Operator, 0616000000000 },
	{ "TSNN",  Operator, 0617000000000 },
	{ "TRZ",   Operator, 0620000000000 },
	{ "TLZ",   Operator, 0621000000000 },
	{ "TRZE",  Operator, 0622000000000 },
	{ "TLZE",  Operator, 0623000000000 },
	{ "TRZA",  Operator, 0624000000000 },
	{ "TLZA",  Operator, 0625000000000 },
	{ "TRZN",  Operator, 0626000000000 },
	{ "TLZN",  Operator, 0627000000000 },
	{ "TDZ",   Operator, 0630000000000 },
	{ "TSZ",   Operator, 0631000000000 },
	{ "TDZE",  Operator, 0632000000000 },
	{ "TSZE",  Operator, 0633000000000 },
	{ "TDZA",  Operator, 0634000000000 },
	{ "TSZA",  Operator, 0635000000000 },
	{ "TDZN",  Operator, 0636000000000 },
	{ "TSZN",  Operator, 0637000000000 },
	{ "TRC",   Operator, 0640000000000 },
	{ "TLC",   Operator, 0641000000000 },
	{ "TRCE",  Operator, 0642000000000 },
	{ "TLCE",  Operator, 0643000000000 },
	{ "TRCA",  Operator, 0644000000000 },
	{ "TLCA",  Operator, 0645000000000 },
	{ "TRCN",  Operator, 0646000000000 },
	{ "TLCN",  Operator, 0647000000000 },
	{ "TDC",   Operator, 0650000000000 },
	{ "TSC",   Operator, 0651000000000 },
	{ "TDCE",  Operator, 0652000000000 },
	{ "TSCE",  Operator, 0653000000000 },
	{ "TDCA",  Operator, 0654000000000 },
	{ "TSCA",  Operator, 0655000000000 },
	{ "TDCN",  Operator, 0656000000000 },
	{ "TSCN",  Operator, 0657000000000 },
	{ "TRO",   Operator, 0660000000000 },
	{ "TLO",   Operator, 0661000000000 },
	{ "TROE",  Operator, 0662000000000 },
	{ "TLOE",  Operator, 0663000000000 },
	{ "TROA",  Operator, 0664000000000 },
	{ "TLOA",  Operator, 0665000000000 },
	{ "TRON",  Operator, 0666000000000 },
	{ "TLON",  Operator, 0667000000000 },
	{ "TDO",   Operator, 0670000000000 },
	{ "TSO",   Operator, 0671000000000 },
	{ "TDOE",  Operator, 0672000000000 },
	{ "TSOE",  Operator, 0673000000000 },
	{ "TDOA",  Operator, 0674000000000 },
	{ "TSOA",  Operator, 0675000000000 },
	{ "TDON",  Operator, 0676000000000 },
	{ "TSON",  Operator, 0677000000000 },

	{ "BLKI",  IoOperator, 0700000000000 },
	{ "BLKO",  IoOperator, 0700100000000 },
	{ "DATAI", IoOperator, 0700040000000 },
	{ "DATAO", IoOperator, 0700140000000 },
	{ "CONO",  IoOperator, 0700200000000 },
	{ "CONI",  IoOperator, 0700240000000 },
	{ "CONSZ", IoOperator, 0700300000000 },
	{ "CONSO", IoOperator, 0700340000000 },

	{ "JEN",   Operator, 0254500000000 },	/* JRST 12, */
	{ "HALT",  Operator, 0254200000000 },	/* JRST 4, */
	{ "JRSTF", Operator, 0254100000000 },	/* JRST 2, */
	{ "JOV",   Operator, 0255400000000 },	/* JFCL 10, */
	{ "JCRY0", Operator, 0255200000000 },	/* JFCL 4, */
	{ "JCRY1", Operator, 0255100000000 },	/* JFCL 2, */
	{ "JCRY",  Operator, 0255300000000 },	/* JFCL 6, */
	{ "JPC",   Operator, 0255040000000 },	/* JFCL 1, */
	{ "RSW", IoOperator, 0700040000000 },

	{ "", 0, 0 }
};
