Source: MVM/MVM.js

/*
* Sketch Virtual Machine
* Darren Findlay
*
* 15th January 2015
*
*/

var MVM = MVM || {};

/**
 * @classdesc The virtual machine used to execute Sketch programs.
 * @class MVM.VM
 * @param {WebGLRenderingContext} glctx - WebGL context of the canvas to operate on.
 * @param {Palette.Manager} manager - Shader Manager used to abstract draw calls and shader manipulation.
 * @param {Array} codeStore - MVM program code generated by an instance of {@link Sketch.SketchGen}.
 * @param {boolean} debugMode - print additional information to the console during execution.
 * @public
 * @author Darren Findlay
 */
MVM.VM = function(glctx, manager, codeStore, debugMode) {

	/*
	*	Struct layouts
	*	
	*				  x    y
	*	Point 		[100, 150]
	*
	*				  r    g   b   a   x1  y1  x2  y2
	*	Line 		[ 0 ,  0 ,255,255,100,100,200,200]
	*
	*				  r    g   b   a   x1  y1  x2  y2  x3  y3 	0 or More points
	*	Polygon 	[ 0 ,  0 ,255,255,100,100,200,200,150, 0 ,.............]
	*
	*/

	//TEMP
	var constantPool = [];
	var labelTable = [];

	// WebGL context
	var glctx = glctx;

	// Shader manager
	var manager = manager;

	// Loop Counter - For debugging
	var lc = 0;

	// Points to the next instruction in the code store to execute
	var cp = 0;

	// Points to the first free location after the program
	var cl;

	// Data store (Stack)
	window.MVM.dataStore = [];
	var data = new MVM.DataModel();

	// Points to the first free space at the top of the data store
	var sp = 0;

	// Points to the first location of the top most frame
	var fp = 0;

	// Local Offset. The off set of the first local address from the frame pointer
	var LO = 2;

	// Address of the dynamic link in a frame
	var DLA = 0;

	// Address of the retrun address of a frame
	var RA = 1;

	// Global data store
	var globalStore = [];

	// Flags wether the virtual machine should hand over control
	// to the browser so te canvas can be rendered
	var needsUpdate = 0;

	this.dead = false;

	this.interpret = function() {
		if(this.dead === true){
			return;
		}

		var dataStore = window.MVM.dataStore;

		if(debugMode) console.log(codeStore);

		cl = codeStore.length;

		var opCodes = MVM.opCodes;
		while (cp < cl && needsUpdate == 0) {
			lc++
			var opCode = codeStore[cp];
			cp++;
			switch (opCode) {
				case opCodes.STOREG:
					//Store a value in a given relative stack frame, in a given index.
					//USE: STOREG index
					//e.g. STORER 0 stores the top value on the stack in slot 0 of the root scope frame.
					var ind = codeStore[cp++];
					var val = data.current()
								  .pop();

					data.root
						.setVar(ind, val);

					if(debugMode) console.log("STOREG: " + val + " in index " + ind);
					break;
				case opCodes.LOADG:
					//Load a value from the root scope frame onto the current stack, from a given index.
					//USE: LOADG index
					//e.g. LOADG 0 loads the value in slot 0 of the root scope frame.
					var ind = codeStore[cp++];	
					var val = data.root
								  .getVar(ind);

					data.current()
						.push(val);

					if(debugMode) console.log("LOADG: " + val + " from index " + ind);
					break;
				case opCodes.STOREL:
					//Store a value in the current scope frame, in a given index.
					//USE: STOREL index
					//e.g. STOREL 0 stores the top value on the stack in slot 0 of the current scope frame.
					var ind = codeStore[cp++];
					var val = data.current()
								  .pop();

					data.current()
						.setVar(ind, val);

					if(debugMode) console.log("STOREL: " + val + " in index " + ind);
					break;
				case opCodes.LOADL:
					//Load a value from the current scope frame onto the current stack, from a given index.
					//USE: LOADL index
					//e.g. LOADL 0 loads the value in slot 0 of the data stack frame.
					var ind = codeStore[cp++];	
					var val = data.current()
								  .getVar(ind);

					data.current()
						.push(val);

					if(debugMode) console.log("LOADL: "  + val + " from index " + ind);
					break;
				case opCodes.LOADC:
					//Place the next codeword on the top of the stack.
					var constant = codeStore[cp++];

					data.current()
						.push(constant);

					if(debugMode) console.log("LOADC: " + constant);
					break;
				case opCodes.IADD:
					//Pop two integers off the stack, add them and push the new result onto the stack.
					var i = Math.floor(
						data.current()
							.pop());
					var j = Math.floor(
						data.current()
							.pop());
					var result = j + i;

					data.current()
						.push(result);

					if(debugMode) console.log("IADD: " + j + " + " + i + " = " + result);
					break;
				case opCodes.ISUB:
					//Pop two integers off the stack, subbtract them and push the new result onto the stack.
					var i = Math.floor(
						data.current()
							.pop());
					var j = Math.floor(
						data.current()
							.pop());
					var result = j - i;

					data.current()
						.push(result);

					if(debugMode) console.log("ISUB: " + j + " - " + i + " = " + result);
					break;
				case opCodes.IMUL:
					//Pop two integers off the stack, multiply them and push the new result onto the stack.
					var i = Math.floor(
						data.current()
							.pop());
					var j = Math.floor(
						data.current()
							.pop());
					var result = j * i;

					data.current()
						.push(result);

					if(debugMode) console.log("IMUL: " + j + " * " + i + " = " + result);
					break;
				case opCodes.IDIV:
					//Pop two integers off the stack, divide them and push the new result onto the stack.
					var i = Math.floor(
						data.current()
							.pop());
					var j = Math.floor(
						data.current()
							.pop());

					var result = Math.floor(j / i);
					
					data.current()
						.push(result);

					if(debugMode) console.log("IDIV: " + j + " / " + i + " = " + result);
					break;
				case opCodes.IMOD:
					//Pop two integers off the stack, take modulus and push the new result onto the stack.
					var i = Math.floor(
						data.current()
							.pop());
					var j = Math.floor(
						data.current()
							.pop());
					var result = Math.floor(j % i);
					
					data.current()
						.push(result);

					if(debugMode) console.log("IMOD: " + j + " % " + i + " = " + result);
					break;
				case opCodes.FADD:
					//Pop two integers off the stack, add them and push the new result onto the stack.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();
					var result = j + i;

					data.current()
						.push(result);

					if(debugMode) console.log("FADD: " + j + " + " + i + " = " + result);
					break;
				case opCodes.FSUB:
					//Pop two integers off the stack, subtract them and push the new result onto the stack.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();
					var result = j - i;

					data.current()
						.push(result);

					if(debugMode) console.log("FSUB: " + j + " - " + i + " = " + result);
					break;
				case opCodes.FMUL:
					//Pop two integers off the stack, multiply them and push the new result onto the stack.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();
					var result = j * i;

					data.current()
						.push(result);

					if(debugMode) console.log("FMUL: " + j + " * " + i + " = " + result);
					break;
				case opCodes.FDIV:
					//Pop two integers off the stack, divide them and push the new result onto the stack.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();
					var result = j / i;

					data.current()
						.push(result);

					if(debugMode) console.log("FDIV: " + j + " / " + i + " = " + result);
					break;
				case opCodes.FMOD:
					//Pop two integers off the stack, divide them and push the remainder onto the stack.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();
					var result = j % i;

					data.current()
						.push(result);

					if(debugMode) console.log("FMOD: " + j + " % " + i + " = " + result);
					break;
				case opCodes.CMPEQ:
					//Pop two values off the stack, push true if they equate or false if they do not.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();

					var result = (j == i);
					
					data.current()
						.push(result);

					if(debugMode) console.log("CMPEQ: " + j + " == " + i + " = " + result);
					break;
				case opCodes.CMPLT:
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();

					var result = (j < i);
					
					data.current()
						.push(result);
					if(debugMode) console.log("CMPLT: " + j + " < " + i + " = " + result);
					break;
				case opCodes.CMPGT:
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();

					var result = (j > i);
					
					data.current()
						.push(result);
					if(debugMode) console.log("CMPGT: " + j + " > " + i + " = " + result);
					break;
				case opCodes.JUMP:
					//Jump to another part of the program unconditionally.
					//USE: JUMP address

					var address = codeStore[cp];
					cp = address;

					if(debugMode) console.log("JUMP: " + address);
					break;
				case opCodes.JUMPT:
					//Jump to another part of the program if the top value on the stack is true.
					//USE: JUMPT address

					var address = codeStore[cp++];
					
					var i = data.current()
								.pop();
					if (i) {
						cp = address;
					}

					if(debugMode) console.log("JUMPT: to " + address + ", cond is " + i);
					break;
				case opCodes.JUMPF:
					//Jump to another part of the program if the top value on the stack is false.
					//USE: JUMPF address

					var address = codeStore[cp++];
					
					var i = data.current()
								.pop();
					if (!i) {
						cp = address;
					}
					
					if(debugMode) console.log("JUMPF: to " + address + ", cond is " + i);
					break;
				case opCodes.CALL:
					//Call a function.
					//USE: CALL definitionHeight codeAddress numArgs
					//e.g. CALL 1 90 3 calls a function starting at code address 90, defined 1 scope frame above the call site with 3 parameters.

					var definitionHeight = codeStore[cp++];
					var codeAddress = codeStore[cp++];
					var numArgs = codeStore[cp++];

					var returnAddress = cp;

					data.call(numArgs, definitionHeight, returnAddress);
					cp = codeAddress;

					if(debugMode) console.log("CALL: " + codeAddress + "  with " + numArgs + " arguments, return to " + returnAddress);
					break;
				case opCodes.RETURNVAL:
					//Return from a function, taking the top value from the stack from the function scope and placing it onto the resumed scope.
					//USE: RETURNVAL
					var value = data.current()
									.pop();

					cp = data.funcreturn(value);

					if(debugMode) console.log("RETURNVAL: " + value + " returned, exiting function. New code pointer is "+cp);
					break;
				case opCodes.RETURN:
					//Return from a function, returning no value.
					//USE: RETURN
					cp = data.funcreturn(null);

					if(debugMode) console.log("RETURN: void return, exiting function.");
					break;
				case opCodes.LNDRAW:
					// Get line from top of stack, and then draw it? What more is there to say?
					var lineStruct = data.current()
								  		 .pop();

					var r = lineStruct[0];
					var g = lineStruct[1];
					var b = lineStruct[2];
					var a = lineStruct[3];
					var pt1x = lineStruct[4];
					var pt1y = lineStruct[5];
					var pt2x = lineStruct[6];
					var pt2y = lineStruct[7];

					var theLine = new Float32Array([pt1x,pt1y,0, pt2x,pt2y,0]);
					var theColor = new Float32Array([r,g,b,a]);

					var prog = manager.getProgram("square", "square");
					var canWidth = glctx.canvas.width;
					var canHeight = glctx.canvas.height;
					prog.setDrawMode(Palette.Program.LINES);
					prog.draw(theLine, {width:[canWidth], height: [canHeight]}, {color: theColor})

					if(debugMode) console.log("LNDRAW: " + lineStruct);
					break;
				case opCodes.PGDRAW:
					// Get polygon from top of stack, and then draw it? What more is there to say?
					var polygonStruct = data.current()
								  			.pop();

					var r = polygonStruct[0];
					var g = polygonStruct[1];
					var b = polygonStruct[2];
					var a = polygonStruct[3];
					var theColor = new Float32Array([r,g,b,a]);

					var points = polygonStruct.slice(4);
					// var i;
					// for (i = 4; i < polygonStruct.length; i+=2) {
					// 	var pt = [polygonStruct[i],polygonStruct[i+1]];
					// 	points.push(pt);
					// }
					var prog = manager.getProgram("square", "square");
					prog.setDrawMode(Palette.Program.POLYGON);
					var canWidth = glctx.canvas.width;
					var canHeight = glctx.canvas.height;

					prog.draw(points, {width:[canWidth], height: [canHeight]}, {color: theColor})
					if(debugMode) console.log("PGDRAW: " + polygonStruct);
					break;
				case opCodes.RENDER:
					needsUpdate = 1;
					if(debugMode) console.log("RENDER");
					break;
				case opCodes.CLEAR:
					//Pop an element off the stack. If it is a colour, set it as the clear colour - if not, use the current clear colour.
					var colour = data.current()
									 .pop();
					if(colour){
						if(colour.length<4){
							colour[3] = 1.0;
						}

						glctx.clearColor(colour[0],colour[1],colour[2],colour[3]);
					}
					
					glctx.clear(glctx.COLOR_BUFFER_BIT|glctx.DEPTH_BUFFER_BIT);
					if(debugMode) console.log("CLEAR");
					break;
				case opCodes.EXIT:
					//Ends program operation.
					//USE: EXIT
					cp = cl;
					if(debugMode) console.log("EXIT");
					break;
				case opCodes.LOADIDX:
					var constPoolindex = codeStore[cp];
					cp++;
					var arrayIndex = codeStore[cp];
					cp++;
					var arr = constantPool[constPoolindex];
					var value = arr[arrayIndex];
					dataStore[sp] = value;
					sp++;
					if(debugMode) console.log("LOADIDX: constant pool index " + constPoolindex + " array index: " + arrayIndex);
					break;
				case opCodes.SETIDX:
					var constPoolindex = codeStore[cp];
					cp++;
					var arrayIndex = codeStore[cp];
					cp++;
					var arr = constantPool[constPoolindex];
					sp--;
					var value = dataStore[sp];
					arr[arrayIndex] = value;
					if(debugMode) console.log("SETIDX: constant pool index " + constPoolindex + " array index: " + arrayIndex);
					break;
				case opCodes.LNTOPG:
					sp--;
					var lineAddress = dataStore[sp];
					sp--;
					var numSides = dataStore[sp];
					var line = constantPool[lineAddress];
					var polygon = line.slice(0);
					var i = 2;
					var angle = 360 / numSides;
					var pt1xIdx = 4;
					var pt1yIdx = 5;
					var pt2xIdx = 6;
					var pt2yIdx = 7;
					var pt3xIdx = 8;
					var pt3yIdx = 9;
					while(i < numSides) {
						var pivot = [polygon[pt1xIdx],polygon[pt1yIdx]];
						var point = [polygon[pt2xIdx],polygon[pt2yIdx]];
						var newpt = rotatePoint(pivot,point,angle);
						// Shift new point
						offSetx = polygon[pt1xIdx] - polygon[pt2xIdx];
						offSety = polygon[pt1yIdx] - polygon[pt2yIdx];
						newpt[0] -= offSetx;
						newpt[1] -= offSety;
						// Add new point to polygon
						polygon[pt3xIdx] = newpt[0];
						polygon[pt3yIdx] = newpt[1];
						pt1xIdx += 2;
						pt1yIdx += 2;
						pt2xIdx += 2;
						pt2yIdx += 2;
						pt3xIdx += 2;
						pt3yIdx += 2;
						i++;
					}
					var targetAddress = codeStore[cp];
					cp++;
					constantPool[targetAddress] = polygon;
					if(debugMode) console.log("LNTOPG " + polygon);
					break;
				case opCodes.PTADD:
					//Pop two points off the stack, then place a white line generated by this back onto the stack.
					var pt2 = data.current()
								  .pop();
					var pt1 = data.current()
								  .pop();
					
					var line = [1,1,1,1,pt1[0],pt1[1],pt2[0],pt2[1]];
					
					data.current()
						.push(line);

					if(debugMode) console.log("PTADD " + line);
					break;
				case opCodes.LNMUL:
					sp--;
					var mulValue = dataStore[sp];
					sp--;
					var lineAddress = dataStore[sp];
					var line = constantPool[lineAddress];
					pt1x = line[4];
					pt1y = line[5];
					pt2x = line[6];
					pt2y = line[7];

					var xDist = pt2x - pt1x;
					var yDist = pt2y - pt1y;

					var xLen = (xDist * mulValue) - xDist; 
					var yLen = (yDist * mulValue) - yDist; 

					var targetLineAddress = codeStore[cp];
					cp++;

					var newLine = line.slice(0);
					newLine[6] += xLen;
					newLine[7] += yLen;
					newLine[0] = 0;
					newLine[1] = 1;
					newLine[2] = 0;
					newLine[3] = 1;
					constantPool[targetLineAddress] = newLine;
					if(debugMode) console.log("LNMUL " + newLine);
					break;
				//Augmentations to support scoping.
				case opCodes.STORER:
					//Store a value in a given relative stack frame, in a given index. (Store Relative)
					//USE: STORER stack index
					//e.g. STORER 1 0 stores the top value on the stack in slot 0 of the data stack frame above the current one.
					var rel = codeStore[cp++];
					var ind = codeStore[cp++];
					var val = data.current()
								  .pop();

					data.relative(rel)
						.setVar(ind, val);

					if(debugMode) console.log("STORER: placed "+val+" in index "+ind+" of relative frame "+rel+".");
					break;
				case opCodes.LOADR:
					//Load a value from a relative stack frame, from a given index. (Load Relative)
					//USE: LOADR stack index
					//e.g. LOADR 2 0 loads the value in slot 0 of the data stack frame 2 layers above the current one.
					var rel = codeStore[cp++];
					var ind = codeStore[cp++];	
					var val = data.relative(rel)
								  .getVar(ind);

					data.current()
						.push(val);

					if(debugMode) console.log("LOADR: retrieved "+val+" from index "+ind+" of relative frame "+rel+".");
					break;
				case opCodes.POPSC:
					//Pop off and discard the current stack data frame, equivalent to leaving a code block. (Pop Scope)
					//USE: POPSC
					if(debugMode){
						console.log("POPSC: exiting scope:");
						console.log(data.current());
					}
					data.exit();

					if(debugMode) console.log("POPSC: exited current block level.");
					break;
				case opCodes.PUSHSC:
					//Create and push a new stack data frame, equivalent to entering a code block. (Push Scope)
					//USE: PUSHSC
					data.enter();

					if(debugMode) console.log("PUSHSC: entered new block level.");
					break;

				//BOOLEAN OPERANDS
				case opCodes.BAND:
					//Pop two values off the stack, push A && B.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();

					var result = (j && i);
					
					data.current()
						.push(result);

					if(debugMode) console.log("BAND: " + j + " && " + i + " = " + result);
					break;
				case opCodes.BOR:
					//Pop two values off the stack, push A || B.
					var i = data.current()
							.pop();
					var j = data.current()
							.pop();

					var result = (j || i);
					
					data.current()
						.push(result);

					if(debugMode) console.log("BOR: " + j + " || " + i + " = " + result);
					break;
				case opCodes.BNEG:
					//Pop one value off the stack, push !A.
					var i = data.current()
								.pop();

					var result = !i;
					
					data.current()
						.push(result);

					if(debugMode) console.log("BNEG: !" + i + " = " + result);
					break;
				case opCodes.AGGR:
					//Aggregate a set of elements from the stack into an array.
					//USE: AGGR num
					//e.g. AGGR 3 pops 3 elements a, b, anc c from the stack and pushes [a,b,c] to the stack.
					var num = codeStore[cp++];
					var out = [];

					while(num-- > 0){
						var i = data.current()
									.pop();
						out.unshift(i);
					}

					data.current()
						.push(out);

					if(debugMode) console.log("AGGR: output " + out);
					break;
				case opCodes.WIDTH:
					//Push the width of the canvas onto the stack. Since this can't be gleaned normally.
					data.current()
						.push(glctx.canvas.width);

					if(debugMode) console.log("WIDTH: " + glctx.canvas.width);
					break;
				case opCodes.HEIGHT:
					//Push the height of the canvas onto the stack. Since this can't be gleaned normally.
					data.current()
						.push(glctx.canvas.height);

					if(debugMode) console.log("WIDTH: " + glctx.canvas.height);
					break;
				case opCodes.AUGPT:
					//Pop two structs off the stack, identify which is the point and append it to the line/poly.
					var i = data.current()
								.pop();
					var j = data.current()
								.pop();

					var out;

					if(i.length>j.length){
						out = i.concat(j);
					} else{
						out = j.concat(i);
					}

					data.current()
						.push(out);

					if(debugMode) console.log("AUGPT: " +i+ " + " +j+ " = " +out);
					break;
				case opCodes.SETCOLOUR:
					//Pop one line/poly and one point, use the point to define the colour for that shape...
					var colour = data.current()
								.pop();
					var shape = data.current()
								.pop();

					if(colour.size === 3){
						colour[3] = 1;
					}

					for(var i = 0; i< colour.length; i++){
						shape[i] = colour[i];
					}

					data.current()
						.push(shape);

					if(debugMode) console.log("SETCOLOUR: " +shape+ " ~ " +colour+ " = " +out);
					break;
				case opCodes.TRANSLATEPT:
					//Pop off twom points, move top of stack by the necessary distance.
					var vectr = data.current()
									.pop();
					var point = data.current()
									.pop();

					var out = point.slice();

					for (var i = 0; i < vectr.length; i++) {
						out[i] += vectr[i];
					};

					data.current()
						.push(out);

					if(debugMode) console.log("TRASLATEPT: [" +point+ "] -> [" +vectr+ "] = [" +out+"]");
					break;
				case opCodes.TRANSLATESTRUCT:
					//Pop off twom points, move top of stack by the necessary distance.
					var vectr = data.current()
									.pop();
					var strct = data.current()
									.pop();

					var out = strct.slice();

					// alert("["+out+"]");

					for (var i = 4; i < strct.length; i++) {
						out[i] += vectr[i%2];
					};

					data.current()
						.push(out);

					if(debugMode) console.log("TRASLATESTRUCT: [" +strct+ "] -> [" +vectr+ "] = [" +out+"]");
					break;
			}
			// remove garbage from stack
			//dataStore.splice(sp,dataStore.length - sp);
			//if(debugMode) console.log(JSON.stringify(dataStore));
		}
		if (needsUpdate) {render();}
		return data;
	};

	// Passes control to the browser to update the canvas and
	// requests a call back to start interpreting once the rendering has
	// complete
	render = function() {
		needsUpdate = 0;
		window.requestAnimationFrame(window.mvm.interpret);
	}

	this.call = function(address, args){
		var returnAddress = codeStore.length;
		for (var i = 0; i < args.length; i++) {
			data.current()
				.push(args[i]);
		};
		data.call(args.length, 0, returnAddress);
		// data.funcreturn();
		cp = address;

		return this.interpret();
	};

	this.kill = function(){
		this.dead = true;
	};

	// angle parameter in deegrees
	function rotatePoint(pivot, point, angle) {
		// Get origin x, y
		var pivx = pivot[0];
		var pivy = pivot[1];
		// Get point x, y
		var ptx = point[0];
		var pty = point[1];
		// Get sin and cos of angle
		var s = Math.sin((angle) * (Math.PI/180));
		var c = Math.cos((angle) * (Math.PI/180));
		// Translate point back to origin
		ptx -= pivx;
		pty -= pivy;
		// Rotate point
		var newx = ptx * c - pty * s;
		var newy = ptx * s + pty * c;
		// Translate new point back
		newx += pivx;
		newy += pivy;
		// Create new point
		var newPt = [newx,newy];
		return newPt;
	}
}

MVM.opCodes = {
	STOREG: 0,
	LOADG: 	1,
	STOREL: 2,
	LOADL: 	3,
	LOADC: 	4,
	IADD: 	5,
	ISUB: 	6,
	IMUL: 	7,
	IDIV: 	8,
	IMOD: 	9,
	FADD: 	10,
	FSUB: 	11,
	FMUL: 	12,
	FDIV: 	13,
	FMOD: 	14,
	LOADIDX:15,
	SETIDX: 16,
	CMPEQ: 17,
	CMPLT: 18,
	CMPGT: 19,
	JUMP: 	20,
	JUMPT: 	21, 
	JUMPF: 	22,
	CALL: 	23, 
	RETURN: 24,
	LNDRAW: 25,
	PGDRAW: 26,
	RENDER: 27,
	CLEAR: 	28,
	PTADD: 	29,
	LNTOPG: 30,
	LNMUL:  31,
	EXIT: 	32,
	STORER: 33,
	LOADR:	34,
	POPSC:	35,
	PUSHSC:	36,
	RETURNVAL: 37,
	BAND:	38,
	BOR:	39,
	BNEG:	40,
	AGGR: 	41,
	WIDTH:	42,
	HEIGHT:	43,
	AUGPT:	44,
	SETCOLOUR:	45,
	TRANSLATEPT:	46, 
	TRANSLATESTRUCT:	47
};