#include <stdio.h>
#include <stdlib.h>

#include "interface.h"
#include "symbols.h"
#include "interpret.h"
#include "task.h"
#include "mem.h"

extern struct task *current_task;

#ifdef STACK_CHECKING
#define inc_sp(i)\
	if (mem_rptr(stack+(i++)) >= mem_max_sp()) {\
		write_byte(MTFEXCEPTION);\
		write_byte(MTESTACKOVERFLOW);\
		write_byte('\n');\
		return false;\
	}
#else
#define inc_sp(i) i++;
#endif

#define uint16_t22uint32_t(x, y) (((uint32_t)x << 16) + (uint32_t)y)

//Binary operators {{{
#define binop(op) {\
	stack[sp-2] = (int16_t)stack[sp-2] op (int16_t)stack[sp-1];\
	sp -= 1;\
}

#define binop2lhs uint16_t22uint32_t(stack[sp-4], stack[sp-3])
#define binop2rhs uint16_t22uint32_t(stack[sp-2], stack[sp-1])
#define binop2(op) {\
	*nextaligned(uint32_t, stack+sp)\
		= (int32_t)binop2lhs op (int32_t)binop2rhs;\
	stack[sp-4] = *nextaligned(uint32_t, stack+sp) >> 16;\
	stack[sp-3] = *nextaligned(uint32_t, stack+sp) & 0xffff;\
	sp -= 2;\
}
#define binopr(op) {\
	*nextaligned(uint32_t, stack+sp) = float2uint32_t(\
		uint32_t2float(binop2lhs) op uint32_t2float(binop2rhs));\
	stack[sp-4] = *nextaligned(uint32_t, stack+sp) >> 16;\
	stack[sp-3] = *nextaligned(uint32_t, stack+sp) & 0xffff;\
	sp -= 2;\
}
#define boolop2(op) {\
	stack[sp-4] = (int32_t)binop2lhs op (int32_t)binop2rhs;\
	sp -= 3;\
}
#define boolopr(op) {\
	stack[sp-4] = uint32_t2float(binop2lhs) op uint32_t2float(binop2rhs);\
	sp -= 3;\
}
//}}}

#define from16(p, i) p[i]*256+p[i+1]

bool interpret(uint8_t *program, uint16_t pc, uint16_t sp, uint16_t *stack)
{
	uint16_t fp = sp, i, j;
	struct tasktree *tt = NULL;
	msg_debug(SC("Interpreting code starting from sp: %d\n"), sp);
	while (true) {
		//Make sure the dogs are fed
		real_yield();
// logging {{{
		dump_stack(program, pc, stack, sp, fp);
// }}}

		switch (program[pc++]) {
#define BCRETURNC(returnwidth, argwidth) {/*{{{*/\
	msg_debug(SC("Return width: %u, arg: %u\n"), returnwidth, argwidth);\
	sp = stack[fp-(argwidth)-3]+(returnwidth);  /*Reset the stack pointer*/\
	pc = stack[fp-(argwidth)-1];                /*Reset program counter*/  \
	uint16_t _i = stack[fp-(argwidth)-2];       /*Safe the frame pointer*/ \
	for (uint16_t _j = 0; _j<(returnwidth); _j++) {\
		stack[fp-(argwidth)-3+_j] = stack[fp+_j];\
	}\
	if (sp-(returnwidth) == 0) {\
		return true;\
	} else {\
		fp = _i;                            /*Reset frame pointer*/    \
	}\
} //}}}
#define BCROTC(depth, rotations) {/*{{{*/\
	for (; (rotations)>0; (rotations)--) {\
		for (uint8_t _k = 0; _k<depth; _k++) {\
			stack[sp-_k] = stack[sp-_k-1];\
		}\
		stack[sp-depth] = stack[sp];\
	}\
}//}}}
#define BCARGC(arg) { /*{{{*/\
		msg_debug(SC("arg(%u)\n"), arg);\
		stack[sp] = stack[fp-1-(arg)];\
		inc_sp(sp);\
} //}}}
		case BCLABEL:
			die("This should not happen!");
			break;
//Return instructions {{{
		case BCRETURN1_0:
			BCRETURNC(1, 0);
			break;
		case BCRETURN1_1:
			BCRETURNC(1, 1);
			break;
		case BCRETURN1_2:
			BCRETURNC(1, 2);
			break;
		case BCRETURN1:
			i = program[pc++];
			BCRETURNC(1, i);
			break;
		case BCRETURN2_0:
			BCRETURNC(2, 0);
			break;
		case BCRETURN2_1:
			BCRETURNC(2, 1);
			break;
		case BCRETURN2_2:
			BCRETURNC(2, 2);
			break;
		case BCRETURN2:
			i = program[pc++];
			BCRETURNC(2, i);
			break;
		case BCRETURN3_0:
			BCRETURNC(3, 0);
			break;
		case BCRETURN3_1:
			BCRETURNC(3, 1);
			break;
		case BCRETURN3_2:
			BCRETURNC(3, 2);
			break;
		case BCRETURN3:
			i = program[pc++];
			BCRETURNC(3, i);
			break;
		case BCRETURN_0:
			i = program[pc++];
			BCRETURNC(i, 0);
			break;
		case BCRETURN_1:
			i = program[pc++];
			BCRETURNC(i, 1);
			break;
		case BCRETURN_2:
			i = program[pc++];
			BCRETURNC(i, 2);
			break;
		case BCRETURN:
			//get return width
			i = program[pc++];
			//get arg width
			j = program[pc++];
			BCRETURNC(i, j);
			break;
//}}}
//Jumping and function calls {{{
		case BCPUSHPTRS:
			stack[sp] = sp;   /* push sp */
			inc_sp(sp);
			stack[sp] = fp; /* push fp */
			inc_sp(sp);
			stack[sp] = 0;  /* reserve space for pc */
			inc_sp(sp);

			msg_debug(SC("Ptrs pushed: (sp: %u, fp: %u, pc: %u)\n"),
				stack[sp-3], stack[sp-2], stack[sp-1]);
			break;
		case BCJUMPF:
			msg_debug(SC("Jumpf (%s: %u)\n"),
				stack[sp-1] ? "true" : "false",
				from16(program,pc));
			if (stack[--sp])
				pc = pc + 2;
			else
				pc = from16(program,pc);
			break;
		case BCJUMP:
			msg_debug(SC("Jump to %u\n"), from16(program,pc));
			pc = from16(program,pc);
			break;
		case BCJUMPSR:
			//Fetch the arity
			i = program[pc++];
			msg_debug(SC("Jump to sr at %u with arity %u\n"),
				from16(program,pc), i);
			msg_debug(SC("sr[0] = %u\n"), *(program+pc));
			msg_debug(SC("sr[1] = %u\n"), *(program+pc+1));
			msg_debug(SC("sr    = %u\n"),
				program[pc]*256+program[pc+1]);
			msg_debug(SC("sr    = %u\n"), from16(program,pc));

			stack[sp-i-1] = pc+2;    // Set PC using backpatching
			fp = sp;                 // Set new fp
			pc = from16(program,pc); // Jump to sr

			break;
		case BCTAILCALL:
			//Argwidth of the current function
			i = program[pc++];
			//Argwidth of the to jump to function
			j = program[pc++];
			//increment rotation depth with the argwidth of the jump
			//and pointers
			i += 3 + j;
			BCROTC(i, j);
			//j is mangled now
			//Reset the stack pointer to the original stack frame
			//compensated for the difference in argument width
			//Remove our old args, add the new args and go one up
			//i and j are mangled because of BCROTC
			fp = (fp - program[pc-2]) + program[pc-1];
			sp = fp;
			pc = from16(program,pc);
			break;
//}}}
// Arguments {{{
		case BCSTEPARG:
			//Skip over the step number
			pc+=2;
			i = program[pc++];
			BCARGC(i);
			break;
		case BCARG0:
			BCARGC(0); break;
		case BCARG1:
			BCARGC(1); break;
		case BCARG2:
			BCARGC(2); break;
		case BCARG3:
			BCARGC(3); break;
		case BCARG4:
			BCARGC(4); break;
		case BCARG10:
			BCARGC(1); BCARGC(0); break;
		case BCARG21:
			BCARGC(2); BCARGC(1); break;
		case BCARG32:
			BCARGC(3); BCARGC(2); break;
		case BCARG43:
			BCARGC(4); BCARGC(3); break;
		case BCARG:
			i = program[pc++];
			BCARGC(i);
			break;
		case BCARGS:
			//from
			i = program[pc++] + 1;
			//to
			j = program[pc++] + 1;
			msg_debug(SC("BCArgs from %u to %u\n"), i - 1, j - 1);
			if (i >= j) {
				for ( ; i>=j; i--)
					BCARGC(i - 1);
			} else {
				for ( ; i<=j; i++)
					BCARGC(i - 1);
			}
			break;
//}}}
//Task node creation {{{
		case BCMKTASK:
			msg_debug(SC("Task: \n"));
			tt = mem_alloc_tree();
			if (tt == NULL)
				return NULL;
			msg_debug(SC("Alloced: %p\n"), tt);
			tt->task_type = program[pc++];
			msg_debug(SC("task_type set: %u\n"), tt->task_type);
			tt->ptr = mem_rptr(current_task);
			msg_debug(SC("rptr: %u\n"), tt->ptr);
			tt->trash = false;
			msg_debug(SC("trash: %u\n"), tt->trash);
			switch (tt->task_type) {
//Constant node values {{{
			case BCSTABLENODE:
				msg_debug(SC("Stable node\n"));
				tt->data.stablenode.next = stack[--sp];
				msg_debug(SC("next: %u\n"), stack[sp]);
				tt->data.stablenode.w = program[pc++];
				msg_debug(SC("width: %u\n"), program[pc]);
				tt->data.stablenode.stable[1] = stack[--sp];
				msg_debug(SC("b0: %u\n"), stack[sp]);
				tt->data.stablenode.stable[0] = stack[--sp];
				msg_debug(SC("b1: %u\n"), stack[sp]);
				mem_cast_tree(tt->data.stablenode.next)->ptr
					= mem_rptr(tt);
				break;
			case BCSTABLE0:
				msg_debug(SC("Stable0\n"));
				break;
			case BCSTABLE1:
				tt->data.stable[0] = stack[--sp];
				msg_debug(SC("Stable1: %u\n"),
					tt->data.stable[0]);
				break;
			case BCSTABLE2:
				tt->data.stable[1] = stack[--sp];
				tt->data.stable[0] = stack[--sp];
				msg_debug(SC("Stable2: %u %u\n"),
					tt->data.stable[0], tt->data.stable[1]);
				break;
			case BCSTABLE3:
				tt->data.stable[2] = stack[--sp];
				tt->data.stable[1] = stack[--sp];
				tt->data.stable[0] = stack[--sp];
				msg_debug(SC("Stable3: %u %u %u\n"),
					tt->data.stable[0], tt->data.stable[1],
					tt->data.stable[2]);
				break;
			case BCSTABLE4:
				tt->data.stable[3] = stack[--sp];
				tt->data.stable[2] = stack[--sp];
				tt->data.stable[1] = stack[--sp];
				tt->data.stable[0] = stack[--sp];
				msg_debug(SC("Stable4: %u %u %u %u\n"),
					tt->data.stable[0], tt->data.stable[1],
					tt->data.stable[2], tt->data.stable[3]);
				break;
			case BCUNSTABLENODE:
				msg_debug(SC("Unstable node\n"));
				tt->data.unstablenode.next = stack[--sp];
				tt->data.unstablenode.w = program[pc++];
				tt->data.unstablenode.unstable[1] = stack[--sp];
				tt->data.unstablenode.unstable[0] = stack[--sp];
				mem_cast_tree(tt->data.unstablenode.next)->ptr
					= mem_rptr(tt);
				break;
			case BCUNSTABLE0:
				msg_debug(SC("Unstable0\n"));
				break;
			case BCUNSTABLE1:
				tt->data.unstable[0] = stack[--sp];
				msg_debug(SC("Unstable: %u\n"),
					tt->data.unstable);
				break;
			case BCUNSTABLE2:
				tt->data.unstable[1] = stack[--sp];
				tt->data.unstable[0] = stack[--sp];
				msg_debug(SC("Stable2: %u %u\n"),
					tt->data.stable[0], tt->data.stable[1]);
				break;
			case BCUNSTABLE3:
				tt->data.unstable[2] = stack[--sp];
				tt->data.unstable[1] = stack[--sp];
				tt->data.unstable[0] = stack[--sp];
				msg_debug(SC("Stable3: %u %u %u\n"),
					tt->data.stable[0], tt->data.stable[1],
					tt->data.stable[2]);
				break;
			case BCUNSTABLE4:
				tt->data.unstable[3] = stack[--sp];
				tt->data.unstable[2] = stack[--sp];
				tt->data.unstable[1] = stack[--sp];
				tt->data.unstable[0] = stack[--sp];
				msg_debug(SC("Stable4: %u %u %u %u\n"),
					tt->data.stable[0], tt->data.stable[1],
					tt->data.stable[2], tt->data.stable[3]);
				break;
//}}}
//Pin IO {{{
			case BCREADD:
				msg_debug(SC("ReadD\n"));
				tt->data.readd = translate_pin(stack[--sp]);
				break;
			case BCWRITED:
				msg_debug(SC("WriteD\n"));
				tt->data.writed.value = stack[--sp];
				tt->data.writed.pin = translate_pin(stack[--sp]);
				break;
			case BCREADA:
				msg_debug(SC("ReadA\n"));
				tt->data.reada = translate_pin(stack[--sp]);
				break;
			case BCWRITEA:
				msg_debug(SC("WriteA\n"));
				tt->data.writea.value = stack[--sp];
				tt->data.writea.pin = translate_pin(stack[--sp]);
				break;
			case BCPINMODE:
				msg_debug(SC("PinMode\n"));
				tt->data.pinmode.pin = translate_pin(stack[--sp]);
				tt->data.pinmode.pinmode = program[pc++];
				break;
//}}}
// Repeat, delay and step {{{
			case BCREPEAT:
				msg_debug(SC("REPEAT\n"));
				tt->data.repeat.oldtree = stack[--sp];
				tt->data.repeat.tree = MT_NULL;
				mem_cast_tree(tt->data.repeat.oldtree)->ptr =
					mem_rptr(tt);
				break;
			case BCDELAY:
				tt->data.delay = stack[--sp];
				msg_debug(SC("delay : %lu\n"), tt->data.delay);
				break;
			case BCDELAYUNTIL:
				die("delay until, shouldn't happen");
				break;
			case BCSTEP:
				msg_debug(SC("step: "));
				tt->data.step.w = program[pc++];
				tt->data.step.lhs = stack[--sp];
				tt->data.step.rhs = from16(program,pc);
				pc+=2;
				msg_debug(SC("step: %u %lu %lu\n"),
					tt->data.step.w, tt->data.step.lhs,
					tt->data.step.rhs);
				mem_cast_tree(tt->data.step.lhs)->ptr =
					mem_rptr(tt);
				break;
			case BCSTEPUNSTABLE:
				msg_debug(SC("stepunstable: "));
				tt->data.stepu.w = program[pc++];
				tt->data.stepu.lhs = stack[--sp];
				tt->data.stepu.rhs = from16(program,pc);
				pc+=2;
				msg_debug(SC("stepu: %u %lu %lu\n"),
					tt->data.stepu.w, tt->data.stepu.lhs,
					tt->data.stepu.rhs);
				mem_cast_tree(tt->data.stepu.lhs)->ptr =
					mem_rptr(tt);
				break;
			case BCSTEPSTABLE:
				msg_debug(SC("stepstable: "));
				tt->data.steps.w = program[pc++];
				tt->data.steps.lhs = stack[--sp];
				tt->data.steps.rhs = from16(program,pc);
				pc+=2;
				msg_debug(SC("steps: %u %lu %lu\n"),
					tt->data.steps.w, tt->data.steps.lhs,
					tt->data.steps.rhs);
				mem_cast_tree(tt->data.steps.lhs)->ptr =
					mem_rptr(tt);
				break;
			case BCSEQSTABLE:
				msg_debug(SC("seq stable: "));
				tt->data.seqs.w = program[pc++];
				tt->data.seqs.rhs = stack[--sp];
				tt->data.seqs.lhs = stack[--sp];
				msg_debug(SC("seq stable: %lu %lu\n"),
					tt->data.seqs.lhs, tt->data.seqs.rhs);
				mem_cast_tree(tt->data.seqs.lhs)->ptr =
					mem_rptr(tt);
				mem_cast_tree(tt->data.seqs.rhs)->ptr =
					mem_rptr(tt);
				break;
			case BCSEQUNSTABLE:
				msg_debug(SC("seq unstable: "));
				tt->data.sequ.w = program[pc++];
				tt->data.sequ.rhs = stack[--sp];
				tt->data.sequ.lhs = stack[--sp];
				msg_debug(SC("seq unstable: %lu %lu\n"),
					tt->data.sequ.lhs, tt->data.sequ.rhs);
				mem_cast_tree(tt->data.sequ.lhs)->ptr =
					mem_rptr(tt);
				mem_cast_tree(tt->data.sequ.rhs)->ptr =
					mem_rptr(tt);
				break;
//}}}
//Parallel {{{
			case BCTOR:
				tt->data.tor.rhs = stack[--sp];
				tt->data.tor.lhs = stack[--sp];
				msg_debug(SC("(-||-) %u %u \n"),
					tt->data.tor.lhs, tt->data.tor.rhs);
				mem_cast_tree(tt->data.tor.lhs)->ptr =
					mem_rptr(tt);
				mem_cast_tree(tt->data.tor.rhs)->ptr =
					mem_rptr(tt);
				break;
			case BCTAND:
				tt->data.tand.rhs = stack[--sp];
				tt->data.tand.lhs = stack[--sp];
				msg_debug(SC("(-&&-) %u %u\n"),
					tt->data.tand.lhs, tt->data.tand.rhs);
				mem_cast_tree(tt->data.tand.lhs)->ptr =
					mem_rptr(tt);
				mem_cast_tree(tt->data.tand.rhs)->ptr =
					mem_rptr(tt);
				break;
//}}}
//Sds {{{
			case BCSDSSET:
				tt->data.sdsset.sdsid = program[pc++];
				tt->data.sdsset.sds =
					sds_get_addr(tt->data.sdsset.sdsid);
				tt->data.sdsset.data = stack[--sp];
				msg_debug(SC("set sds: %u at %u with at %u\n"),
					program[pc-1], tt->data.sdsset.sds,
					tt->data.sdsset.data);
				mem_cast_tree(tt->data.sdsset.data)->ptr =
					mem_rptr(tt);
				break;
			case BCSDSGET:
				tt->data.sdsget.sdsid = program[pc++];
				tt->data.sdsget.sds =
					sds_get_addr(tt->data.sdsget.sdsid);
				msg_debug(SC("get sds: %u at %u\n"),
					program[pc-1], tt->data.sdsget);
				break;
//}}}
//Peripherals {{{
//DHT {{{
#ifdef HAVE_DHT
			case BCDHTTEMP:
				msg_debug(SC("Read dht temp %d\n"), stack[sp-1]);
				tt->data.dhttemp = program[pc++];
				break;
			case BCDHTHUMID:
				msg_debug(SC("Read dht humid %d\n"), stack[sp-1]);
				tt->data.dhthumid = program[pc++];
				break;
#else
			case BCDHTTEMP:
				pc++;
				break;
			case BCDHTHUMID:
				pc++;
				break;
#endif
//}}}
//I2CButton {{{
#ifdef HAVE_I2CBUTTON
			case BCABUTTON:
				msg_debug(SC("abutton: %d\n"), stack[sp-1]);
				tt->data.abutton = program[pc++];
				break;
			case BCBBUTTON:
				msg_debug(SC("bbutton: %d\n"), stack[sp-1]);
				tt->data.bbutton = program[pc++];
				break;
#else
			case BCABUTTON:
				pc++;
				break;
			case BCBBUTTON:
				pc++;
				break;
#endif
//}}}
//{{{LightSensor
#ifdef HAVE_LIGHTSENSOR
			case BCGETLIGHT:
				tt->data.lightsensor = program[pc++];
				break;
#else
			case BCGETLIGHT:
				pc++;
				break;
#endif
//}}}
//{{{AirQualitySensor
#ifdef HAVE_AIRQUALITYSENSOR
			case BCSETENVIRONMENTALDATA:
				tt->data.setenvironment.humid =
					uint32_t2float(
						(stack[sp-2] << 16) +
						 stack[sp-1]
					);
				tt->data.setenvironment.temp =
					uint32_t2float(
						(stack[sp-4] << 16) +
						 stack[sp-3]
					);
				sp-=4;
				tt->data.setenvironment.sensor = program[pc++];
				msg_debug(SC("set environment %u\n"),
						tt->data.setenvironment.sensor);
				break;
			case BCTVOC:
				tt->data.airqualitysensor = program[pc++];
				msg_debug(SC("get air quality voc %u\n"),
						tt->data.airqualitysensor);
				break;
			case BCCO2:
				tt->data.airqualitysensor = program[pc++];
				msg_debug(SC("get air quality co2 %u\n"),
						tt->data.airqualitysensor);
				break;
#else
			case BCSETENVIRONMENTALDATA:
				sp-=4;
				pc++;
				break;
			case BCTVOC:
			case BCCO2:
				pc++;
				break;
#endif
//}}}
//LEDMatrix {{{
#ifdef HAVE_LEDMATRIX
			case BCLEDMATRIXDISPLAY:
				//msg_log(SC("LED matrix display\n"));
				tt->data.ledmatrixdisplay = program[pc++];
				break;
			case BCLEDMATRIXINTENSITY:
				//msg_log(SC("LED matrix intensity to: %d\n"),
				//	stack[sp-1]);
				tt->data.ledmatrixintensity.id = program[pc++];
				tt->data.ledmatrixintensity.intensity
					= stack[--sp];
				break;
			case BCLEDMATRIXDOT:
				//msg_log(SC("LED matrix dot to: s=%d y=%d x=%d\n"),
				//	stack[sp-1], stack[sp-2], stack[sp-3]);
				tt->data.ledmatrixdot.id = program[pc++];
				tt->data.ledmatrixdot.s = stack[--sp];
				tt->data.ledmatrixdot.y = stack[--sp];
				tt->data.ledmatrixdot.x = stack[--sp];
				break;
			case BCLEDMATRIXCLEAR:
				//msg_log(SC("LED matrix clear\n"));
				tt->data.ledmatrixclear = program[pc++];
				break;
#else
			case BCLEDMATRIXDISPLAY:
				pc++;
				break;
			case BCLEDMATRIXINTENSITY:
				pc++;
				sp--;
				break;
			case BCLEDMATRIXDOT:
				pc++;
				sp-=3;
				break;
			case BCLEDMATRIXCLEAR:
				pc++;
				break;
#endif
//}}}
//}}}
			}
			msg_debug(SC("Task pushed at %p %lu: "),
				tt, mem_rptr(tt));
			tasktree_print(tt, 0);
			msg_debug(SC("\n"));
			stack[sp] = mem_rptr(tt);
			inc_sp(sp);
			break;
//}}}
//Task value ops {{{
		case BCISNOVALUE:
			msg_debug(SC("IsNoValue\n"));
			stack[sp-1] = stack[sp-1] == MT_NOVALUE;
			break;
		case BCISUNSTABLE:
			msg_debug(SC("IsUnStable\n"));
			stack[sp-1] = stack[sp-1] == MT_UNSTABLE;
			break;
		case BCISSTABLE:
			msg_debug(SC("IsStable\n"));
			stack[sp-1] = stack[sp-1] == MT_STABLE;
			break;
		case BCISVALUE:
			msg_debug(SC("IsValue\n"));
			stack[sp-1] = stack[sp-1] != MT_NOVALUE;
			break;
//}}}
//Stack ops {{{
		case BCROT:
			msg_debug(SC("Rot\n"));
			//depth
			i = program[pc++];
			//rotations
			j = program[pc++];
			BCROTC(i,j);
			break;
		case BCDUP:
			msg_debug(SC("Dup\n"));
			stack[sp] = stack[sp-1];
			sp++;
			break;
		case BCPOP4:
			sp-=4;
			msg_debug(SC("Pop4\n"));
			break;
		case BCPOP3:
			sp-=3;
			msg_debug(SC("Pop2\n"));
			break;
		case BCPOP2:
			sp-=2;
			msg_debug(SC("Pop2\n"));
			break;
		case BCPOP1:
			sp--;
			msg_debug(SC("Pop\n"));
			break;
		case BCPOP:
			msg_debug(SC("Popn\n"));
			sp-=program[pc++];
			break;
		case BCPUSH1:
			stack[sp] = program[pc++];
			inc_sp(sp);
			msg_debug(SC("Push1: %u\n"), stack[sp-1]);
			break;
		case BCPUSH2:
			stack[sp] = from16(program,pc);
			msg_debug(SC("Push2: %u\n"), stack[sp]);
			inc_sp(sp);
			pc+=2;
			break;
		case BCPUSH3:
			stack[sp++] = from16(program,pc);
			pc+=2;
			stack[sp] = program[pc++];
			inc_sp(sp);
			msg_debug(SC("Push3: %u\n"), stack[sp-1]);
			break;
		case BCPUSH4:
			stack[sp++] = from16(program,pc);
			pc+=2;
			stack[sp] = from16(program,pc);
			inc_sp(sp);
			pc+=2;
			msg_debug(SC("Push4: %u\n"), stack[sp-1]);
			break;
		case BCPUSH:
			i = program[pc++];
			msg_debug(SC("Push %u\n"), i);
			for (j = 0; j<i; j++)
				if (j % 2 == 0) {
					stack[sp] = program[pc++]*256;
					inc_sp(sp);
				} else {
					stack[sp-1] += program[pc++];
				}
			break;
		case BCPUSHNULL:
			stack[sp] = MT_NULL;
			inc_sp(sp);
			break;
//}}}
//Casting {{{
		case BCITOR:
			i = stack[sp-1];
			stack[sp-1] =
				float2uint32_t((float)(int16_t)i) >> 16;
			stack[sp] =
				float2uint32_t((float)(int16_t)i) &  0xffff;
			inc_sp(sp);
			break;
		case BCITOL:
			i = stack[sp-1];
			stack[sp-1] = ((int32_t)i) >> 16;
			stack[sp]   = ((int32_t)i) &  0xffff;
			inc_sp(sp);
			break;
		case BCRTOI:
			i = stack[sp-2];
			j = stack[sp-1];
			sp--;
			stack[sp-1] = (int16_t)uint32_t2float(
				uint16_t22uint32_t(i, j));
			break;
		case BCRTOL:
			i = stack[sp-2];
			j = stack[sp-1];
			stack[sp-2] = ((int32_t)uint32_t2float(
				uint16_t22uint32_t(i, j))) >> 16;
			stack[sp-1] = ((int32_t)uint32_t2float(
				uint16_t22uint32_t(i, j))) &  0xffff;
			break;
		case BCLTOI:
			i = stack[sp-2];
			j = stack[sp-1];
			sp--;
			stack[sp-1] = uint16_t22uint32_t(i, j);
			break;
		case BCLTOR:
			i = stack[sp-2];
			j = stack[sp-1];
			sp--;
			stack[sp-2] = ((int32_t)uint32_t2float(
				uint16_t22uint32_t(i, j))) >> 16;
			stack[sp-1] = ((int32_t)uint32_t2float(
				uint16_t22uint32_t(i, j))) &  0xffff;
			break;
//}}}
//Integer arith {{{
		case BCADDI:
			msg_debug(SC("Add\n"));
			binop(+);
			break;
		case BCSUBI:
			msg_debug(SC("Sub\n"));
			binop(-);
			break;
		case BCMULTI:
			msg_debug(SC("Mult\n"));
			binop(*);
			break;
		case BCDIVI:
			msg_debug(SC("Div\n"));
			binop(/);
			break;
//}}}
//Long arith {{{
		case BCADDL:
			msg_debug(SC("Add2\n"));
			binop2(+);
			break;
		case BCSUBL:
			msg_debug(SC("Sub2\n"));
			binop2(-);
			break;
		case BCMULTL:
			msg_debug(SC("Mult2\n"));
			binop2(*);
			break;
		case BCDIVL:
			msg_debug(SC("Div2\n"));
			binop2(/);
			break;
//}}}
//Real arith {{{
		case BCADDR:
			msg_debug(SC("Addr\n"));
			binopr(+);
			break;
		case BCSUBR:
			msg_debug(SC("Subr\n"));
			binopr(-);
			break;
		case BCMULTR:
			msg_debug(SC("Multr\n"));
			binopr(*);
			break;
		case BCDIVR:
			msg_debug(SC("Divr\n"));
			binopr(/);
			break;
//}}}
//Bool arith {{{
		case BCAND:
			msg_debug(SC("And\n"));
			binop(&&);
			break;
		case BCOR:
			msg_debug(SC("Or\n"));
			binop(||);
			break;
		case BCNOT:
			msg_debug(SC("Not\n"));
			stack[sp-1] = !stack[sp-1];
			break;
//}}}
//Equality {{{
		case BCEQI:
			msg_debug(SC("Eq\n"));
			binop(==);
			break;
		case BCEQL:
			msg_debug(SC("Eq2\n"));
			boolop2(==);
			break;
		case BCNEQI:
			msg_debug(SC("Neq\n"));
			binop(!=);
			break;
		case BCNEQL:
			msg_debug(SC("Eq2\n"));
			boolop2(!=);
			break;
// }}}
// Integer comparison {{{
		case BCLEI:
			msg_debug(SC("Le\n"));
			binop(<);
			break;
		case BCGEI:
			msg_debug(SC("Ge\n"));
			binop(>);
			break;
		case BCLEQI:
			msg_debug(SC("Leq\n"));
			binop(<=);
			break;
		case BCGEQI:
			msg_debug(SC("Geq\n"));
			binop(>=);
			break;
//}}}
//Long comparison {{{
		case BCLEL:
			msg_debug(SC("Le2\n"));
			boolop2(<);
			break;
		case BCGEL:
			msg_debug(SC("Ge2\n"));
			boolop2(>);
			break;
		case BCLEQL:
			msg_debug(SC("Leq2\n"));
			boolop2(<=);
			break;
		case BCGEQL:
			msg_debug(SC("Geq2\n"));
			boolop2(>=);
			break;
//}}}
//Real comparison {{{
		case BCLER:
			msg_debug(SC("Le2\n"));
			boolopr(<);
			break;
		case BCGER:
			msg_debug(SC("Ge2\n"));
			boolopr(>);
			break;
		case BCLEQR:
			msg_debug(SC("Leq2\n"));
			boolopr(<=);
			break;
		case BCGEQR:
			msg_debug(SC("Geq2\n"));
			boolopr(>=);
			break;
//}}}
		default:
			break;
		}
	}
}
