#include "common"

binop(p1, p2, p3) struct at *p1, *p2, *p3;
	{struct at *pl, *pr, *pt;
	int k, op;
	if (skip) return;
	pl = ref(p1);
	op = (pt = ref(p2))->value;
	pr = ref(p3);
	if (atime || test(pt, CRSF) && !test(p1, PUBF))
/*
	during atime:
	DCLF	we're defining something
	PUBF	this is what we're defining

	outside atime:
	DCLF	we've defined something
	PUBF	we've generated machine code
*/
		{switch (op) {
	 case NOP:	return;

	 case GETSNOT:
	 case GETSMI:
	 case GETS:	set(p1, PUBF | DCLF, TRUE);
			if (test(p1, DEFF) || op != GETS && pr->bias) break;
			switch (op) {
		 case GETSNOT:	setvb(p3, ~pr->value, 0); break;
		 case GETSMI:	setvb(p3, -pr->value, 0); break;
		 case GETS:	setvb(p3, pr->value, pr->bias); break;
					}
			define(p1->value, p3->flags & ATTR, p3->type,
				p3->value, p3->bias);
			return;

	 case GOESTO:	set(p1, DCLF, TRUE);
			if (test(p3, DEFF) || !test(pl, DEFF)) break;
			define(p3->value, p1->flags & ATTR, p1->type,
				pl->value, pl->bias);
			return;

	 case SIZE:	set(p1, DCLF, TRUE);
			if (con(p3) == NULL) break;
			p1->size = pr->value;
			return;

	 default:	if (!test(p1, PUBF))
				{setvb(p1, pl->value, pl->bias);
				set(p1, DCLF, FALSE);
				pl = p1; }
			switch (op) {
		 case PLUS:	if (pl->bias && pr->bias) break;
				pl->value =+ pr->value;
				pl->bias =+ pr->bias;
				return;

		 case MINUS:	if (pl->bias != pr->bias && pr->bias) break;
				pl->value =- pr->value;
				pl->bias =- pr->bias;
				return;

		 default:	if (pl->bias || pr->bias) break;
				switch (op) {
			 case NOT:	pl->value = ~(pl->value ^ pr->value);
					return;
			 case OR:	pl->value =| pr->value; return;
			 case AND:	pl->value =& pr->value; return;
			 case ANDNOT:	pl->value =& ~pr->value; return;
			 case XOR:	pl->value =^ pr->value; return;
			 case SHIFT:	pl->value =<< pr->value; return;
			 case TIMES:	pl->value =* pr->value; return;
			 case DIVIDE:	pl->value =/ pr->value; return;
			 case CAT:	pl->value = pl->value & 0377
						| (pr->value << 8);
					p1->type = CON; return;
						}
					}
				}
		error("%s %s %s illegal at compile time", s(p1), s(p2), s(p3));
		return; }
	else {
		set(p1, PUBF, TRUE);
		set(p1, BCCF, FALSE);
		switch (op) {
	 case NOP:	return;

	 case GOESTO:	pt = p1; p1 = p3; p3 = pt;
			pt = pl; pl = pr; pr = pt;
	 case GETSNOT:
	 case GETSMI:
	 case GETS:	if (con(p3) && pr->value == 0 && op != GETSNOT)
				geninst(byte(p1, NULL) + 005000, NULL, p1);
			else if(p1->type == KOND && op != GETSMI &&
				p3->type == KOND &&
				((k = pr->value) == TRUE || k == FALSE))
				{k = (k ^ (op == GETSNOT)) << 4;
				switch (pl->value) {
			 case N:	gen(000250 ^ k, 0); return;
			 case Z:	gen(000244 ^ k, 0); return;
			 case V:	gen(000242 ^ k, 0); return;
			 case C:	gen(000241 ^ k, 0); return;
							 }
				 }
			else if (p3->type == KOND && pr->value == N &&
				op == GETS && !test(p1, BYTF))
				geninst(006700, NULL, p1);
			else if (compat(p1, p3) &&
				(op == GETSNOT || op == GETSMI) &&
				p1->type == p3->type &&
				pl->value == pr->value &&
				pl->bias == pr->bias)
				geninst(byte(p1, NULL) + (op == GETSNOT ?
					005100 : 005400), NULL, p1);
			else if (compat(p1, p3) &&
				(op == GETS || op == GOESTO))
				geninst(byte(p1, p3) + 010000, p3, p1);
			else break;
			return;

	 case PLUS:	if (con(p3) && pr->value == 1)
				geninst(byte(p1) + 005200, NULL, p1);
			else if (p3->type == KOND && pr->value == C)
				geninst(byte(p1, NULL) + 005500, NULL, p1);
			else if (!byte(p1, p3))
				geninst(060000, p3, p1);
			else break;
			return;

	 case MINUS:	if (con(p3) && pr->value == 1)
				geninst(byte(p1) + 005300, NULL, p1);
			else if (p3->type == KOND && pr->value == C)
				geninst(byte(p1, NULL) + 005600, NULL, p1);
			else if (!byte(p1, p3))
				geninst(0160000, p3, p1);
			else break;
			return;

	 case OR:	if (!compat(p1, p3)) break;
			geninst(byte(p1, p3) + 050000, p3, p1); return;

	 case AND:	if (con(p3) == NULL) break;
			setvb(p3, ~pr->value, 0);
	 case ANDNOT:	if (!compat(p1, p3)) break;
			geninst(byte(p1, p3) + 040000, p3, p1);
			return;

	 case XOR:	if (byte(p1, p3) || p3->type != REG) break;
			geninst(074000, p3, p1);
			return;

	 case ROTATE:	if (con(p3) == NULL) break;
			if (pr->value == 1)
				geninst(byte(p1, NULL) + 006100, NULL, p1);
			else if (pr->value == -1)
				geninst(byte(p1, NULL) + 006000, NULL, p1);
			else break;
			return;

	 case SROTATE:	if (con(p3) == NULL) break;
			if (pr->value == 8 && !test(p1, BYTF))
				{set(p1, BCCF, TRUE);
				geninst(000300, NULL, p1); }
			else if (reg(p1) && pl->value & 1 &&
				-16 <= pr->value && pr->value <= 0)
				geninst(073000 + (pr->value & 077), p1, NULL);
			else break;
			return;

	 case SHIFT:	if (con(p3) == NULL) break;
			if (pr->value == 1)
				geninst(byte(p1, NULL) + 006300, NULL, p1);
			else if (pr->value == -1)
				geninst(byte(p1, NULL) + 006200, NULL, p1);
			else if (reg(p1)) geninst(072000 + (pr->value & 077),
				p1, NULL);
			else break;
			return;

	 case DSHIFT:	if (con(p3) == NULL || reg(p1) == NULL) break;
			geninst(073000 + (pr->value & 077), p1, NULL);
			return;

	 case TIMES:	if (byte(p1, p3) || reg(p1) == NULL) break;
			geninst(070000, p1, p3); return;

	 case DIVIDE:	if (byte(p1, p3) || reg(p1) == NULL) break;
			geninst(071000, p1, p3); return;

	 case TEST:	if (!compat(p1, p3)) break;
			if (con(p3) && pr->value == 0)
			geninst(byte(p1, NULL) + 005700, NULL, p1);
			else geninst(byte(p1, p3) + 020000, p1, p3);
			return;

	 case TESTAND:	if (!compat(p1, p3)) break;
			geninst(byte(p1, p3) + 030000, p3, p1);
			return;

	 case JSR:	if (byte(p1) || reg(p3) == NULL) break;
			geninst(004037, p3, p1);
			return;
				}
		error("%s %s %s illegal", s(p1), s(p2), s(p3));
		}
	}
