#include "b.h"

/*
 * Code generation (EM)
 */

static int
shiftsize(void)
{
	switch (wordsize) {
		case 1: return 0;
		case 2: return 1;
		case 4: return 2;
		case 8: return 3;
		default:
			error("unsupported word size");
			exit(1);
	}
}

void
tonativeaddr(void)
{
	C_loc(shiftsize());
	C_slu(wordsize);
}

void
fromnativeaddr(void)
{
	C_loc(shiftsize());
	C_sru(wordsize);
}

char*
manglename(char* name, char prefix)
{
	static char buffer[NCPS+3];
	buffer[0] = prefix;
	buffer[1] = '_';
	strcpy(buffer+2, name);
	return buffer;
}

void
binary(struct tnode *tr)
{
	rcexpr(tr->tr1);
	rcexpr(tr->tr2);
}

int
pushargs(struct tnode *tr)
{
	int stk;

	if (tr == NULL)
		return 0;
	if (tr->op == COMMA) {
		rcexpr(tr->tr2);
		stk = pushargs(tr->tr1);
		return stk+wordsize;
	}
	rcexpr(tr);
	return wordsize;
}

void
lvalexp(struct tnode *tr)
{
	struct hshtab *bs;
	char memloc[64];

	switch (tr->op) {

	case DECBEF:
	case INCBEF:
	case DECAFT:
	case INCAFT:
		if (tr->tr1->op == STAR) {
			rcexpr(tr->tr1->tr1);
			tonativeaddr();

			if ((tr->op == DECBEF) || (tr->op == INCBEF)) {
				C_dup(wordsize); /* ( addr addr -- ) */
				C_loi(wordsize); /* ( addr val -- ) */
				if (tr->op == DECBEF)
					C_dec(); /* ( addr newval -- ) */
				else
					C_inc(); /* ( addr newval -- ) */
				C_exg(wordsize); /* ( newval addr -- ) */
				C_dup(wordsize*2); /* ( newval addr newval addr -- ) */
				C_sti(wordsize); /* ( newval addr -- ) */
				C_asp(wordsize); /* ( newval -- ) */
			} else {
				C_dup(wordsize); /* ( addr addr -- ) */
				C_loi(wordsize); /* ( addr val -- ) */
				C_dup(wordsize*2); /* ( addr val addr val -- ) */
				if (tr->op == DECAFT)
					C_dec(); /* ( addr val addr newval -- ) */
				else
					C_inc(); /* ( addr val addr newval -- ) */
				C_exg(wordsize); /* ( addr val newval addr -- ) */
				C_sti(wordsize); /* ( addr val -- ) */
				C_exg(wordsize); /* ( val addr -- ) */
				C_asp(wordsize); /* ( val -- ) */
			}
		} else {	/* NAME, checked in "build" */
			bs = (struct hshtab *) tr->tr1->tr1;
			if (bs->class == EXTERN) {
				switch (tr->op) {
					case INCBEF:
						C_ine_dnam(manglename(bs->name, 'b'), 0);
						C_loe_dnam(manglename(bs->name, 'b'), 0);
						break;

					case DECBEF:
						C_dee_dnam(manglename(bs->name, 'b'), 0);
						C_loe_dnam(manglename(bs->name, 'b'), 0);
						break;

					case INCAFT:
						C_loe_dnam(manglename(bs->name, 'b'), 0);
						C_ine_dnam(manglename(bs->name, 'b'), 0);
						break;

					case DECAFT:
						C_loe_dnam(manglename(bs->name, 'b'), 0);
						C_dee_dnam(manglename(bs->name, 'b'), 0);
						break;
				}
			} else if (bs->class == AUTO) {
				switch (tr->op) {
					case INCBEF:
						C_inl(bs->offset);
						C_lol(bs->offset);
						break;

					case DECBEF:
						C_del(bs->offset);
						C_lol(bs->offset);
						break;

					case INCAFT:
						C_lol(bs->offset);
						C_inl(bs->offset);
						break;

					case DECAFT:
						C_lol(bs->offset);
						C_del(bs->offset);
						break;
				}
			} else
				goto classerror;
		}
		return;

	case ASSIGN:
		rcexpr(tr->tr2);
		C_dup(wordsize);
		if (tr->tr1->op == STAR) {
			rcexpr(tr->tr1->tr1);
			tonativeaddr();
			C_sti(wordsize);
		} else {	/* NAME */
			bs = (struct hshtab *) tr->tr1->tr1;
			if (bs->class == EXTERN) {
				C_ste_dnam(manglename(bs->name, 'b'), 0);
			} else if (bs->class == AUTO) {
				C_stl(bs->offset);
			} else
				goto classerror;
		}
		return;

	case ASPLUS:
	case ASMINUS:
	case ASMOD:
	case ASTIMES:
	case ASDIV:
	case ASOR:
	case ASAND:
	case ASLSH:
	case ASRSH:
	case ASEQUAL:
	case ASNEQL:
	case ASLEQ:
	case ASLESS:
	case ASGTQ:
	case ASGREAT:
	case ASEOR:
		tr->op -= ASPLUS-PLUS;
		rcexpr(block(ASSIGN,0,tr->tr1,tr));
		return;
	}

classerror:
	error("Storage class");
}

void
rcexpr(struct tnode *tr)
{
	int o1, o2;
	int stk;
	struct hshtab *bs;

	if (tr == NULL)
		return;

	if (opdope[tr->op]&02) {
		lvalexp(tr);
		return;
	}

	switch (tr->op) {

	case CON:
		C_loc(tr->value);
		return;

	case STRING:
		C_lae_dlb(tr->value, 0);
		fromnativeaddr();
		return;

	case NAME:	/* only rvalue */
		bs = (struct hshtab *) tr->tr1;
		if (bs->class == EXTERN)
			C_loe_dnam(manglename(bs->name, 'b'), 0);
		else if (bs->class == AUTO)
			C_lol(bs->offset);
		else
			goto classerror;
		return;

	case CALL:
		stk = pushargs(tr->tr2);
		rcexpr(tr->tr1);
		tonativeaddr();
		C_cai();
		if (stk)
			C_asp(stk);
		C_lfr(wordsize);
		return;

	case AMPER:
		bs = (struct hshtab *) tr->tr1->tr1;
		if (bs->class == EXTERN) {
			C_lae_dnam(manglename(bs->name, 'b'), 0);
		} else if (bs->class == AUTO) {
			C_lal(bs->offset);
		} else
			goto classerror;
		fromnativeaddr();
		return;

	case STAR:	/* only rvalue */
		rcexpr(tr->tr1);
		tonativeaddr();
		C_loi(wordsize);
		return;

	case PLUS:
		binary(tr);
		C_adi(wordsize);
		return;

	case MINUS:
		binary(tr);
		C_sbi(wordsize);
		return;

	case TIMES:
		binary(tr);
		C_mli(wordsize);
		return;

	case DIVIDE:
		binary(tr);
		C_dvi(wordsize);
		return;

	case MOD:
		binary(tr);
		C_rmi(wordsize);
		return;

	case AND:
		binary(tr);
		C_and(wordsize);
		return;

	case OR:
		binary(tr);
		C_ior(wordsize);
		return;

	case EOR:
		binary(tr);
		C_xor(wordsize);
		return;

	case LSHIFT:
		binary(tr);
		C_sli(wordsize);
		return;

	case RSHIFT:
		binary(tr);
		C_sri(wordsize);
		return;

	case EQUAL:
	case NEQUAL:
	case LESS:
	case LESSEQ:
	case GREAT:
	case GREATEQ:
		binary(tr);
		C_cmi(wordsize);
		switch (tr->op) {
		case EQUAL:
			C_teq();
			break;
		case NEQUAL:
			C_tne();
			break;
		case LESS:
			C_tlt();
			break;
		case LESSEQ:
			C_tle();
			break;
		case GREAT:
			C_tgt();
			break;
		case GREATEQ:
			C_tge();
			break;
		}
		return;

	case EXCLA:
		rcexpr(tr->tr1);
		C_teq();
		return;

	case NEG:
		rcexpr(tr->tr1);
		C_ngi(wordsize);
		return;

	case NOT:
		rcexpr(tr->tr1);
		C_com(wordsize);
		return;

	case QUEST:
		cbranch(tr->tr1, o1=isn++);
		rcexpr(tr->tr2->tr1);
		jump(o2 = isn++);
		fnlabel(o1);
		rcexpr(tr->tr2->tr2);
		fnlabel(o2);
		return;

	default:
		error("Can't print tree (op: %d)", tr->op);
	}

classerror:
	error("Storage class");
}

/* Prints the tree in RPN, for debugging */
/*
void
rcexpr(struct tnode *tr)
{
	struct hshtab *bs;

	if (tr == NULL)
		printf("(NULL) ");
	else if (tr->op == CON)
		printf("%d ", tr->value);
	else if (tr->op == STRING)
		printf("s(L%d) ", tr->value);
	else if (tr->op == NAME) {
		bs = (struct hshtab *)tr->tr1;
		if (bs->class == AUTO)
			printf("%s(%d) ", bs->name, bs->offset);
		else
			printf("%s ", bs->name);
	} else {
		rcexpr(tr->tr1);
		if (opdope[tr->op]&01)
			rcexpr(tr->tr2);
		printtoken(tr->op, stdout);
	}
}
*/