text: // Inferno utils/5a/lex.c
  // http://code.google.com/p/inferno-os/source/browse/utils/5a/lex.c
  //
  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
  //	Portions Copyright © 1997-1999 Vita Nuova Limited
  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
  //	Portions Copyright © 2004,2006 Bruce Ellis
  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
  //	Portions Copyright © 2009 The Go Authors.  All rights reserved.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  
  #define	EXTERN
  #include "a.h"
  #include "y.tab.h"
  #include <ctype.h>
  
  enum
  {
  	Plan9	= 1<<0,
  	Unix	= 1<<1,
  	Windows	= 1<<2,
  };
  
  int
  systemtype(int sys)
  {
  	return sys&Plan9;
  }
  
  void*	alloc(int32);
  void*	allocn(void*, int32, int32);
  
  void
  main(int argc, char *argv[])
  {
  	char *p;
  	int nout, nproc, i, c;
  
  	thechar = '5';
  	thestring = "arm";
  	memset(debug, 0, sizeof(debug));
  	cinit();
  	outfile = 0;
  	include[ninclude++] = ".";
  	ARGBEGIN {
  	default:
  		c = ARGC();
  		if(c >= 0 || c < sizeof(debug))
  			debug[c] = 1;
  		break;
  
  	case 'o':
  		outfile = ARGF();
  		break;
  
  	case 'D':
  		p = ARGF();
  		if(p)
  			Dlist[nDlist++] = p;
  		break;
  
  	case 'I':
  		p = ARGF();
  		setinclude(p);
  		break;
  	case 't':
  		thechar = 't';
  		thestring = "thumb";
  		break;
  	} ARGEND
  	if(*argv == 0) {
  		print("usage: %ca [-options] file.s\n", thechar);
  		errorexit();
  	}
  	if(argc > 1 && systemtype(Windows)){
  		print("can't assemble multiple files on windows\n");
  		errorexit();
  	}
  	if(argc > 1 && !systemtype(Windows)) {
  		nproc = 1;
  		if(p = getenv("NPROC"))
  			nproc = atol(p);	/* */
  		c = 0;
  		nout = 0;
  		for(;;) {
  			Waitmsg *w;
  
  			while(nout < nproc && argc > 0) {
  				i = fork();
  				if(i < 0) {
  					fprint(2, "fork: %r\n");
  					errorexit();
  				}
  				if(i == 0) {
  					print("%s:\n", *argv);
  					if(assemble(*argv))
  						errorexit();
  					exits(0);
  				}
  				nout++;
  				argc--;
  				argv++;
  			}
  			w = wait();
  			if(w == nil) {
  				if(c)
  					errorexit();
  				exits(0);
  			}
  			if(w->msg[0])
  				c++;
  			nout--;
  		}
  	}
  	if(assemble(argv[0]))
  		errorexit();
  	exits(0);
  }
  
  int
  assemble(char *file)
  {
  	char ofile[100], incfile[20], *p;
  	int i, of;
  
  	strcpy(ofile, file);
  	p = utfrrune(ofile, '/');
  	if(p) {
  		include[0] = ofile;
  		*p++ = 0;
  	} else
  		p = ofile;
  	if(outfile == 0) {
  		outfile = p;
  		if(outfile){
  			p = utfrrune(outfile, '.');
  			if(p)
  				if(p[1] == 's' && p[2] == 0)
  					p[0] = 0;
  			p = utfrune(outfile, 0);
  			p[0] = '.';
  			p[1] = thechar;
  			p[2] = 0;
  		} else
  			outfile = "/dev/null";
  	}
  	p = getenv("INCLUDE");
  	if(p) {
  		setinclude(p);
  	} else {
  		if(systemtype(Plan9)) {
  			sprint(incfile,"/%s/include", thestring);
  			setinclude(strdup(incfile));
  		}
  	}
  
  	of = create(outfile, OWRITE, 0664);
  	if(of < 0) {
  		yyerror("%ca: cannot create %s", thechar, outfile);
  		errorexit();
  	}
  	Binit(&obuf, of, OWRITE);
  
  	pass = 1;
  	pinit(file);
  
  	Bprint(&obuf, "%s\n", thestring);
  
  	for(i=0; i<nDlist; i++)
  		dodefine(Dlist[i]);
  	yyparse();
  	if(nerrors) {
  		cclean();
  		return nerrors;
  	}
  
  	Bprint(&obuf, "\n!\n");
  
  	pass = 2;
  	outhist();
  	pinit(file);
  	for(i=0; i<nDlist; i++)
  		dodefine(Dlist[i]);
  	yyparse();
  	cclean();
  	return nerrors;
  }
  
  struct
  {
  	char	*name;
  	ushort	type;
  	ushort	value;
  } itab[] =
  {
  	"SP",		LSP,	D_AUTO,
  	"SB",		LSB,	D_EXTERN,
  	"FP",		LFP,	D_PARAM,
  	"PC",		LPC,	D_BRANCH,
  
  	"R",		LR,	0,
  	"R0",		LREG,	0,
  	"R1",		LREG,	1,
  	"R2",		LREG,	2,
  	"R3",		LREG,	3,
  	"R4",		LREG,	4,
  	"R5",		LREG,	5,
  	"R6",		LREG,	6,
  	"R7",		LREG,	7,
  	"R8",		LREG,	8,
  	"R9",		LREG,	9,
  	"R10",		LREG,	10,
  	"R11",		LREG,	11,
  	"R12",		LREG,	12,
  	"R13",		LREG,	13,
  	"R14",		LREG,	14,
  	"R15",		LREG,	15,
  
  	"F",		LF,	0,
  
  	"F0",		LFREG,	0,
  	"F1",		LFREG,	1,
  	"F2",		LFREG,	2,
  	"F3",		LFREG,	3,
  	"F4",		LFREG,	4,
  	"F5",		LFREG,	5,
  	"F6",		LFREG,	6,
  	"F7",		LFREG,	7,
  	"F8",		LFREG,	8,
  	"F9",		LFREG,	9,
  	"F10",		LFREG,	10,
  	"F11",		LFREG,	11,
  	"F12",		LFREG,	12,
  	"F13",		LFREG,	13,
  	"F14",		LFREG,	14,
  	"F15",		LFREG,	15,
  
  	"C",		LC,	0,
  
  	"C0",		LCREG,	0,
  	"C1",		LCREG,	1,
  	"C2",		LCREG,	2,
  	"C3",		LCREG,	3,
  	"C4",		LCREG,	4,
  	"C5",		LCREG,	5,
  	"C6",		LCREG,	6,
  	"C7",		LCREG,	7,
  	"C8",		LCREG,	8,
  	"C9",		LCREG,	9,
  	"C10",		LCREG,	10,
  	"C11",		LCREG,	11,
  	"C12",		LCREG,	12,
  	"C13",		LCREG,	13,
  	"C14",		LCREG,	14,
  	"C15",		LCREG,	15,
  
  	"CPSR",		LPSR,	0,
  	"SPSR",		LPSR,	1,
  
  	"FPSR",		LFCR,	0,
  	"FPCR",		LFCR,	1,
  
  	".EQ",		LCOND,	0,
  	".NE",		LCOND,	1,
  	".CS",		LCOND,	2,
  	".HS",		LCOND,	2,
  	".CC",		LCOND,	3,
  	".LO",		LCOND,	3,
  	".MI",		LCOND,	4,
  	".PL",		LCOND,	5,
  	".VS",		LCOND,	6,
  	".VC",		LCOND,	7,
  	".HI",		LCOND,	8,
  	".LS",		LCOND,	9,
  	".GE",		LCOND,	10,
  	".LT",		LCOND,	11,
  	".GT",		LCOND,	12,
  	".LE",		LCOND,	13,
  	".AL",		LCOND,	Always,
  
  	".U",		LS,	C_UBIT,
  	".S",		LS,	C_SBIT,
  	".W",		LS,	C_WBIT,
  	".P",		LS,	C_PBIT,
  	".PW",		LS,	C_WBIT|C_PBIT,
  	".WP",		LS,	C_WBIT|C_PBIT,
  
  	".F",		LS,	C_FBIT,
  
  	".IBW",		LS,	C_WBIT|C_PBIT|C_UBIT,
  	".IAW",		LS,	C_WBIT|C_UBIT,
  	".DBW",		LS,	C_WBIT|C_PBIT,
  	".DAW",		LS,	C_WBIT,
  	".IB",		LS,	C_PBIT|C_UBIT,
  	".IA",		LS,	C_UBIT,
  	".DB",		LS,	C_PBIT,
  	".DA",		LS,	0,
  
  	"@",		LAT,	0,
  
  	"AND",		LTYPE1,	AAND,
  	"EOR",		LTYPE1,	AEOR,
  	"SUB",		LTYPE1,	ASUB,
  	"RSB",		LTYPE1,	ARSB,
  	"ADD",		LTYPE1,	AADD,
  	"ADC",		LTYPE1,	AADC,
  	"SBC",		LTYPE1,	ASBC,
  	"RSC",		LTYPE1,	ARSC,
  	"ORR",		LTYPE1,	AORR,
  	"BIC",		LTYPE1,	ABIC,
  
  	"SLL",		LTYPE1,	ASLL,
  	"SRL",		LTYPE1,	ASRL,
  	"SRA",		LTYPE1,	ASRA,
  
  	"MUL",		LTYPE1, AMUL,
  	"MULA",		LTYPEN, AMULA,
  	"DIV",		LTYPE1,	ADIV,
  	"MOD",		LTYPE1,	AMOD,
  
  	"MULL",		LTYPEM, AMULL,
  	"MULAL",	LTYPEM, AMULAL,
  	"MULLU",	LTYPEM, AMULLU,
  	"MULALU",	LTYPEM, AMULALU,
  
  	"MVN",		LTYPE2, AMVN,	/* op2 ignored */
  
  	"MOVB",		LTYPE3, AMOVB,
  	"MOVBU",	LTYPE3, AMOVBU,
  	"MOVH",		LTYPE3, AMOVH,
  	"MOVHU",	LTYPE3, AMOVHU,
  	"MOVW",		LTYPE3, AMOVW,
  
  	"MOVD",		LTYPE3, AMOVD,
  	"MOVDF",		LTYPE3, AMOVDF,
  	"MOVDW",	LTYPE3, AMOVDW,
  	"MOVF",		LTYPE3, AMOVF,
  	"MOVFD",		LTYPE3, AMOVFD,
  	"MOVFW",		LTYPE3, AMOVFW,
  	"MOVWD",	LTYPE3, AMOVWD,
  	"MOVWF",		LTYPE3, AMOVWF,
  
  	"LDREX",		LTYPE3, ALDREX,
  	"STREX",		LTYPE9, ASTREX,
  
  /*
  	"ABSF",		LTYPEI, AABSF,
  	"ABSD",		LTYPEI, AABSD,
  	"NEGF",		LTYPEI, ANEGF,
  	"NEGD",		LTYPEI, ANEGD,
  	"SQTF",		LTYPEI,	ASQTF,
  	"SQTD",		LTYPEI,	ASQTD,
  	"RNDF",		LTYPEI,	ARNDF,
  	"RNDD",		LTYPEI,	ARNDD,
  	"URDF",		LTYPEI,	AURDF,
  	"URDD",		LTYPEI,	AURDD,
  	"NRMF",		LTYPEI,	ANRMF,
  	"NRMD",		LTYPEI,	ANRMD,
  */
  
  	"CMPF",		LTYPEL, ACMPF,
  	"CMPD",		LTYPEL, ACMPD,
  	"ADDF",		LTYPEK,	AADDF,
  	"ADDD",		LTYPEK,	AADDD,
  	"SUBF",		LTYPEK,	ASUBF,
  	"SUBD",		LTYPEK,	ASUBD,
  	"MULF",		LTYPEK,	AMULF,
  	"MULD",		LTYPEK,	AMULD,
  	"DIVF",		LTYPEK,	ADIVF,
  	"DIVD",		LTYPEK,	ADIVD,
  
  	"B",		LTYPE4, AB,
  	"BL",		LTYPE4, ABL,
  	"BX",		LTYPEBX,	ABX,
  
  	"BEQ",		LTYPE5,	ABEQ,
  	"BNE",		LTYPE5,	ABNE,
  	"BCS",		LTYPE5,	ABCS,
  	"BHS",		LTYPE5,	ABHS,
  	"BCC",		LTYPE5,	ABCC,
  	"BLO",		LTYPE5,	ABLO,
  	"BMI",		LTYPE5,	ABMI,
  	"BPL",		LTYPE5,	ABPL,
  	"BVS",		LTYPE5,	ABVS,
  	"BVC",		LTYPE5,	ABVC,
  	"BHI",		LTYPE5,	ABHI,
  	"BLS",		LTYPE5,	ABLS,
  	"BGE",		LTYPE5,	ABGE,
  	"BLT",		LTYPE5,	ABLT,
  	"BGT",		LTYPE5,	ABGT,
  	"BLE",		LTYPE5,	ABLE,
  	"BCASE",	LTYPE5,	ABCASE,
  
  	"SWI",		LTYPE6, ASWI,
  
  	"CMP",		LTYPE7,	ACMP,
  	"TST",		LTYPE7,	ATST,
  	"TEQ",		LTYPE7,	ATEQ,
  	"CMN",		LTYPE7,	ACMN,
  
  	"MOVM",		LTYPE8, AMOVM,
  
  	"SWPBU",	LTYPE9, ASWPBU,
  	"SWPW",		LTYPE9, ASWPW,
  
  	"RET",		LTYPEA, ARET,
  	"RFE",		LTYPEA, ARFE,
  
  	"TEXT",		LTYPEB, ATEXT,
  	"GLOBL",	LTYPEB, AGLOBL,
  	"DATA",		LTYPEC, ADATA,
  	"CASE",		LTYPED, ACASE,
  	"END",		LTYPEE, AEND,
  	"WORD",		LTYPEH, AWORD,
  	"NOP",		LTYPEI, ANOP,
  
  	"MCR",		LTYPEJ, 0,
  	"MRC",		LTYPEJ, 1,
  	0
  };
  
  void
  cinit(void)
  {
  	Sym *s;
  	int i;
  
  	nullgen.sym = S;
  	nullgen.offset = 0;
  	nullgen.type = D_NONE;
  	nullgen.name = D_NONE;
  	nullgen.reg = NREG;
  	if(FPCHIP)
  		nullgen.dval = 0;
  	for(i=0; i<sizeof(nullgen.sval); i++)
  		nullgen.sval[i] = 0;
  
  	nerrors = 0;
  	iostack = I;
  	iofree = I;
  	peekc = IGN;
  	nhunk = 0;
  	for(i=0; i<NHASH; i++)
  		hash[i] = S;
  	for(i=0; itab[i].name; i++) {
  		s = slookup(itab[i].name);
  		s->type = itab[i].type;
  		s->value = itab[i].value;
  	}
  
  	pathname = allocn(pathname, 0, 100);
  	if(getwd(pathname, 99) == 0) {
  		pathname = allocn(pathname, 100, 900);
  		if(getwd(pathname, 999) == 0)
  			strcpy(pathname, "/???");
  	}
  }
  
  void
  syminit(Sym *s)
  {
  
  	s->type = LNAME;
  	s->value = 0;
  }
  
  int
  isreg(Gen *g)
  {
  
  	USED(g);
  	return 1;
  }
  
  void
  cclean(void)
  {
  
  	outcode(AEND, Always, &nullgen, NREG, &nullgen);
  	Bflush(&obuf);
  }
  
  void
  zname(char *n, int t, int s)
  {
  
  	Bputc(&obuf, ANAME);
  	Bputc(&obuf, t);	/* type */
  	Bputc(&obuf, s);	/* sym */
  	while(*n) {
  		Bputc(&obuf, *n);
  		n++;
  	}
  	Bputc(&obuf, 0);
  }
  
  void
  zaddr(Gen *a, int s)
  {
  	int32 l;
  	int i;
  	char *n;
  	Ieee e;
  
  	Bputc(&obuf, a->type);
  	Bputc(&obuf, a->reg);
  	Bputc(&obuf, s);
  	Bputc(&obuf, a->name);
  	switch(a->type) {
  	default:
  		print("unknown type %d\n", a->type);
  		exits("arg");
  
  	case D_NONE:
  	case D_REG:
  	case D_FREG:
  	case D_PSR:
  	case D_FPCR:
  		break;
  
  	case D_REGREG:
  		Bputc(&obuf, a->offset);
  		break;
  
  	case D_OREG:
  	case D_CONST:
  	case D_BRANCH:
  	case D_SHIFT:
  		l = a->offset;
  		Bputc(&obuf, l);
  		Bputc(&obuf, l>>8);
  		Bputc(&obuf, l>>16);
  		Bputc(&obuf, l>>24);
  		break;
  
  	case D_SCONST:
  		n = a->sval;
  		for(i=0; i<NSNAME; i++) {
  			Bputc(&obuf, *n);
  			n++;
  		}
  		break;
  
  	case D_FCONST:
  		ieeedtod(&e, a->dval);
  		Bputc(&obuf, e.l);
  		Bputc(&obuf, e.l>>8);
  		Bputc(&obuf, e.l>>16);
  		Bputc(&obuf, e.l>>24);
  		Bputc(&obuf, e.h);
  		Bputc(&obuf, e.h>>8);
  		Bputc(&obuf, e.h>>16);
  		Bputc(&obuf, e.h>>24);
  		break;
  	}
  }
  
  static int bcode[] =
  {
  	ABEQ,
  	ABNE,
  	ABCS,
  	ABCC,
  	ABMI,
  	ABPL,
  	ABVS,
  	ABVC,
  	ABHI,
  	ABLS,
  	ABGE,
  	ABLT,
  	ABGT,
  	ABLE,
  	AB,
  	ANOP,
  };
  
  void
  outcode(int a, int scond, Gen *g1, int reg, Gen *g2)
  {
  	int sf, st, t;
  	Sym *s;
  
  	/* hack to make B.NE etc. work: turn it into the corresponding conditional */
  	if(a == AB){
  		a = bcode[scond&0xf];
  		scond = (scond & ~0xf) | Always;
  	}
  
  	if(pass == 1)
  		goto out;
  jackpot:
  	sf = 0;
  	s = g1->sym;
  	while(s != S) {
  		sf = s->sym;
  		if(sf < 0 || sf >= NSYM)
  			sf = 0;
  		t = g1->name;
  		if(h[sf].type == t)
  		if(h[sf].sym == s)
  			break;
  		zname(s->name, t, sym);
  		s->sym = sym;
  		h[sym].sym = s;
  		h[sym].type = t;
  		sf = sym;
  		sym++;
  		if(sym >= NSYM)
  			sym = 1;
  		break;
  	}
  	st = 0;
  	s = g2->sym;
  	while(s != S) {
  		st = s->sym;
  		if(st < 0 || st >= NSYM)
  			st = 0;
  		t = g2->name;
  		if(h[st].type == t)
  		if(h[st].sym == s)
  			break;
  		zname(s->name, t, sym);
  		s->sym = sym;
  		h[sym].sym = s;
  		h[sym].type = t;
  		st = sym;
  		sym++;
  		if(sym >= NSYM)
  			sym = 1;
  		if(st == sf)
  			goto jackpot;
  		break;
  	}
  	Bputc(&obuf, a);
  	Bputc(&obuf, scond);
  	Bputc(&obuf, reg);
  	Bputc(&obuf, lineno);
  	Bputc(&obuf, lineno>>8);
  	Bputc(&obuf, lineno>>16);
  	Bputc(&obuf, lineno>>24);
  	zaddr(g1, sf);
  	zaddr(g2, st);
  
  out:
  	if(a != AGLOBL && a != ADATA)
  		pc++;
  }
  
  void
  outhist(void)
  {
  	Gen g;
  	Hist *h;
  	char *p, *q, *op, c;
  	int n;
  
  	g = nullgen;
  	c = '/';
  	for(h = hist; h != H; h = h->link) {
  		p = h->name;
  		op = 0;
  		/* on windows skip drive specifier in pathname */
  		if(systemtype(Windows) && p && p[1] == ':'){
  			p += 2;
  			c = *p;
  		}
  		if(p && p[0] != c && h->offset == 0 && pathname){
  			/* on windows skip drive specifier in pathname */
  			if(systemtype(Windows) && pathname[1] == ':') {
  				op = p;
  				p = pathname+2;
  				c = *p;
  			} else if(pathname[0] == c){
  				op = p;
  				p = pathname;
  			}
  		}
  		while(p) {
  			q = strchr(p, c);
  			if(q) {
  				n = q-p;
  				if(n == 0){
  					n = 1;	/* leading "/" */
  					*p = '/';	/* don't emit "\" on windows */
  				}
  				q++;
  			} else {
  				n = strlen(p);
  				q = 0;
  			}
  			if(n) {
  				Bputc(&obuf, ANAME);
  				Bputc(&obuf, D_FILE);	/* type */
  				Bputc(&obuf, 1);	/* sym */
  				Bputc(&obuf, '<');
  				Bwrite(&obuf, p, n);
  				Bputc(&obuf, 0);
  			}
  			p = q;
  			if(p == 0 && op) {
  				p = op;
  				op = 0;
  			}
  		}
  		g.offset = h->offset;
  
  		Bputc(&obuf, AHISTORY);
  		Bputc(&obuf, Always);
  		Bputc(&obuf, 0);
  		Bputc(&obuf, h->line);
  		Bputc(&obuf, h->line>>8);
  		Bputc(&obuf, h->line>>16);
  		Bputc(&obuf, h->line>>24);
  		zaddr(&nullgen, 0);
  		zaddr(&g, 0);
  	}
  }
  
  #include "../cc/lexbody"
  #include "../cc/macbody"
  