#define EXTERN extern
#include "mc.h"

int arg_offset = 2;
int int_size = 2;
int disp_offset = 0;
int endian = 1;

code_init()
{
}

gexpr(e1)
int e1;
{int e2,e3;
	if (chk) return;
	e2 = cadr(e1);
	switch (car(e1))
	{case GVAR:
		leaxy(e2);
		return;
	case RGVAR:
		lddy(e2);
		return;
	case CRGVAR:
		ldby(e2);
		sex();
		return;
	case LVAR:
		leaxu(e2);
		return;
	case RLVAR:
		lddu(e2);
		return;
	case CRLVAR:
		ldbu(e2);
		sex();
		return;
	case FNAME:
		leaxpcr((NMTBL *)e2);
		tfrxd();
		return;
	case CONST:
		if (e2) lddim(e2);
		else clrd();
		return;
	case STRING:
		string(e1);
		return;
	case FUNCTION:
		function(e1);
		return;
	case INDIRECT:
		indirect(e1);
		return;
	case RINDIRECT: case CRINDIRECT:
		rindirect(e1);
		return;
	case ADDRESS:
		gexpr(e2);
		tfrxd();
		return;
	case MINUS:
		gexpr(e2);
		printf("\tNEGA\n\tNEGB\n\tSBCA\t#0\n");
		return;
	case BNOT:
		gexpr(e2);
		printf("\tCOMA\n\tCOMB\n");
		return;
	case PREINC:
		switch (car(e2))
		{case GVAR: case LVAR:
			ldd(e2);
			adddim(caddr(e1));
			std(e2);
			return;
		default:
			gexpr(e2);
			lddx();
			adddim(caddr(e1));
			stdx();
			return;
		}
	case POSTINC:
		switch (car(e2))
		{case GVAR: case LVAR:
			ldd(e2);
			adddim(e3 = caddr(e1));
			std(e2);
			subdim(e3);
			return;
		default:
			gexpr(e2);
			lddx();
			adddim(e3=caddr(e1));
			stdx();
			subdim(e3);
			return;
		}
	case CPOSTINC:
		gexpr(e2);
		ldbx();
		incx();
		sex();
		return;
	case CPREINC:
		gexpr(e2);
		incx();
		ldbx();
		sex();
		return;
	case CPOSTDEC:
		gexpr(e2);
		ldbx();
		decx();
		sex();
		return;
	case CPREDEC:
		gexpr(e2);
		decx();
		ldbx();
		sex();
		return;
	case MUL: case UMUL:
		if (car(e3=caddr(e1)) == CONST)
		{	if (0 < (e3 = cadr(e3)) && e3 <= 10)
			{	gexpr(e2);
				switch (e3)
				{case 8:
					asld();
				case 4:
					asld();
				case 2:
					asld();
				case 1:
					return;
				case 10:
					asld();
				case 5:
					pushd();
					asld();
					asld();
					addds();
					return;
				case 6:
					asld();
				case 3:
					pushd();
					asld();
					addds();
					return;
				case 9: case 7:
					pushd();
					asld();
					asld();
					asld();
					if (e3 == 9) addds(); else subds();
					return;
				}
			}
		}
	case DIV:    case UDIV:	   case MOD:	case UMOD:
	case LSHIFT: case ULSHIFT: case RSHIFT: case URSHIFT:
		binexpr(e1);
		return;
	case ADD: case SUB: case BAND: case EOR: case BOR:
		machinop(e1);
		return;
	case COND:
		e2=fwdlabel();
		bexpr(cadr(e1),0,e2);
		gexpr(caddr(e1));
		jmp(e3=fwdlabel());
		fwddef(e2);
		gexpr(cadddr(e1));
		fwddef(e3);
		return;
	case ASS: case CASS:
		assign(e1);
		return;
	case ASSOP: case CASSOP:
		assop(e1);
		return;
	case COMMA:
		gexpr(e2);
		gexpr(caddr(e1));
		return;
	default:
		bexpr(e1,1,e2=fwdlabel());
		clrd();
		printf("\tBRA\t*+5\n");
		fwddef(e2);
		lddim(1);
	}
}

bexpr(e1,cond,l1)
int e1,l1;
char cond;
{int e2,l2;
        if (chk) return;
        e2=cadr(e1);
        switch(car(e1))
        {case LNOT:
                bexpr(e2,!cond,l1);
                return;
        case GT:
                rexpr(e1,l1,cond?"GT":"LE");
                return;
        case UGT:
                rexpr(e1,l1,cond?"HI":"LS");
                return;
        case GE:
                rexpr(e1,l1,cond?"GE":"LT");
                return;
        case UGE:
                rexpr(e1,l1,cond?"HS":"LO");
                return;
        case LT:
                rexpr(e1,l1,cond?"LT":"GE");
                return;
        case ULT:
                rexpr(e1,l1,cond?"LO":"HS");
                return;
        case LE:
                rexpr(e1,l1,cond?"LE":"GT");
                return;
        case ULE:
                rexpr(e1,l1,cond?"LS":"HI");
                return;
        case EQ:
                rexpr(e1,l1,cond?"EQ":"NE");
                return;
        case NEQ:
                rexpr(e1,l1,cond?"NE":"EQ");
                return;
        case LAND:
                bexpr(e2,0,cond?(l2=fwdlabel()):l1);
                bexpr(caddr(e1),cond,l1);
                if(cond) fwddef(l2);
                return;
        case LOR:
                bexpr(e2,1,cond?l1:(l2=fwdlabel()));
                bexpr(caddr(e1),cond,l1);
                if(!cond) fwddef(l2);
                return;
        case CRGVAR:
                ldby(e2);
                jcond(l1,cond);
                return;
        case CRLVAR:
                ldbu(e2);
                jcond(l1,cond);
                return;
        case CONST:
                if(cond&&e2||!cond&&!e2) jmp(l1);
                return;
        case RGVAR:
        case RLVAR:
        case CRINDIRECT:
                gexpr(e1);
                jcond(l1,cond);
                return;
        default:gexpr(e1);
                subdim(0);
                jcond(l1,cond);
                return;
        }
}

string(e1)
int e1;
{char *s;
int i,l,lb;
	s=(char *)cadr(e1);
	lb=fwdlabel();
	if ((l = caddr(e1)) < 128)
		printf("\tLEAX\t2,PC\n\tBRA\t_%d\n",lb);
	else
		printf("\tLEAX\t3,PC\n\tLBRA\t_%d\n",lb);
	do
	{	printf("\tFCB\t%d",*s++);
		for (i=8; --l && --i;) printf(",%d",*s++);
		printf("\n");
	}
	while (l);
	fwddef(lb);
}
function(e1)
int e1;
{int e2,e3,e4,e5,nargs;
NMTBL *n;
	e2 = cadr(e1);
	nargs = 0;
	for (e3 = caddr(e1); e3; e3 = cadr(e3))
	{	n=(NMTBL *)(e5=(cadr(e4 = car(e3))));
		switch(car(e4))
		{case FNAME:
			leaxpcr(n);
			pushx();
			break;
		case ADDRESS:
			gexpr(e5);
			pushx();
			break;
		default:gexpr(e4);
			pushd();
		}
		++nargs;
	}
	if (car(e2) == FNAME)
	{	n=(NMTBL *)cadr(e2);
		printf("\tLBSR\t%s\n",n->nm);
	}
	else
	{	gexpr(e2);
		printf("\tJSR\t,X\n");
	}
	if (nargs) printf("\tLEAS\t%d,S\n",2*nargs);
}
indirect(e1)
int e1;
{int e2,e3,e4;
	e3 = cadr(e2 = cadr(e1));
	switch(car(e2))
	{case RGVAR: case RLVAR:
		ldx(e2);
		return;
	case ADD:
		if(car(e3)==ADDRESS)
		{	gexpr(caddr(e2));
			gexpr(cadr(e3));
			opdx("LEAX");
			return;
		}
		switch(car(e4 = caddr(e2)))
		{case RGVAR: case RLVAR:
			gexpr(e3);
			ldx(e4);
			opdx("LEAX");
			return;
		}
	default:
		gexpr(e2);
		tfrdx();
	}
}

machinop(e1)
int e1;
{int e2,e3;
	e2 = cadr(e1);
	switch (car(e3 = caddr(e1)))
	{case RGVAR: case RLVAR: case CONST:
		gexpr(e2);
		oprt(car(e1),e3);
		return;
	default:
		gexpr(e3);
		pushd();
		gexpr(e2);
		tosop(car(e1));
		return;
	}
}

rindirect(e1)
int e1;
{char *op;
int e2,e3,e4,byte,l;
	op = ((byte = (car(e1) == CRINDIRECT)) ? "LDB" : "LDD");
	e3 = cadr(e2 = cadr(e1));
	switch (car(e2))
	{case RGVAR: case RLVAR:
		indir(op,e2);
		sextend(byte);
		return;
	case ADD:
		if(car(e3)==ADDRESS)
		{	gexpr(caddr(e2));
			gexpr(cadr(e3));
			opdx(op);
			sextend(byte);
			return;
		}
		switch(car(e4=caddr(e2)))
		{case RGVAR: case RLVAR:
			gexpr(e3);
			ldx(e4);
			opdx(op);
			sextend(byte);
			return;
		case CONST:
			switch (car(e3))
			{case RGVAR: case RLVAR:
				ldx(e3);
				indexx(op,cadr(e4));
				sextend(byte);
				return;
			}
		default:
			gexpr(e3);
			pushd();
			gexpr(e4);
			pulx();
			opdx(op);
			sextend(byte);
			return;
		}
	case PREINC:
		if ((l = caddr(e2)) == -1 || l == -2)
			switch (car(e3))
			{case GVAR: case LVAR:
				ldx(e3);
				predecx(op,l);
				stx(e3);
				sextend(byte);
				return;
			}
		break;
	case POSTINC:
		if ((l = caddr(e2)) == 1 || l == 2)
			switch (car(e3))
			{case GVAR: case LVAR:
				ldx(e3);
				postincx(op,l);
				stx(e3);
				sextend(byte);
				return;
			}
		break;
	}
	gexpr(e2);
	tfrdx();
	indexx(op,0);
	sextend(byte);
}
assign(e1)
int e1;
{char *op;
int e2,e3,e4,e5,l;
	op = (car(e1) == CASS ? "STB" : "STD");
	e3 = cadr(e2 = cadr(e1));
	e4 = caddr(e1);
	switch(car(e2))
	{case GVAR: case LVAR:
		gexpr(e4);
		index(op,e2);
		return;
	case INDIRECT:
		switch(car(e3))
		{case RGVAR: case RLVAR:
			gexpr(e4);
			indir(op,e3);
			return;
		case ADD:
			if (car(caddr(e3)) == CONST)
				switch (car(e5=cadr(e3)))
				{case RGVAR: case RLVAR:
					gexpr(e4);
					ldx(e5);
					indexx(op,cadr(caddr(e3)));
					return;
				}
			break;
		case PREINC:
			if ((l = caddr(e3)) == -1 || l == -2)
				switch (car(e5=cadr(e3)))
				{case GVAR: case LVAR:
					gexpr(e4);
					ldx(e5);
					predecx(op,l);
					stx(e5);
					return;
				}
			break;
		case POSTINC:
			if ((l = caddr(e3)) == 1 || l == 2)
				switch (car(e5=cadr(e3)))
				{case GVAR: case LVAR:
					gexpr(e4);
					ldx(e5);
					postincx(op,l);
					stx(e5);
					return;
				}
			break;
		}
	}
	switch (car(e4))
	{case RGVAR: case CRGVAR: case RLVAR: case CRLVAR: case CONST:
		gexpr(e2);
		gexpr(e4);
		break;
	default:
		gexpr(e4);
		pushd();
		gexpr(e2);
		pulld();
	}
	indexx(op,0);
	return;
}
assop(e1)
int e1;
{int e2,e3,byte,op;
char *ldop,*stop;
	ldop = ((byte = (car(e1) == CASSOP)) ? "LDB" : "LDD");
	stop = (byte ? "STB" : "STD");
	e2 = cadr(e1);
	e3 = caddr(e1);
	op = cadddr(e1);
	switch (car(e2))
	{case GVAR: case LVAR:
		switch (car(e3))
		{case RGVAR: case RLVAR: case CONST:
			if (simpop(op))
			{	index(ldop,e2);
				sextend(byte);
				oprt(op,e3);
				index(stop,e2);
				return;
			}
		default:
			gexpr(e3);
			pushd();
			index(ldop,e2);
			sextend(byte);
			tosop(op);
			index(stop,e2);
			return;
		}
	default:
		switch (car(e3))
		{case RGVAR: case RLVAR: case CONST:
			if (simpop(op))
			{	gexpr(e2);
				indexx(ldop,0);
				sextend(byte);
				oprt(op,e3);
				indexx(stop,0);
				return;
			}
		default:
			gexpr(e3);
			pushd();
			gexpr(e2);
			indexx(ldop,0);
			sextend(byte);
			tosop(op);
			indexx(stop,0);
			return;
		}
	}
}
simpop(op)
int op;
{	return (op == ADD || op == SUB ||
		op == BAND || op == EOR || op == BOR);
}
oprt(op,e1)
int op,e1;
{int e2;
	e2 = cadr(e1);
	switch (car(e1))
	{case RGVAR:
		oprt1(op,"Y",e2);
		return;
	case RLVAR:
		oprt1(op,"U",e2);
		return;
	case CONST:
		oprtc(op,e2);
		return;
	}
}
oprt1(op,index,n)
int op,n;
char *index;
{	switch (op)
	{case ADD:
		printf("\tADDD\t%d,%s\n",n,index);
		return;
	case SUB:
		printf("\tSUBD\t%d,%s\n",n,index);
		return;
	case BAND: case EOR: case BOR:
		dualop(op,index,n);
		return;
	}
}
dualop(op,index,n)
int op;
char *index;
int n;
{char *ops;
	ops =  ((op == BAND) ? "AND" :
		(op == EOR)  ? "EOR" :
		(op == BOR)  ? "OR"  : (char *)DEBUG);
	printf("\t%sA\t%d,%s\n\t%sB\t%d+1,%s\n",ops,n,index,ops,n,index);
}

oprtc(op,n)
int op,n;
{	switch (op)
	{case ADD:
		adddim(n);
		return;
	case SUB:
		subdim(n);
		return;
	case BAND: case EOR: case BOR:
		dualc(op,n);
		return;
	}
}
dualc(op,n)
int op;
int n;
{char *ops;
	ops =  ((op == BAND) ? "AND" :
		(op == EOR)  ? "EOR" :
		(op == BOR)  ? "OR"  : (char *)DEBUG);
	printf("\t%sA\t#%d\n\t%sB\t#%d\n",ops,(n >> 8) & 0xff,ops,n & 0xff);
}
tosop(op)
int op;
{	switch (op)
	{case ADD:
		addds();
		return;
	case SUB:
		subds();
		return;
	case BAND: case EOR: case BOR:
		dualtosop(op);
		return;
	default:
		pulx();
		library(op);
	}
}
dualtosop(op)
int op;
{char *ops;
	ops =  ((op == BAND) ? "AND" :
		(op == EOR)  ? "EOR" :
		(op == BOR)  ? "OR"  : (char *)DEBUG);
	printf("\t%sA\t,S+\n\t%sB\t,S+\n",ops,ops);
}
pushd()
{	printf("\tPSHS\tD\n");
}
pushx()
{	printf("\tPSHS\tX\n");
}
pulld()
{	printf("\tPULS\tD\n");
}
pulx()
{	printf("\tPULS\tX\n");
}
tfrdx()
{	printf("\tTFR\tD,X\n");
}
tfrxd()
{	printf("\tTFR\tX,D\n");
}
/*
exgdx()
{	printf("\tEXG\tD,X\n");
}
*/
asld()
{	printf("\tASLB\n\tROLA\n");
}
adddim(n)
{	printf("\tADDD\t#%d\n",n);
}
subdim(n)
{	printf("\tSUBD\t#%d\n",n);
}
cmpdimm(n)
int n;
{	printf("\tCMPD\t#%d\n",n);
}
addds()
{	printf("\tADDD\t,S++\n");
}
subds()
{	printf("\tSUBD\t,S++\n");
}
clrd()
{	printf("\tCLRA\n\tCLRB\n");
}
lddim(n)
int n;
{	printf("\tLDD\t#%d\n",n);
}

ldd(e)
int e;
{	switch (car(e))
	{case GVAR:
		lddy(cadr(e));
		return;
	case LVAR:
		lddu(cadr(e));
		return;
	default:
		DEBUG;
	}
}

lddx()
{	printf("\tLDD\t,X\n");
}
lddy(n)
int n;
{	printf("\tLDD\t%d,Y\n",n);
}
lddu(n)
int n;
{	printf("\tLDD\t%d,U\n",n);
}

std(e)
int e;
{	switch (car(e))
	{case GVAR:
		stdy(cadr(e));
		return;
	case LVAR:
		stdu(cadr(e));
		return;
	default:
		DEBUG;
	}
}
stdx()
{	printf("\tSTD\t,X\n");
}
stdy(n)
int n;
{	printf("\tSTD\t%d,Y\n",n);
}
stdu(n)
int n;
{	printf("\tSTD\t%d,U\n",n);
}

ldbx()
{	printf("\tLDB\t,X\n");
}
/*
stbx()
{	printf("\tSTB\t,X\n");
}
*/
ldby(n)
int n;
{	printf("\tLDB\t%d,Y\n",n);
}
ldbu(n)
int n;
{	printf("\tLDB\t%d,U\n",n);
}
predecx(op,l)
char *op;
int l;
{	printf("\t%s\t,%sX\n",op,(l == -1 ? "-" : "--"));
}
postincx(op,l)
char *op;
int l;
{	printf("\t%s\t,X%s\n",op,(l == 1 ? "+" : "++"));
}
leaxy(n)
int n;
{	printf("\tLEAX\t%d,Y\n",n);
}
leaxu(n)
int n;
{	printf("\tLEAX\t%d,U\n",n);
}
leaxpcr(n)
NMTBL *n;
{	printf("\tLEAX\t%s,PCR\n",n->nm);
}

ldx(e)
int e;
{	switch (car(e))
	{case GVAR: case RGVAR:
		ldxy(cadr(e));
		return;
	case LVAR: case RLVAR:
		ldxu(cadr(e));
		return;
	default:
		DEBUG;
	}
}

ldxy(n)
int n;
{	printf("\tLDX\t%d,Y\n",n);
}
ldxu(n)
int n;
{	printf("\tLDX\t%d,U\n",n);
}
/*
ldxi(n)
int n;
{	printf("\tLDX\t#%d\n",n);
}
*/
stx(e)
int e;
{	switch (car(e))
	{case GVAR:
		stxy(cadr(e));
		return;
	case LVAR:
		stxu(cadr(e));
		return;
	default:
		DEBUG;
	}
}

stxy(n)
int n;
{	printf("\tSTX\t%d,Y\n",n);
}
stxu(n)
int n;
{	printf("\tSTX\t%d,U\n",n);
}

sex()
{	printf("\tSEX\n");
}
incx()
{	printf("\tINC\t,X\n");
}
decx()
{	printf("\tDEC\t,X\n");
}
opdx(op)
char *op;
{	printf("\t%s\tD,X\n",op);
}
indexx(op,n)
char *op;
int n;
{	printf("\t%s\t%d,X\n",op,n);
}

index(op,e)
char *op;
int e;
{	switch (car(e))
	{case GVAR:
		indexy(op,cadr(e));
		return;
	case LVAR:
		indexu(op,cadr(e));
		return;
	default:
		DEBUG;
	}
}

indexy(op,n)
char *op;
int n;
{	printf("\t%s\t%d,Y\n",op,n);
}
indexu(op,n)
char *op;
int n;
{	printf("\t%s\t%d,U\n",op,n);
}


indir(op,e)
char *op;
int e;
{	switch (car(e))
	{case RGVAR:
		indiry(op,cadr(e));
		return;
	case RLVAR:
		indiru(op,cadr(e));
		return;
	default:
		DEBUG;
	}
}

indiry(op,n)
char *op;
int n;
{	printf("\t%s\t[%d,Y]\n",op,n);
}
indiru(op,n)
char *op;
int n;
{	printf("\t%s\t[%d,U]\n",op,n);
}
sextend(byte)
int byte;
{	if (byte) sex();
}
binexpr(e1)
int e1;
{	gexpr(caddr(e1));
	pushd();
	gexpr(cadr(e1));
	pulx();
	library(car(e1));
}
library(op)
int op;
{	printf("\tLBSR\t_0000%d\n",
	       ((op == MUL || op == UMUL) ? 1 :
		(op == DIV)	? 2 :
		(op == UDIV)	? 3 :
		(op == MOD)	? 4 :
		(op == UMOD)	? 5 :
		(op == LSHIFT)	? 6 :
		(op == ULSHIFT) ? 7 :
		(op == RSHIFT)	? 8 :
		(op == URSHIFT) ? 9 : DEBUG));
}
cexpr(e)
int e;
{	if (car(e) != CONST) error(CNERR);
	return (cadr(e));
}

ret()
{       
	char *reg;
        switch(lvar)
        {case 0:
                reg = "";
                return;
        case 1:
                reg = "A,";
                return;
        case 2:
                reg = "X,";
                return;
        case 3:
                reg = "A,X,";
                return;
        case 4:
                reg = "D,X,";
        default:
		printf("\tLEAS\t,U\n");
                reg = "";
        }
	printf("\tPULS\t%sU,PC\n",reg);
}
opening(filename)
char *filename;
{
    printf("\t.file\t\"%s\"\n",filename);
    printf(".text\n");
}
closing()
{
    printf("_%d\tRTS\n_INITIALIZE\tEQU\t_1\n",ilabel);
    printf("_GLOBALS\tEQU\t%u\n\tEND\n",gpc);
}

gen_gdecl(n,gpc) 
    char *n;
    int gpc;
{
    printf("%s\tEQU\t%u\n",n,gpc);
}

jmp_label(l)
    int l;
{
    printf("\tBRA\t_%d\n",l);
}

jmp_eq_label(l)
    int l;
{
     printf("\tBEQ\t_%d\n",l);
}

rexpr(e1,l1,s)
int e1,l1;
char *s;
{       gexpr(list3(SUB,cadr(e1),caddr(e1)));
        printf("\tLB%s\t_%d\n",s,l1);
}
jcond(l,cond)
int l;
char cond;
{       printf("\tLB%s\t_%d\n",cond?"NE":"EQ",l);
}
jmp(l)
int l;
{       control=0;
        printf("\tLBRA\t_%d\n",l);
}
fwdlabel()
{       return labelno++;
}
fwddef(l)
int l;
{       control=1;
        printf("_%d\n",l);
}

backdef()
{       control=1;
        printf("_%d\n",labelno);
        return labelno++;
}

def_label(cslabel,dlabel) 
    int cslabel,dlabel;
{
    if(dlabel) printf("_%d\tEQU\t_%d\n",cslabel,dlabel);
}

gen_comment(s)
    char *s;
{
     printf("* %s",s);
}

gen_source(s)
    char *s;
{
     printf("%s",s);
}

enter(name)
    char *name;
{
        printf("%s\n\tPSHS\tU\n\tLEAU\t,S\n",name);
}
enter1(disp)
    int disp;
{
        if(disp) printf("\tLEAS\t%d,S\n",disp);
}
leave(control,name) 
int control;
char *name;
{
    if (control)
	ret();
}


void
emit_data(e,t)
int e,t;
{
	int l;
	if(mode==STADECL) 
	    jmp_label(l=fwdlabel());
	fwddef(ilabel);
	if(car(e)==CONST)
	{       lddim(cadr(e));
		indexy(t==CHAR?"STB":"STD",gpc); 
	}
	else if(t!=CHAR)
	{       if(car(e)==ADDRESS&&car(cadr(e))==GVAR)
			leaxy(cadr(cadr(e)));
		else if(car(e)==FNAME)
			leaxpcr((NMTBL *)cadr(e));
		else error(TYERR);
		stxy(gpc); 
	}
	jmp(ilabel=fwdlabel());
	if(mode==STADECL) fwddef(l);
}