/*
 * Z-80 assembler.
 */
#include <stdio.h>

#define NCPS	8
#define NSYM	256
#define	NBIN	32
#define NTSYM	128
#define NERR	10
#define NINPUT	128
#define NCODE	128
#define NTIT	64

#define NLIST	0
#define SLIST	1
#define ALIST	2
#define CLIST	3

#define B	0
#define C	1
#define D	2
#define E	3
#define H	4
#define L	5
#define M	6
#define A	7
#define V	8
#define R	9

#define BC	0
#define DE	1
#define HL	2
#define SP	3
#define AF	4
#define IX	5
#define IY	6

#define	dot	(&sym[0])
typedef	unsigned addr_t;

struct	sym
{
	char	s_name[NCPS];
	char	s_type;
	char	s_flag;
	addr_t	s_addr;
};

#define S_UND	0
#define S_ABS	1

#define S_OP0	2
#define S_OP1	3
#define S_OP2	4
#define S_OP3	5
#define S_OP4	6
#define S_OP5	7
#define S_OP6	8
#define S_OP7	9
#define S_OP8	10
#define S_OP9	11
#define S_OP10	12
#define S_OP11	13
#define S_OP12	14
#define S_OP13	15
#define S_OP14	16
#define S_OP15	17
#define S_OP16	18

#define S_RPAIR	19
#define S_IC	20
#define S_IPAIR	21
#define S_LIT	22
#define S_INDEX	23
#define S_REG	24

#define	S_WORD	25
#define S_ASCII	26
#define S_ASCIZ	27
#define S_BLKB	28
#define S_PAGE	29
#define S_TITLE	30
#define S_BYTE	31
#define S_HLIST	32
#define S_OLIST	33
#define S_INTEL	34
#define S_MKBUG	35

#define	S_MDF	01		/* Mult. def */
#define S_ASG	02		/* Assigned def */
#define S_TWO	04		/* Two byte opcode */

#define	PCHL	0351
#define JMP	0303
#define DJNZ	0020
#define	INC	0004
#define INX	0003
#define DCX	0013
#define	BITOP	0313
#define XTHL	0343
#define EXAF	0010
#define XCHG	0353
#define IN	0333
#define OUT	0323
#define IN_C	0100
#define OUT_C	0101
#define	ADD	0200
#define ADDX	0011
#define ADC	0210
#define ADCX	0112
#define SBCX	0102
#define MVI	0006
#define MVI_M	0066
#define LXI	0001
#define STA	0062
#define STAX	0002
#define MOV_AV	0107
#define MOV_AR	0117
#define MOV	0100
#define MOV_RM	0160
#define LDA	0072
#define LDAX	0012
#define MOV_VA	0127
#define MOV_RA	0137
#define MOV_MR	0106
#define SPHL	0371
#define SHLD	0042
#define LHLD	0052
#define SRPD	0103
#define LRPD	0113

struct	tsym
{
	int	t_num;
	addr_t	t_addr;
};

struct	tsymp
{
	struct	tsym *t_fp;
	struct	tsym *t_bp;
};

struct	addr
{
	char	a_type;
	char	a_reg;
	addr_t	a_addr;
};

int	line;
int	page;
int	lop;
int	pass;
int	nsym	= 124;
int	ntsym;
int	lflag;
int	nflag;
int	eflag;
addr_t	laddr;
int	lmode;
int	hlist;
int	mkbug;
char	*ep;
char	eb[NERR];
char	*ip;
char	ib[NINPUT];
char	*cp;
char	cb[NCODE];
char	tb[NTIT];
int	bo;
int	ba;
char	bb[NBIN];

extern	struct	sym sym[];

char	imtab[3] = { 0106, 0126, 0136 };

struct	tsym tsym[NTSYM];
struct	tsymp tsymp[10];

FILE	*lfp;
FILE	*ofp;
FILE	*sfp;

main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;
	struct tsym *tp;
	char *fn;

	fn = NULL;
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			++p;
			while (c = *p++)
				switch(c) {

				case 'l':
				case 'L':
					++lflag;
					break;

				case 'n':
				case 'N':
					++nflag;
					break;

				default:
					fprintf(stderr, "%c: ignored.\n", c);
				}
		} else
			fn = p;
	}
	if (fn != NULL) {
		sfp = afile(fn, 0);
		if (lflag)
			lfp = afile("z.l", 1);
		if (!nflag)
			ofp = afile("z.o", 1);
		for (pass=0; pass<2; ++pass) {
			dot->s_type = S_ABS;
			dot->s_flag = S_ASG;
			dot->s_addr = 0;
			line = 0;
			page = 1;
			lop =  60;
			rewind(sfp);
			if (pass == 1) {
				for (i=0; i<10; ++i) {
					tsymp[i].t_fp = NULL;
					tsymp[i].t_bp = NULL;
					tp = tsym;
					while (tp < &tsym[ntsym]) {
						if (tp->t_num == i) {
							tsymp[i].t_fp = tp;
							break;
						}
						++tp;
					}
				}
			}
			while (fgets(ib, sfp) != NULL) {
				++line;
				cp = cb;
				ep = eb;
				ip = ib;
				eflag = 0;
				setexit();
				if (eflag == 0)
					asm();
				if (pass == 1) {
					diag();
					list();
				}
			}
		}
		if (!nflag) {
			bflush();
			beof();
		}
	}
}

afile(fn, wf)
char *fn;
{
	register FILE *fp;

	fp = fopen(fn, wf?"w":"r");
	if (fp == NULL) {
		fprintf(stderr, "%s: cannot open.\n", fn);
		exit(1);
	}
	return (fp);
}

asm()
{
	register c;
	register addr_t a;
	struct sym *sp;
	struct tsym *tp;
	int b, d, op;
	char *p, id[NCPS];
	struct addr a1, a2;

	laddr = dot->s_addr;
	lmode = SLIST;
loop:
	while ((c = getnb()) == ';')
		;
	if (c == 0 || c == '/')
		return;
	if (isdigit(c)) {
		if (getnb() != ':')
			err('q');
		c -= '0';
		if (pass == 0) {
			if (ntsym >= NTSYM)
				error("Too many temp. symbols!\n");
			tsym[ntsym].t_num = c;
			tsym[ntsym].t_addr = dot->s_addr;
			++ntsym;
		}
		tp = tsymp[c].t_fp;
		tsymp[c].t_bp = tp;
		tsymp[c].t_fp = NULL;
		++tp;
		while (tp < &tsymp[ntsym]) {
			if (tp->t_num == c) {
				tsymp[c].t_fp = tp;
				break;
			}
			++tp;
		}
		goto loop;
	}
	if (!isidc(c))
		err('q');
	getid(c, id);
	if ((c = getnb()) == ':') {
		sp = lookup(id, 1);
		if (pass == 0) {
			if (sp->s_type!=S_UND && (sp->s_flag&S_ASG)==0)
				sp->s_flag |= S_MDF;
			sp->s_type = S_ABS;
			sp->s_addr = dot->s_addr;
		} else {
			if ((sp->s_flag&S_MDF) != 0)
				err('m');
			if (sp->s_type!=S_ABS || sp->s_addr!=dot->s_addr)
				err('p');
		}
		lmode = ALIST;
		goto loop;
	}
	if (c == '=') {
		sp = lookup(id, 1);
		if (sp->s_type!=S_UND && (sp->s_flag&S_ASG)==0)
			err('m');
		expr(&a1);
		sp->s_addr = laddr = a1.a_addr;
		sp->s_flag = S_ASG;
		sp->s_type = a1.a_type;
		lmode = ALIST;
		goto loop;
	}
	unget(c);
	lmode = CLIST;
	if ((sp=lookup(id, 0)) == NULL) {
		err('o');
		return;
	}
	op = sp->s_addr;
	switch(sp->s_type) {

	case S_HLIST:
	case S_OLIST:
		hlist = op;
		lmode = SLIST;
		break;

	case S_INTEL:
	case S_MKBUG:
		mkbug = op;
		lmode = SLIST;
		break;

	case S_BYTE:
	case S_WORD:
		do {
			expr(&a1);
			a = a1.a_addr;
			if (sp->s_type == S_BYTE) {
				bcheck(a, 't');
				outb(a);
			} else
				outw(a);
		} while ((c = getnb()) == ',');
		unget(c);
		break;

	case S_ASCII:
	case S_ASCIZ:
		if ((d = getnb()) == 0)
			err('q');
		while ((c = get()) != 0 && c != d)
			outb(c);
		if (c == 0)
			err('q');
		if (sp->s_type == S_ASCIZ)
			outb(0);
		break;

	case S_BLKB:
		expr(&a1);
		a = a1.a_addr;
		if ((c = getnb()) == ',') {
			expr(&a1);
			b = a1.a_addr;
		} else {
			unget(c);
			b = 0;
		}
		while (a--)
			outb(b);
		lmode = ALIST;
		break;

	case S_TITLE:
		p = tb;
		if (c = getnb()) {
			do {
				if (p < &tb[NTIT-1])
					*p++ = c;
			} while (c = get());
		}
		*p = 0;
		unget(c);

	case S_PAGE:
		lop = 60;
		lmode = NLIST;
		break;

	case S_OP0:
		if ((sp->s_flag&S_TWO) != 0)
			outb(op>>8);
		outb(op);
		break;

	case S_OP1:
		expr(&a1);
		if ((a=a1.a_addr) < 3) {
			outb(0355);
			outb(imtab[a]);
			break;
		}
		aerr();
		break;

	case S_OP2:
		expr(&a1);
		if ((a=a1.a_addr) < 8) {
			out3(op, a);
			break;
		}
		aerr();
		break;

	case S_OP3:
	case S_OP4:
	case S_OP5:
	case S_OP15:
		addr(sp->s_type==S_OP5 ? &a2 : &a1);
		comma();
		addr(sp->s_type==S_OP5 ? &a1 : &a2);
		if (sp->s_type == S_OP15) {
			mov(&a1, &a2);
			break;
		}
		if (sp->s_type == S_OP3) {
			if (a1.a_type==S_RPAIR && a2.a_type==S_RPAIR) {
				a = a1.a_addr;
				b = a2.a_addr;
				if ((b!=HL && b!=IX && b!=IY) ||
				   (b!=HL && op!=ADD) ||
				   (a!=BC && a!=DE && a!=SP && a!=b))
					aerr();
				if (a > AF)
					a = HL;
				if (op == ADD) {
					testx(b);
					op = ADDX;
				} else {
					outb(0355);
					op = (op == ADC) ? ADCX : SBCX;
				}
				out4(op, a);
				break;
			}
		}
		if (a2.a_type!=S_REG || a2.a_addr!=A)
			aerr();
		if (a1.a_type == S_INDEX) {
			outx(a1.a_reg);
			outb(op|M);
			outb(a1.a_addr);
			break;
		}
		if (a1.a_type == S_LIT) {
			outb(op|0100|M);
			outb(a1.a_addr);
			break;
		}
		if (a1.a_type == S_REG) {
			outb(op|a1.a_addr);
			break;
		}
		aerr();
		break;

	case S_OP6:
		expr(&a1);
		comma();
		addr(&a2);
		if (a1.a_addr > 7)
			aerr();
		if (a2.a_type == S_INDEX) {
			outx(a2.a_reg);
			outb(BITOP);
			outb(a2.a_addr);
			out3(op|M, a1.a_addr);
			break;
		}
		if (a2.a_type == S_REG) {
			outb(BITOP);
			out3(op|a2.a_addr, a1.a_addr);
			break;
		}
		aerr();
		break;

	case S_OP8:
		addr(&a1);
		if (op == JMP) {
			if (a1.a_type==S_REG && a1.a_addr==M) {
				outb(PCHL);
				break;
			}
			if (a1.a_type==S_INDEX && a1.a_addr==0) {
				outx(a1.a_reg);
				outb(PCHL);
				break;
			}
		}
		if (a1.a_type == S_ABS) {
			outb(op);
			outw(a1.a_addr);
			break;
		}
		aerr();
		break;

	case S_OP9:
		if (op == DJNZ) {
			addr(&a1);
			if (a1.a_type!=S_REG || a1.a_addr!=B)
				aerr();
			comma();
		}
		expr(&a1);
		a = a1.a_addr - dot->s_addr - 2;
		bcheck(a, 'a');
		outb(op);
		outb(a);
		break;

	case S_OP10:
	case S_OP11:
	case S_OP12:
		addr(&a1);
		if (sp->s_type == S_OP10) {
			if (a1.a_type==S_RPAIR && (a=a1.a_addr)!=AF) {
				out4((op==INC)?INX:DCX, testx(a));
				break;
			}
		}
		if (sp->s_type == S_OP11) {
			if (a1.a_type==S_REG && a1.a_addr==A) {
				outb(op|A);
				break;
			}
		}
		if (a1.a_type == S_INDEX) {
			outx(a1.a_reg);
			if ((sp->s_flag&S_TWO) != 0)
				outb(op>>8);
			else
				out3(op, M);
			outb(a1.a_addr);
			if ((sp->s_flag&S_TWO) != 0)
				outb(op|M);
			break;
		}
		if (a1.a_type == S_REG) {
			if ((sp->s_flag&S_TWO) != 0) {
				outb(op>>8);
				outb(op|a1.a_addr);
			} else
				out3(op, a1.a_addr);
			break;
		}
		aerr();
		break;

	case S_OP13:
		addr(&a1);
		if (a1.a_type==S_RPAIR && (a=a1.a_addr)!=SP) {
			if ((a = testx(a)) == AF)
				a = SP;
			out4(op, a);
			break;
		}
		aerr();
		break;

	case S_OP14:
		addr(op==IN ? &a1 : &a2);
		comma();
		addr(op==IN ? &a2 : &a1);
		if (a2.a_type == S_REG) {
			if (a2.a_addr==A && a1.a_type==S_ABS) {
				a = a1.a_addr;
				bcheck(a, 't');
				outb(op);
				outb(a);
				break;
			}
			if (a1.a_type == S_IC) {
				outb(0355);
				out3((op == IN) ? IN_C : OUT_C, a2.a_addr);
				break;
			}
		}
		aerr();
		break;

	case S_OP16:
		addr(&a1);
		comma();
		addr(&a2);
		if (a2.a_type == S_RPAIR) {
			if (a1.a_type==S_IPAIR && a1.a_addr==SP) {
				if (testx(a2.a_addr) == HL) {
					outb(XTHL);
					break;
				}
			} else if (a1.a_type == S_RPAIR) {
				a = a1.a_addr;
				if (a==AF && a2.a_addr==AF) {
					outb(EXAF);
					break;
				}
				if (a==DE && a2.a_addr==HL) {
					outb(XCHG);
					break;
				}
			}
		}
		aerr();
		break;

	default:
		err('o');
	}
	goto loop;
}

mov(s, d)
register struct addr *s, *d;
{
	addr_t as, ad;

	as = s->a_addr;
	ad = d->a_addr;

	/*
	 * Literal source.
	 */
	if (s->a_type == S_LIT) {
		if (d->a_type==S_REG && ad<=A) {
			out3(MVI, ad);
			outb(as);
		} else if (d->a_type == S_INDEX) {
			outx(d->a_reg);
			outb(MVI_M);
			outb(ad);
			outb(as);
		} else if (d->a_type==S_RPAIR && ad!=AF) {
			out4(LXI, testx(ad));
			outw(as);
		} else
			aerr();
		return;
	}

	/*
	 * 8 bit.
	 */
	if (isr8(s)) {
		if (as == A) {
			if (d->a_type == S_ABS) {
				outb(STA);
				outw(ad);
				return;
			}
			if (d->a_type==S_IPAIR && ad<=DE) {
				out4(STAX, ad);
				return;
			}
			if (d->a_type==S_REG && ad>A) {
				outb(0355);
				outb((ad == V) ? MOV_AV : MOV_AR);
				return;
			}
		}
		if (isr8(d)) {
			out3(MOV|as, ad);
			return;
		}
		if (d->a_type == S_INDEX) {
			outx(d->a_reg);
			outb(MOV_RM|as);
			outb(d->a_addr);
			return;
		}
		aerr();
		return;
	}
	if (isr8(d)) {
		if (ad == A) {
			if (s->a_type == S_ABS) {
				outb(LDA);
				outw(as);
				return;
			}
			if (s->a_type==S_IPAIR && as<=DE) {
				out4(LDAX, as);
				return;
			}
			if (s->a_type==S_REG && as>A) {
				outb(0355);
				outb((as == V) ? MOV_VA : MOV_RA);
				return;
			}
		}
		if (s->a_type == S_INDEX) {
			outx(s->a_reg);
			out3(MOV_MR, ad);
			outb(s->a_addr);
			return;
		}
		aerr();
		return;
	}

	/*
	 * 16 bit.
	 */
	if (s->a_type == S_RPAIR) {
		if (d->a_type==S_RPAIR && ad==SP) {
			if ((as = testx(as)) == HL)
				outb(SPHL);
			else
				aerr();
			return;
		}
		if (d->a_type == S_ABS) {
			if ((as = testx(as)) == HL) {
				outb(SHLD);
				outw(ad);
			} else if (as != AF) {
				outb(0355);
				out4(SRPD, as);
				outw(ad);
			} else
				aerr();
			return;
		}
		aerr();
		return;
	}
	if (d->a_type == S_RPAIR) {
		if (s->a_type == S_ABS) {
			if ((ad = testx(ad)) == HL) {
				outb(LHLD);
				outw(as);
			} else if (ad != AF) {
				outb(0355);
				out4(LRPD, ad);
				outw(as);
			} else
				aerr();
			return;
		}
	}
	aerr();
}

isr8(ap)
register struct addr *ap;
{
	return (ap->a_type==S_REG && ap->a_addr<=A);
}

testx(r)
register r;
{
	if (r > AF) {
		outx(r);
		r = HL;
	}
	return (r);
}

addr(ap)
register struct addr *ap;
{
	register c;
	register addr_t a;
	struct sym *sp;

	if ((c=getnb()) == '$') {
		expr(ap);
		ap->a_type = S_LIT;
		return;
	}
	if (c == '(') {
		sp = addr1();
		if (sp->s_type == S_REG) {
			if (sp->s_addr != C)
				err('q');
			ap->a_type = S_IC;
			return;
		}
		if (sp->s_type==S_RPAIR && (a=sp->s_addr)!=AF) {
			if (a == HL) {
				ap->a_type = S_REG;
				ap->a_addr = M;
				return;
			}
			if (a==IX || a==IY) {
				ap->a_type = S_INDEX;
				ap->a_reg = a;
				ap->a_addr = 0;
				return;
			}
			ap->a_type = S_IPAIR;
			ap->a_addr = a;
			return;
		}
		err('q');
	}
	unget(c);
	expr(ap);
	if (ap->a_type != S_ABS)
		return;
	if ((c=getnb()) != '(') {
		unget(c);
		return;
	}
	sp = addr1();
	if (sp->s_type==S_RPAIR && (a=sp->s_addr)!=AF) {
		if (a!=IX && a!=IY) {
			if (ap->a_addr != 0)
				err('q');
			if (a == HL) {
				ap->a_type = S_REG;
				ap->a_addr = M;
				return;
			}
			ap->a_type = S_IPAIR;
			ap->a_addr = a;
			return;
		}
		bcheck(ap->a_addr, 'a');
		ap->a_type = S_INDEX;
		ap->a_reg = a;
	}
}

addr1()
{
	register c;
	register struct sym *sp;
	char id[NCPS];

	if (isidc(c = getnb())) {
		getid(c, id);
		if (getnb()==')' && (sp=lookup(id, 0))!=NULL)
			return (sp);
	}
	err('q');
}

expr(ap)
register struct addr *ap;
{
	register c;
	struct addr right;

	term(ap);
	if (ap->a_type != S_ABS)
		return;
	for (;;) {
		c = getnb();
		if ((c=='<' || c=='>') && c!=getnb())
			err('q');
		if (!any(c, "+-*%&|><")) {
			unget(c);
			break;
		}
		term(&right);
		if (right.a_type != S_ABS)
			err('r');
		switch(c) {

		case '+':
			ap->a_addr += right.a_addr;
			break;

		case '-':
			ap->a_addr -= right.a_addr;
			break;

		case '*':
			ap->a_addr *= right.a_addr;
			break;

		case '%':
			ap->a_addr /= right.a_addr;
			break;

		case '&':
			ap->a_addr &= right.a_addr;
			break;

		case '|':
			ap->a_addr |= right.a_addr;
			break;

		case '>':
			ap->a_addr >>= right.a_addr;
			break;

		case '<':
			ap->a_addr <<= right.a_addr;
		}
	}
}

term(ap)
register struct addr *ap;
{
	register c;
	addr_t dec, oct;
	char id[NCPS];
	struct sym *sp;
	struct tsym *tp;
	int n, r, v;

	c = getnb();
	if (c == '[') {
		expr(ap);
		if (getnb() != ']')
			err('q');
		return;
	}
	if (c == '-' || c == '!') {
		expr(ap);
		if (ap->a_type != S_ABS) {
			err('r');
			ap->a_type = S_ABS;
		}
		if (c == '-')
			ap->a_addr = -ap->a_addr;
		else
			ap->a_addr = ~ap->a_addr;
		return;
	}
	if (isdigit(c)) {
		ap->a_type = S_ABS;
		r = 10;
		if (c == '0') {
			r = 8;
			if ((c = get()) == 'x') {
				r = 16;
				c = get();
			}
		}
		n = 0;
		while ((v = digit(c, r)) >= 0) {
			n = r*n + v;
			c = get();
		}
		if (c=='f' || c=='b') {
			if (n < 10) {
				if (c == 'f')
					tp = tsymp[n].t_fp; else
					tp = tsymp[n].t_bp;
				if (tp != NULL) {
					ap->a_addr = tp->t_addr;
					return;
				}
			}
			err('u');
			ap->a_addr = 0;
			return;
		}
		unget(c);
		ap->a_addr = n;
		return;
	}
	if (isidc(c)) {
		getid(c, id);
		if ((sp = lookup(id, 0)) != NULL) {
			ap->a_type = sp->s_type;
			ap->a_addr = sp->s_addr;
			return;
		}
		err('u');
		ap->a_type = S_ABS;
		ap->a_addr = 0;
		return;
	}
	err('q');
}

digit(c, r)
register c, r;
{
	if (r == 16) {
		if (c >= 'A' && c <= 'F')
			return (c - 'A' + 10);
		if (c >= 'a' && c <= 'f')
			return (c - 'a' + 10);
	}
	if (c >= '0' && c <= '9')
		return (c - '0');
	return (-1);
}

any(c, s)
register c;
register char *s;
{
	register b;

	while (b = *s++)
		if (b == c)
			return (1);
	return (0);
}

list()
{
	register char *wp;
	register nb;

	if (lfp==NULL || lmode==NLIST)
		return;
	slew();
	while (ep < &eb[NERR])
		*ep++ = ' ';
	fprintf(lfp, "%.10s", eb);
	if (lmode == SLIST) {
		fprintf(lfp, "%24s%5d %s\n", "", line, ib);
		return;
	}
	fprintf(lfp, hlist?"   %04x":" %06o", laddr);
	if (lmode == ALIST) {
		fprintf(lfp, "%17s%5d %s\n", "", line, ib);
		return;
	}
	wp = cb;
	nb = cp - cb;
	list1(wp, nb, 1);
	fprintf(lfp, " %5d %s\n", line, ib);
	while ((nb -= 4) > 0) {
		wp += 4;
		slew();
		fprintf(lfp, "%17s", "");
		list1(wp, nb, 0);
		putc('\n', lfp);
	}
}

list1(wp, nb, f)
register char *wp;
register nb;
{
	register i;

	if (nb > 4)
		nb = 4;
	for (i=0; i<nb; ++i)
		fprintf(lfp, hlist?"  %02x":" %03o", (*wp++)&0377);
	if (f)
		while (i < 4) {
			fprintf(lfp, "    ");
			++i;
		}
}

slew()
{
	if (lop++ >= 60) {
		fprintf(lfp, "\fZ-80 Assembler, page %d\n", page++);
		fprintf(lfp, "%s\n\n", tb);
		lop = 4;
	}
}


bcheck(n, f)
register n;
{
	if ((n&0200) != 0)
		n |= ~0377;
	if (n<-128 || n>127)
		err(f);
}

comma()
{
	if (getnb() != ',')
		err('q');
}

getid(c, id)
register c;
char *id;
{
	register char *p;

	p = id;
	do {
		if (p < &id[NCPS])
			*p++ = c;
	} while (isidc(c=get()) || c=='\'' || (c>='0' && c<='9'));
	unget(c);
	while (p < &id[NCPS])
		*p++ = 0;
}

getnb()
{
	register c;

	while ((c=get())==' ' || c=='\t')
		;
	return (c);
}

get()
{
	register c;

	if ((c = *ip) != '\0')
		++ip;
	return (c);
}

unget(c)
{
	if (c != '\0')
		--ip;
}

isdigit(c)
register c;
{
	return (c>='0' && c<='9');
}

isidc(c)
register c;
{
	if (c == '_')
		return (1);
	if (c == '.')
		return (1);
	if (c>='a' && c<='z')
		return (1);
	if (c>='A' && c<='Z')
		return (1);
	return (0);
}

lookup(id, f)
char *id;
{
	register struct sym *sp;
	register char *p1, *p2;

	sp = sym;
	while (sp < &sym[nsym]) {
		p1 = id;
		p2 = sp->s_name;
		while (p1 < &id[NCPS]) {
			if (*p1 != *p2++)
				break;
			++p1;
		}
		if (p1 == &id[NCPS])
			return (sp);
		++sp;
	}
	if (f == 0)
		return (NULL);
	if (nsym++ >= NSYM)
		error("Too many symbols!\n");
	p1 = id;
	p2 = sp->s_name;
	while (p1 < &id[NCPS])
		*p2++ = *p1++;
	sp->s_type = S_UND;
	sp->s_flag = 0;
	sp->s_addr = 0;
	return (sp);
}

aerr()
{
	err('a');
}

err(c)
register c;
{
	register char *p;

	p = eb;
	while (p < ep)
		if (c == *p++)
			return;
	if (p < &eb[NERR]) {
		*p++ = c;
		ep = p;
	}
	if (c == 'q') {
		++eflag;
		reset();
	}
}

diag()
{
	register char *p;

	p = eb;
	while (p < ep)
		printf("%c %04d\n", *p++, line);
}

outx(x)
{
	outb(x==IX ? 0335 : 0375);
}

outw(w)
register w;
{
	outb(w);
	outb(w>>8);
}

outb(b)
register b;
{
	if (pass != 0) {
		if (cp >= &cb[NCODE])
			err('z');
		else
			*cp++ = b;
		if (!nflag) {
			if (bo>=NBIN || ba+bo!=dot->s_addr) {
				bflush();
				ba = dot->s_addr;
				bo = 0;
			}
			bb[bo++] = b;
		}
	}
	++dot->s_addr;
}

bflush()
{
	register b, c, i;

	if (bo == 0)
		return;
	fprintf(ofp, ":%02X%04X00", bo, ba);
	c = bo + ba + (ba>>8);
	for (i=0; i<bo; ++i) {
		b = bb[i]&0377;
		fprintf(ofp, "%02X", b);
		c += b;
	}
	fprintf(ofp, "%02X\n", (-c)&0377);
}

beof()
{
	fprintf(ofp, ":00000000\n");
}

out3(a, b)
{
	outb(a | (b<<3));
}

out4(a, b)
{
	outb(a | (b<<4));
}

/*
 * Opcode table for the
 * Z-80 assembler.
 * This table contains all
 * machine and pseudo ops
 * and some predefined
 * symbols.
 * The symbol `.' must be
 * the first entry.
 */
struct	sym sym[NSYM] = {

	".",		S_ABS,		S_ASG,	0,

	".byte",	S_BYTE,		0,	0,
	".word",	S_WORD,		0,	0,
	".ascii",	S_ASCII,	0,	0,
	".asciz",	S_ASCIZ,	0,	0,
	".blkb",	S_BLKB,		0,	0,
	".page",	S_PAGE,		0,	0,
	".title",	S_TITLE,	0,	0,
	".hlist",	S_HLIST,	0,	1,
	".olist",	S_OLIST,	0,	0,
	".intel",	S_INTEL,	0,	0,
	".mkbug",	S_MKBUG,	0,	1,

	"b",		S_REG,		0,	0,
	"c",		S_REG,		0,	1,
	"d",		S_REG,		0,	2,
	"e",		S_REG,		0,	3,
	"h",		S_REG,		0,	4,
	"l",		S_REG,		0,	5,
	"a",		S_REG,		0,	7,
	"v",		S_REG,		0,	8,
	"r",		S_REG,		0,	9,

	"bc",		S_RPAIR,	0,	0,
	"de",		S_RPAIR,	0,	1,
	"hl",		S_RPAIR,	0,	2,
	"sp",		S_RPAIR,	0,	3,
	"af",		S_RPAIR,	0,	4,
	"af'",		S_RPAIR,	0,	4,
	"ix",		S_RPAIR,	0,	5,
	"iy",		S_RPAIR,	0,	6,

	"cmc",		S_OP0,		0,	0077,
	"cmpd",		S_OP0,		S_TWO,	0166651,
	"cmpdr",	S_OP0,		S_TWO,	0166671,
	"cmpi",		S_OP0,		S_TWO,	0166641,
	"cmpir",	S_OP0,		S_TWO,	0166661,
	"com",		S_OP0,		0,	0057,
	"daa",		S_OP0,		0,	0047,
	"di",		S_OP0,		0,	0363,
	"ei",		S_OP0,		0,	0373,
	"exx",		S_OP0,		0,	0331,
	"halt",		S_OP0,		0,	0166,
	"ind",		S_OP0,		S_TWO,	0166652,
	"indr",		S_OP0,		S_TWO,	0166672,
	"ini",		S_OP0,		S_TWO,	0166642,
	"inir",		S_OP0,		S_TWO,	0166662,
	"movd",		S_OP0,		S_TWO,	0166650,
	"movdr",	S_OP0,		S_TWO,	0166670,
	"movi",		S_OP0,		S_TWO,	0166640,
	"movir",	S_OP0,		S_TWO,	0166660,
	"neg",		S_OP0,		S_TWO,	0166504,
	"nop",		S_OP0,		0,	0000,
	"outd",		S_OP0,		S_TWO,	0166653,
	"outdr",	S_OP0,		S_TWO,	0166673,
	"outi",		S_OP0,		S_TWO,	0166643,
	"outir",	S_OP0,		S_TWO,	0166663,
	"ret",		S_OP0,		0,	0311,
	"rcs",		S_OP0,		0,	0330,
	"rmi",		S_OP0,		0,	0370,
	"rcc",		S_OP0,		0,	0320,
	"rne",		S_OP0,		0,	0300,
	"rpl",		S_OP0,		0,	0360,
	"rpe",		S_OP0,		0,	0350,
	"rpo",		S_OP0,		0,	0340,
	"req",		S_OP0,		0,	0310,
	"reti",		S_OP0,		S_TWO,	0166515,
	"retn",		S_OP0,		S_TWO,	0166505,
	"rrd",		S_OP0,		S_TWO,	0166547,
	"sec",		S_OP0,		0,	0067,

	"im",		S_OP1,		0,	0,

	"rst",		S_OP2,		0,	0307,

	"adc",		S_OP3,		0,	0210,
	"add",		S_OP3,		0,	0200,
	"sbc",		S_OP3,		0,	0230,

	"sub",		S_OP4,		0,	0220,
	"and",		S_OP4,		0,	0240,
	"or",		S_OP4,		0,	0260,
	"xor",		S_OP4,		0,	0250,

	"cmp",		S_OP5,		0,	0270,

	"bit",		S_OP6,		0,	0100,
	"bic",		S_OP6,		0,	0200,
	"bis",		S_OP6,		0,	0300,

	"jmp",		S_OP8,		0,	0303,
	"call",		S_OP8,		0,	0315,
	"ccs",		S_OP8,		0,	0334,
	"cmi",		S_OP8,		0,	0374,
	"ccc",		S_OP8,		0,	0324,
	"cne",		S_OP8,		0,	0304,
	"cpl",		S_OP8,		0,	0364,
	"cpe",		S_OP8,		0,	0354,
	"cpo",		S_OP8,		0,	0344,
	"ceq",		S_OP8,		0,	0314,
	"jcs",		S_OP8,		0,	0332,
	"jmi",		S_OP8,		0,	0372,
	"jcc",		S_OP8,		0,	0322,
	"jne",		S_OP8,		0,	0302,
	"jpl",		S_OP8,		0,	0362,
	"jpe",		S_OP8,		0,	0352,
	"jpo",		S_OP8,		0,	0342,
	"jeq",		S_OP8,		0,	0312,

	"sob",		S_OP9,		0,	0020,
	"br",		S_OP9,		0,	0030,
	"bcs",		S_OP9,		0,	0070,
	"blo",		S_OP9,		0,	0070,
	"bcc",		S_OP9,		0,	0060,
	"bhis",		S_OP9,		0,	0060,
	"bne",		S_OP9,		0,	0040,
	"beq",		S_OP9,		0,	0050,

	"dec",		S_OP10,		0,	0005,
	"inc",		S_OP10,		0,	0004,

	"rol",		S_OP11,		S_TWO,	0145420,
	"rlc",		S_OP11,		S_TWO,	0145400,
	"ror",		S_OP11,		S_TWO,	0145430,
	"rrc",		S_OP11,		S_TWO,	0145410,

	"asl",		S_OP12,		S_TWO,	0145440,
	"asr",		S_OP12,		S_TWO,	0145450,
	"lsr",		S_OP12,		S_TWO,	0145470,

	"pop",		S_OP13,		0,	0301,
	"push",		S_OP13,		0,	0305,

	"in",		S_OP14,		0,	0333,
	"out",		S_OP14,		0,	0323,

	"mov",		S_OP15,		0,	0,

	"ex",		S_OP16,		0,	0
};
