#include <ctype.h>
#define	EXTERN
#include "a.h"
#include "y.tab.h"

void
main(int argc, char *argv[])
{
	char *p;
	int nout, nproc, status, i, c;

	thechar = 'k';
	thestring = "sparc";
	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;
	} 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(;;) {
			while(nout < nproc && argc > 0) {
				i = myfork();
				if(i < 0) {
					i = mywait(&status);
					if(i < 0)
						errorexit();
					if(status)
						c++;
					nout--;
					continue;
				}
				if(i == 0) {
					print("%s:\n", *argv);
					if(assemble(*argv))
						errorexit();
					exits(0);
				}
				nout++;
				argc--;
				argv++;
			}
			i = mywait(&status);
			if(i < 0) {
				if(c)
					errorexit();
				exits(0);
			}
			if(status)
				c++;
			nout--;
		}
	}
	if(assemble(argv[0]))
		errorexit();
	exits(0);
}

int
assemble(char *file)
{
	char *ofile, *p;
	int i, of;

	ofile = strdup(file);
	p = utfrrune(ofile, pathchar());
	if(p) {
		include[0] = ofile;
		*p++ = 0;
	} else
		p = ofile;
	if(outfile == 0) {
		if(p){
			outfile = p;
			p = utfrrune(outfile, '.');
			if(p)
				if(p[1] == 's' && p[2] == 0)
					p[0] = 0;
			outfile = smprint("%s.%C", outfile, thechar);
		} else
			outfile = "/dev/null";
	}
	p = getenv("INCLUDE");
	if(p) {
		setinclude(p);
	} else {
		if(systemtype(Plan9))
			setinclude(smprint("/%s/include", thestring));
	}

	of = mycreat(outfile, 0664);
	if(of < 0) {
		yyerror("%Ca: cannot create %s", thechar, outfile);
		errorexit();
	}
	Binit(&obuf, of, OWRITE);

	pass = 1;
	pinit(file);
	for(i=0; i<nDlist; i++)
		dodefine(Dlist[i]);
	yyparse();
	if(nerrors) {
		cclean();
		return nerrors;
	}

	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,

	"FSR",		LFSR,	D_FSR,
	"CSR",		LFSR,	D_CSR,

	"FQ",		LFPQ,	D_FPQ,
	"CQ",		LFPQ,	D_CPQ,

	"Y",		LPSR,	D_Y,
	"PSR",		LPSR,	D_PSR,
	"WIM",		LPSR,	D_WIM,
	"TBR",		LPSR,	D_TBR,

	"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,
	"R16",		LREG,	16,
	"R17",		LREG,	17,
	"R18",		LREG,	18,
	"R19",		LREG,	19,
	"R20",		LREG,	20,
	"R21",		LREG,	21,
	"R22",		LREG,	22,
	"R23",		LREG,	23,
	"R24",		LREG,	24,
	"R25",		LREG,	25,
	"R26",		LREG,	26,
	"R27",		LREG,	27,
	"R28",		LREG,	28,
	"R29",		LREG,	29,
	"R30",		LREG,	30,
	"R31",		LREG,	31,

	"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,
	"C16",		LCREG,	16,
	"C17",		LCREG,	17,
	"C18",		LCREG,	18,
	"C19",		LCREG,	19,
	"C20",		LCREG,	20,
	"C21",		LCREG,	21,
	"C22",		LCREG,	22,
	"C23",		LCREG,	23,
	"C24",		LCREG,	24,
	"C25",		LCREG,	25,
	"C26",		LCREG,	26,
	"C27",		LCREG,	27,
	"C28",		LCREG,	28,
	"C29",		LCREG,	29,
	"C30",		LCREG,	30,
	"C31",		LCREG,	31,

	"F",		LF,	0,
	"F0",		LFREG,	0,
	"F2",		LFREG,	2,
	"F4",		LFREG,	4,
	"F6",		LFREG,	6,
	"F8",		LFREG,	8,
	"F10",		LFREG,	10,
	"F12",		LFREG,	12,
	"F14",		LFREG,	14,
	"F16",		LFREG,	16,
	"F18",		LFREG,	18,
	"F20",		LFREG,	20,
	"F22",		LFREG,	22,
	"F24",		LFREG,	24,
	"F26",		LFREG,	26,
	"F28",		LFREG,	28,
	"F30",		LFREG,	30,
	"F1",		LFREG,	1,
	"F3",		LFREG,	3,
	"F5",		LFREG,	5,
	"F7",		LFREG,	7,
	"F9",		LFREG,	9,
	"F11",		LFREG,	11,
	"F13",		LFREG,	13,
	"F15",		LFREG,	15,
	"F17",		LFREG,	17,
	"F19",		LFREG,	19,
	"F21",		LFREG,	21,
	"F23",		LFREG,	23,
	"F25",		LFREG,	25,
	"F27",		LFREG,	27,
	"F29",		LFREG,	29,
	"F31",		LFREG,	31,

	"ADD",		LADDW, AADD,
	"ADDCC",	LADDW, AADDCC,
	"ADDX",		LADDW, AADDX,
	"ADDXCC",	LADDW, AADDXCC,
	"AND",		LADDW, AAND,
	"ANDCC",	LADDW, AANDCC,
	"ANDN",		LADDW, AANDN,
	"ANDNCC",	LADDW, AANDNCC,
	"BA",		LBRA, ABA,
	"BCC",		LBRA, ABCC,
	"BCS",		LBRA, ABCS,
	"BE",		LBRA, ABE,
	"BG",		LBRA, ABG,
	"BGE",		LBRA, ABGE,
	"BGU",		LBRA, ABGU,
	"BL",		LBRA, ABL,
	"BLE",		LBRA, ABLE,
	"BLEU",		LBRA, ABLEU,
	"BN",		LBRA, ABN,
	"BNE",		LBRA, ABNE,
	"BNEG",		LBRA, ABNEG,
	"BPOS",		LBRA, ABPOS,
	"BVC",		LBRA, ABVC,
	"BVS",		LBRA, ABVS,
	"CB0",		LBRA, ACB0,
	"CB01",		LBRA, ACB01,
	"CB012",	LBRA, ACB012,
	"CB013",	LBRA, ACB013,
	"CB02",		LBRA, ACB02,
	"CB023",	LBRA, ACB023,
	"CB03",		LBRA, ACB03,
	"CB1",		LBRA, ACB1,
	"CB12",		LBRA, ACB12,
	"CB123",	LBRA, ACB123,
	"CB13",		LBRA, ACB13,
	"CB2",		LBRA, ACB2,
	"CB23",		LBRA, ACB23,
	"CB3",		LBRA, ACB3,
	"CBA",		LBRA, ACBA,
	"CBN",		LBRA, ACBN,
	"CMP",		LCMP, ACMP,
	"CPOP1",	LCPOP, ACPOP1,
	"CPOP2",	LCPOP, ACPOP2,
	"DATA",		LDATA, ADATA,
	"DIV",		LADDW, ADIV,
	"DIVL",		LADDW, ADIVL,
	"END",		LEND, AEND,
	"FABSD",	LFCONV, AFABSD,
	"FABSF",	LFCONV, AFABSF,
	"FABSX",	LFCONV, AFABSX,
	"FADDD",	LFADD, AFADDD,
	"FADDF",	LFADD, AFADDF,
	"FADDX",	LFADD, AFADDX,
	"FBA",		LBRA, AFBA,
	"FBE",		LBRA, AFBE,
	"FBG",		LBRA, AFBG,
	"FBGE",		LBRA, AFBGE,
	"FBL",		LBRA, AFBL,
	"FBLE",		LBRA, AFBLE,
	"FBLG",		LBRA, AFBLG,
	"FBN",		LBRA, AFBN,
	"FBNE",		LBRA, AFBNE,
	"FBO",		LBRA, AFBO,
	"FBU",		LBRA, AFBU,
	"FBUE",		LBRA, AFBUE,
	"FBUG",		LBRA, AFBUG,
	"FBUGE",	LBRA, AFBUGE,
	"FBUL",		LBRA, AFBUL,
	"FBULE",	LBRA, AFBULE,
	"FCMPD",	LFADD, AFCMPD,
	"FCMPED",	LFADD, AFCMPED,
	"FCMPEF",	LFADD, AFCMPEF,
	"FCMPEX",	LFADD, AFCMPEX,
	"FCMPF",	LFADD, AFCMPF,
	"FCMPX",	LFADD, AFCMPX,
	"FDIVD",	LFADD, AFDIVD,
	"FDIVF",	LFADD, AFDIVF,
	"FDIVX",	LFADD, AFDIVX,
	"FMOVD",	LFMOV, AFMOVD,
	"FMOVDF",	LFCONV, AFMOVDF,
	"FMOVDW",	LFCONV, AFMOVDW,
	"FMOVDX",	LFCONV, AFMOVDX,
	"FMOVF",	LFMOV, AFMOVF,
	"FMOVFD",	LFCONV, AFMOVFD,
	"FMOVFW",	LFCONV, AFMOVFW,
	"FMOVFX",	LFCONV, AFMOVFX,
	"FMOVWD",	LFCONV, AFMOVWD,
	"FMOVWF",	LFCONV, AFMOVWF,
	"FMOVWX",	LFCONV, AFMOVWX,
	"FMOVX",	LFCONV, AFMOVX,
	"FMOVXD",	LFCONV, AFMOVXD,
	"FMOVXF",	LFCONV, AFMOVXF,
	"FMOVXW",	LFCONV, AFMOVXW,
	"FMULD",	LFADD, AFMULD,
	"FMULF",	LFADD, AFMULF,
	"FMULX",	LFADD, AFMULX,
	"FNEGD",	LFCONV, AFNEGD,
	"FNEGF",	LFCONV, AFNEGF,
	"FNEGX",	LFCONV, AFNEGX,
	"FSQRTD",	LFCONV, AFSQRTD,
	"FSQRTF",	LFCONV, AFSQRTF,
	"FSQRTX",	LFCONV, AFSQRTX,
	"FSUBD",	LFADD, AFSUBD,
	"FSUBF",	LFADD, AFSUBF,
	"FSUBX",	LFADD, AFSUBX,
	"GLOBL",	LTEXT, AGLOBL,
	"IFLUSH",	LFLUSH, AIFLUSH,
	"JMPL",		LJMPL, AJMPL,
	"JMP",		LJMPL, AJMP,
	"MOD",		LADDW, AMOD,
	"MODL",		LADDW, AMODL,
	"MOVB",		LMOVB, AMOVB,
	"MOVBU",	LMOVB, AMOVBU,
	"MOVD",		LMOVD, AMOVD,
	"MOVH",		LMOVB, AMOVH,
	"MOVHU",	LMOVB, AMOVHU,
	"MOVW",		LMOVW, AMOVW,
	"MUL",		LADDW, AMUL,
	"MULSCC",	LADDW, AMULSCC,
	"NOP",		LNOP, ANOP,
	"OR",		LADDW, AOR,
	"ORCC",		LADDW, AORCC,
	"ORN",		LADDW, AORN,
	"ORNCC",	LADDW, AORNCC,
	"RESTORE",	LADDW, ARESTORE,
	"RETT",		LRETT, ARETT,
	"RETURN",	LRETRN, ARETURN,
	"SAVE",		LADDW, ASAVE,
	"SLL",		LADDW, ASLL,
	"SRA",		LADDW, ASRA,
	"SRL",		LADDW, ASRL,
	"SUB",		LADDW, ASUB,
	"SUBCC",	LADDW, ASUBCC,
	"SUBX",		LADDW, ASUBX,
	"SUBXCC",	LADDW, ASUBXCC,
	"SWAP",		LSWAP, ASWAP,
	"TA",		LTRAP, ATA,
	"TADDCC",	LADDW, ATADDCC,
	"TADDCCTV",	LADDW, ATADDCCTV,
	"TAS",		LSWAP, ATAS,
	"TCC",		LTRAP, ATCC,
	"TCS",		LTRAP, ATCS,
	"TE",		LTRAP, ATE,
	"TEXT",		LTEXT, ATEXT,
	"TG",		LTRAP, ATG,
	"TGE",		LTRAP, ATGE,
	"TGU",		LTRAP, ATGU,
	"TL",		LTRAP, ATL,
	"TLE",		LTRAP, ATLE,
	"TLEU",		LTRAP, ATLEU,
	"TN",		LTRAP, ATN,
	"TNE",		LTRAP, ATNE,
	"TNEG",		LTRAP, ATNEG,
	"TPOS",		LTRAP, ATPOS,
	"TSUBCC",	LADDW, ATSUBCC,
	"TSUBCCTV",	LADDW, ATSUBCCTV,
	"TVC",		LTRAP, ATVC,
	"TVS",		LTRAP, ATVS,
	"UNIMP",	LUNIMP, AUNIMP,
	"WORD",		LUNIMP, AWORD,
	"XNOR",		LADDW, AXNOR,
	"XNORCC",	LADDW, AXNORCC,
	"XOR",		LXORW, AXOR,
	"XORCC",	LADDW, AXORCC,

	"SCHED",	LSCHED, 0,
	"NOSCHED",	LSCHED, 0x80,

	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;
	nullgen.xreg = 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;
	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(mygetwd(pathname, 99) == 0) {
		pathname = allocn(pathname, 100, 900);
		if(mygetwd(pathname, 999) == 0)
			strcpy(pathname, "/???");
	}
}

void
syminit(Sym *s)
{

	s->type = LNAME;
	s->value = 0;
}

void
cclean(void)
{

	outcode(AEND, &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)
{
	long 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_CREG:
	case D_PREG:
		break;

	case D_OREG:
	case D_ASI:
	case D_CONST:
	case D_BRANCH:
		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;
	}
}

void
outcode(int a, Gen *g1, int reg, Gen *g2)
{
	int sf, st, t;
	Sym *s;

	if(pass == 1)
		goto out;
	if(g1->xreg != NREG) {
		if(reg != NREG || g2->xreg != NREG)
			yyerror("bad addressing modes");
		reg = g1->xreg;
	} else
	if(g2->xreg != NREG) {
		if(reg != NREG)
			yyerror("bad addressing modes");
		reg = g2->xreg;
	}
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, reg|nosched);
	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 = pathchar();
	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, 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"
#include "../cc/compat"
