var readPointer = 4;
var stack = [];

var ff = {
	jvm: {
		private: {}
	}
};

ff.jvm.private.getConstantPoolInfo = function(data, d) {
	var tag = sToU8b(read(d,1)), out;
	
	switch (tag) {
		case 1: // Utf8
			var len=sToU16b(read(d,2)), bytes=new Array(len), str="";
			for (var j=0; j<len; j++)
				str += String.fromCharCode(bytes[j] = sToU8b(read(d,1)));
			out = {tag: tag, length: len, bytes: bytes, str: str}; break;
		case 3: // Integer
			out = {tag: tag, bytes: sToU32b(read(d,4))}; break;
		case 4: // Float
			out = {tag: tag, bytes: sToF32b(read(d,4))}; break;
		case 5: // Long
			out = {tag: tag, hBytes: sToU32b(read(d,4)), lBytes: sToU32b(read(d,4))};
			break;
		case 6: // Double
			out = {tag: tag, hBytes: sToU32b(read(d,4)), lBytes: sToU32b(read(d,4))};
			break;
		case 7:	// Class
			out = {tag: tag, nameIdx: sToU16b(read(d,2))}; break;
		case 8: // String
			out = {tag: tag, stringIdx: sToU16b(read(d,2))}; break;
		case 9: case 10: case 11: // Fieldref, Methodref, InterfaceMethodref
			out = {tag: tag, classIdx: sToU16b(read(d,2)), nameAndTypeIdx: sToU16b(read(d,2))}; break;
		case 12: // NameAndType
			out = {tag: tag, nameIdx: sToU16b(read(d,2)), descIdx: sToU16b(read(d,2))}; break;
		default:
			log("Unknown constant pool tag: "+tag);
	}
	return out;
}
ff.jvm.private.getAttributeInfo = function(data, d) {
	var attrNameIdx = sToU16b(read(d,2));
	var attrLength = sToU32b(read(d,4));
	var attrName = data.constantPool[attrNameIdx-1].str;
	
	var out;
	
	if (attrName == 'Code') {
		var maxStack = sToU16b(read(d,2));
		var maxLocals = sToU16b(read(d,2));
		
		var codeLength = sToU32b(read(d,4));
		var code = [];
		for (var i=0; i<codeLength; i++)
			code[i] = sToU8b(read(d,1));
		
		var exceptionTableLength = sToU16b(read(d,2));
		var exceptionTable = [];
		for (var i=0; i<exceptionTableLength; i++) {
			exceptionTable[i] = {
				startPc: sToU16b(read(d,2)),
				endPc: sToU16b(read(d,2)),
				handlerPc: sToU16b(read(d,2)),
				catchType: sToU16b(read(d,2))
			};
		}
		
		var attrCount;
		var attrs = [];
		
		var rest = attrLength - 10 - codeLength -
				exceptionTableLength*8;
		read(d, rest);
		
	//	for (var i=0; i<attrCount; i++)
	//		attrs[i] = ff.jvm.private.getAttributeInfo(data, d);
		
		out = {
			nameIdx: attrNameIdx,
			length: attrLength,
			maxStack: maxStack,
			maxLocals: maxLocals,
			code: code,
			exceptions: exceptionTable,
			attributes: attrs
		}
	} else {	// Default attribute.
		var info = [];
		for (var i=0; i<attrLength; i++)
			info[i] = sToU8b(read(d,1));
		
		out = {
			nameIdx: attrNameIdx,
			length: attrLength,
			info: info
		};
	}
	
	return out;
}

ff.jvm.parseClass = function(d) {
	readPointer = 4;
	log("Längd: " + d.length);
	if (d.substr(0,4) != String.fromCharCode(0xCA,0xFE,0xBA,0xBE)) {
		log("Error: Invalid magic number: " +
				sToU16b(d.substr(0,4)).toString(16));
		return null;
	}
	
	var data = {
		constantPool: [],
		interfaces: [],
		fields: [],
		methods: [],
		attributes: []
	};
//	var constantPool=new Array(), interfaces=new Array(), fields=new Array(), methods=new Array(), attributes=new Array();
	
		// Parse some metadata...
	var minorVer = sToU16b(read(d,2));
	var majorVer = sToU16b(read(d,2));
	var cpCount  = sToU16b(read(d,2));
	
		// Parse constant pool data.
	for (var i=0; i<cpCount-1; i++) {
		data.constantPool[i] =
				ff.jvm.private.getConstantPoolInfo(data, d);
		
		if (data.constantPool[i].tag == 5 ||
				data.constantPool[i].tag == 6) {
			i++;
		}
	}
	
		// Parse info about 'this'.
	var flags = sToU16b(read(d,2));
	var thisIdx = sToU16b(read(d,2));
	var superIdx = sToU16b(read(d,2));
	var interfCount = sToU16b(read(d,2));
	
//	log("nextVals: "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2)));
	
		// Parse interfaces implemented by 'this'.
	log("Interfaces? " + interfCount);
	for (var i=0; i<interfCount; i++)
		data.interfaces[data.interfaces.length] = sToU16b(read(d,2));
	
		// Parse fields declared by 'this'.
	var fieldCount = sToU16b(read(d,2));
	for (var i=0; i<fieldCount; i++) {
		var flags = sToU16b(read(d,2)),
				nameIdx = sToU16b(read(d,2)),
				descIdx = sToU16b(read(d,2)),
				attrCount = sToU16b(read(d,2)),
				attrs = [];
		for (var j=0; j<attrCount; j++) {
			attrs[j] = ff.jvm.private.getAttributeInfo(data, d);
		}
		
		data.fields[data.fields.length] = {
			accFlags: flags,
			nameIdx: nameIdx,
			descIdx: descIdx,
			attrCount: attrCount,
			attrInfo: attrs
		};
	}
	
//	log("nextVals: "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2))+" "+sToU16b(read(d,2)));
	
		// Parse methods in 'this'.
	var methodCount = sToU16b(read(d,2));
	for (var i=0; i<methodCount; i++) {
		var flags = sToU16b(read(d,2)),
				nameIdx = sToU16b(read(d,2)),
				descIdx = sToU16b(read(d,2)),
				attrCount = sToU16b(read(d,2)),
				attrs = [];
		for (var j=0; j<attrCount; j++)
			attrs[j] = ff.jvm.private.getAttributeInfo(data, d);
		
		var desc = data.constantPool[descIdx-1].str;
		var nargs = 0;
		
		for (var j=0; j<desc.length; j++) {
			if (desc.charAt(j) == '[' || desc.charAt(j) == '(') {
				continue;
			} else if (desc.charAt(j) == ')') {
				break;
			} else if (desc.charAt(j) == 'L') {
				nargs++;
				for (; !(desc.charAt(j) == ';'); j++);
			} else {
				nargs++;
			}
		}
		
		data.methods[i] = {
			accFlags: flags,
			nameIdx: nameIdx,
			descIdx: descIdx,
			attrCount: attrCount,
			attrInfo: attrs,
			nArgs: nargs
		};
	}
	
	var attrCount = sToU16b(read(d,2));
	for (var i=0; i<attrCount; i++)
		data.attributes[i] = ff.jvm.private.getAttributeInfo(data, d);
	
		// Debugging output.
	log("Version: "+majorVer+" ("+minorVer+"). cpCount: "+cpCount);
	log("\nConstant pool:");
	
		// Debugging, print constant pool.
	for (var i=0; i<data.constantPool.length; i++) {	
		var msg = "\t" + (i<10 ? "&nbsp;" : "") + "[" + (i) + "] ";
		var thisInfo = data.constantPool[i];
		
		if (typeof(thisInfo) == 'undefined')
			continue;
		
		switch (thisInfo.tag) {
			case 1:
				msg += "Utf8: '" + thisInfo['str']+"'"; break;
			case 3:
				msg += "Integer: " + thisInfo['bytes']; break;
			case 4:
				msg += "Float: " + thisInfo['bytes']; break;
			case 5:
				msg += "Long: " + thisInfo['hBytes'] + " " +
						thisInfo['lBytes']; break;
			case 6:
				msg += "Double: " + thisInfo['hBytes'] + " " +
						thisInfo['lBytes']; break;
			case 7:
				msg += "Class: " + (thisInfo.nameIdx-1) +
						":'" + data.constantPool[thisInfo[
						'nameIdx']-1].str + "'"; break;
			case 8:
				msg += "String: (" + data.constantPool[
						thisInfo['stringIndex']-1] + ")"; break;
			case 9: case 10: case 11:
				if (thisInfo.tag == 9)
					msg += "Fieldref: ";
				else if (thisInfo.tag == 10)
					msg += "Methodref: ";
				else if (thisInfo.tag == 11)
					msg += "InterfaceMethodref: ";
				
				msg += "Class " + (thisInfo.classIdx-1) +
						", NameAndType " + (thisInfo.nameAndTypeIdx-1);
						break;
			case 12:
				msg += "NameAndType: " + (thisInfo.nameIdx-1) + ":'" +
						data.constantPool[thisInfo.nameIdx-1].str +
						"' (" + (thisInfo.descIdx-1) + ":'" +
						data.constantPool[thisInfo.descIdx-1].str +
						"')"; break;
		}
		log(msg);
	}
	
	log("\ninterfaces: " + data.interfaces + " (" + interfCount + ")");
	log("flags: " + flags.toString(2));
	log("thisIdx: " + (thisIdx-1) + ":'" + data.constantPool[data.
			constantPool[thisIdx-1].nameIdx-1].str + "'; superIdx: " +
			(superIdx-1) + ":'" + data.constantPool[data.constantPool[
			superIdx-1].nameIdx-1].str + "'");
	
	log("\nfields: "+fieldCount);
	for (var i=0; i<data.fields.length; i++) {
		log("\t> fields[" + i + "]: name:(" + data.fields[i].nameIdx +
				") " + data.constantPool[data.fields[i].nameIdx-1].str +
				"; desc:(" + data.fields[i].descIdx + ") " +
				data.constantPool[data.fields[i].descIdx-1].str);
		for (var j=0; j<data.fields[i].attrCount; j++)
			log("\t\t> fields[" + i + "]['attrInfo'][" + j +
					"]: name:" + data.constantPool[data.fields[i].
					attrInfo[j].nameIdx-1].str + "; length:" +
					data.fields[i].attrInfo[j].length + "; info:" +
					data.fields[i].attrInfo[j].info);
	}
	
	
	log("\nmethods: " + methodCount);
	for (var i=0; i<data.methods.length; i++) {
		log("\t> methods[" + i + "]: name:(" + data.methods[i].nameIdx + 
				") " + data.constantPool[data.methods[i].nameIdx-1].str + 
				"; desc:(" + data.methods[i].descIdx + ") " + 
				data.constantPool[data.methods[i].descIdx-1].str);
		for (var j=0; j<data.methods[i].attrCount; j++) {
			log("\t\t> methods[" + i + "]['attrInfo'][" + j +
					"]: name:" + data.constantPool[data.methods[i].
					attrInfo[j].nameIdx-1].str + "; length:" +
					data.methods[i].attrInfo[j].length);// + "; info:" +
				//	data.methods[i].attrInfo[j].info);
			if (data.constantPool[data.methods[i].attrInfo[j].nameIdx
					-1].str == 'Code') {
				log("\t\t\t- code: [" + data.methods[i].attrInfo[j].
						code + "]");
			}
		}
	}
	
	log("\nattributes: "+attrCount);
	for (var i=0; i<data.attributes.length; i++)
		log("\t> attributes[" + i + "]: name:" + data.constantPool[
		data.attributes[i].nameIdx-1].str + "; length:" + data.
		attributes[i].length + "; info:" + data.attributes[i].info);
	
	log("\n----------------------------------");
	
	data.className = data.constantPool[data.constantPool[
			thisIdx-1].nameIdx-1].str;
	data.superName = data.constantPool[data.constantPool[
			superIdx-1].nameIdx-1].str
	
	return data;
}

ff.jvm.loadClass = function(name) {
	var xhr = new XMLHttpRequest();
	
	log("Hämtar '" + "get.php?f=classes/" + name + ".class'...");
	xhr.open("GET", "get.php?f=classes/" + name + ".class", false);
	xhr.send(null);
	
	if (xhr.status == 200) {
		log("Hämtat fil!!1");
		log("Fil: " + xhr.responseText);
		return ff.jvm.parseClass(debase64(xhr.responseText.replace(
				/\n\+/g, "")));
	} else {
		log("aaaaaaaurgh, status: " + xhr.status);
		throw new Error("Class file not successfully loaded!");
	}
}

ff.jvm.executeMain = function(classFile) {
	var classes = {};
	var mainData = ff.jvm.parseClass(classFile);
	
	classes[mainData.className] = mainData;
	classes.getClass = function(name) {
		if (typeof(classes[name]) != 'undefined') {
		//	log("Nice, returnerar " + name);
			return classes[name];
		} else {
		//	log("Måste ladda " + name);
			return ff.jvm.loadClass(name);
		}
	}
	classes.getMethod = function(class, name, desc) {
		var c = classes.getClass(class);
		
		for (var i=0; i<c.methods.length; i++) {
			if (c.constantPool[c.methods[i].nameIdx-1].str ==
					name && c.constantPool[c.methods[i].descIdx
					-1].str == desc) {
				return c.methods[i];
			}
		}
		return null;
	}
	classes.getField = function(class, field) {
		var c = classes.getClass(class);
		
		for (var i=0; i<c.methods.length; i++) {
			if (c.constantPool[c.fields[i].nameIdx-1].str == field) {
				return c.fields[i];
			}
		}
		throw new Error("Field " + field + " not found in class " +
				class);
	}
	classes.getMethodCode = function(class, name, desc) {
		var c = classes.getClass(class);
		
		for (var i=0; i<c.methods.length; i++) {
			for (var j=0; j<c.methods[i].attrInfo.length; j++) {
				if (c.constantPool[c.methods[i].attrInfo[j].nameIdx-
						1].str == 'Code' && c.constantPool[c.
						methods[i].nameIdx-1].str == name &&
						c.constantPool[c.methods[i].descIdx-1].str
						== desc) {
					return c.methods[i].attrInfo[j].code;
				}
			}
		}
		return null;
	}
	
	interpret(classes, mainData.className, 'main',
			'([Ljava/lang/String;)V', []);
}

function interpret(classes, className, func, desc, args) {
//	var vars = new Array(5);
	var fcc = String.fromCharCode;
	
	var data = classes.getClass(className);
	var code = classes.getMethodCode(className, func, desc);
	
	var fr = {
		vars: new Array(5),
		stack: []
	};
	for (var i=0; i<args.length; i++)
		fr.vars[i] = args[i];
	
	for (var k=0; k<code.length; k++) {
		switch (code[k]) {
			case 0x00: log("nop"); break;
			case 0x01: log("aconst_null"); break;
			case 0x02: log("iconst_m1");
				fr.stack.push(-1); break;
			case 0x03: log("iconst_0");
				fr.stack.push(0); break;
			case 0x04: log("iconst_1");
				fr.stack.push(1); break;
			case 0x05: log("iconst_2");
				fr.stack.push(2); break;
			case 0x06: log("iconst_3");
				fr.stack.push(3); break;
			case 0x07: log("iconst_4");
				fr.stack.push(4); break;
			case 0x08: log("iconst_5");
				fr.stack.push(5); break;
			case 0x09: log("lconst_0");
				fr.stack.push(0); break;
			case 0x0A: log("lconst_1");
				fr.stack.push(1); break;
			case 0x0B: log("fconst_0");
				fr.stack.push(0); break;
			case 0x0C: log("fconst_1");
				fr.stack.push(1); break;
			case 0x0D: log("fconst_2");
				fr.stack.push(2); break;
			case 0x0E: log("dconst_0");
				fr.stack.push(0);
				fr.stack.push(0);
				break;
			case 0x0F: log("dconst_1");
				fr.stack.push(1);
				fr.stack.push(0);
				break;
			case 0x10: log("bipush "+cToI8b(code[k+1]));
				fr.stack.push(cToI8b(code[++k])); break;
			case 0x11: log("sipush " + sToI16b(fcc(code[k+2],code[k+2])));
				fr.stack.push(sToI16b(fcc(code[++k],code[++k]))); break;
			case 0x12: log("ldc " + (code[k+1]-1));
				var d = data.constantPool[code[++k]-1]
			//	if (d.tag == 4)
			//		fr.stack.push(iToF32b(d.bytes));
			//	else if (d.tag == 5)
					fr.stack.push(d.bytes);
			//	else
			//		log(" ----- Unknown ldc tag: " + d.tag);
				break;
			case 0x13: log("ldc_w"); break;
			case 0x14: log("ldc2_w"); break;
			case 0x15: log("iload"); break;
			case 0x16: log("lload"); break;
			case 0x17: log("fload"); break;
			case 0x18: log("dload"); break;
			case 0x19: log("aload"); break;
			case 0x1A: log("iload_0");
				fr.stack.push(fr.vars[0]); break;
			case 0x1B: log("iload_1");
				fr.stack.push(fr.vars[1]); break;
			case 0x1C: log("iload_2");
				fr.stack.push(fr.vars[2]); break;
			case 0x1D: log("iload_3");
				fr.stack.push(fr.vars[3]); break;
			case 0x1E: log("lload_0");
				fr.stack.push((fr.vars[0] & 0xFFFFFFFF) + ((fr.vars[1] &
						0xFFFFFFFF) << 8)); break;
			case 0x1F: log("lload_1");
				fr.stack.push((fr.vars[1] & 0xFFFFFFFF) + ((fr.vars[2] &
						0xFFFFFFFF) << 8)); break;
			case 0x20: log("lload_2");
				fr.stack.push((fr.vars[2] & 0xFFFFFFFF) + ((fr.vars[3] &
						0xFFFFFFFF) << 8)); break;
			case 0x21: log("lload_3");
				fr.stack.push((fr.vars[3] & 0xFFFFFFFF) + ((fr.vars[4] &
						0xFFFFFFFF) << 8)); break;
			case 0x22: log("fload_0 " + fr.vars[0]);
				fr.stack.push(fr.vars[0]); break;
			case 0x23: log("fload_1");
				fr.stack.push(fr.vars[1]); break;
			case 0x24: log("fload_2");
				fr.stack.push(fr.vars[2]); break;
			case 0x25: log("fload_3");
				fr.stack.push(fr.vars[3]); break;
			case 0x26: log("dload_0");
				fr.stack.push(fr.vars[0]); break;
			case 0x27: log("dload_1");
				fr.stack.push(fr.vars[1]); break;
			case 0x28: log("dload_2");
				fr.stack.push(fr.vars[2]); break;
			case 0x29: log("dload_3");
				fr.stack.push(fr.vars[3]); break;
			case 0x2A: log("aload_0"); break;
			case 0x2B: log("aload_1"); break;
			case 0x2C: log("aload_2"); break;
			case 0x2D: log("aload_3"); break;
			case 0x2E: log("iaload"); break;
			case 0x2F: log("laload"); break;
			case 0x30: log("faload"); break;
			case 0x31: log("daload"); break;
			case 0x32: log("aaload"); break;
			case 0x33: log("baload"); break;
			case 0x34: log("caload"); break;
			case 0x35: log("saload"); break;
			case 0x36: log("istore"); break;
			case 0x37: log("lstore"); break;
			case 0x38: log("fstore"); break;
			case 0x39: log("dstore"); break;
			case 0x3A: log("astore"); break;
			case 0x3B: log("istore_0");
				fr.vars[0] = fr.stack.pop(); break;
			case 0x3C: log("istore_1");
				fr.vars[1] = fr.stack.pop(); break;
			case 0x3D: log("istore_2");
				fr.vars[2] = fr.stack.pop(); break;
			case 0x3E: log("istore_3");
				fr.vars[3] = fr.stack.pop(); break;
			case 0x3F: log("lstore_0");
				var n = fr.stack.pop();
				fr.vars[0] = n & 0xFFFFFFFF;
				fr.vars[1] = (n & 0xFFFFFFFF00000000) >> 8; break;
			case 0x40: log("lstore_1");
				var n = fr.stack.pop();
				fr.vars[1] = n & 0xFFFFFFFF;
				fr.vars[2] = (n & 0xFFFFFFFF00000000) >> 8; break;
			case 0x41: log("lstore_2");
				var n = fr.stack.pop();
				fr.vars[2] = n & 0xFFFFFFFF;
				fr.vars[3] = (n & 0xFFFFFFFF00000000) >> 8; break;
			case 0x42: log("lstore_3");
				var n = fr.stack.pop();
				fr.vars[3] = n & 0xFFFFFFFF;
				fr.vars[4] = (n & 0xFFFFFFFF00000000) >> 8; break;
			case 0x43: log("fstore_0");
				fr.vars[0] = fr.stack.pop(); break;
			case 0x44: log("fstore_1");
				fr.vars[1] = fr.stack.pop(); break;
			case 0x45: log("fstore_2");
				fr.vars[2] = fr.stack.pop(); break;
			case 0x46: log("fstore_3");
				fr.vars[3] = fr.stack.pop(); break;
			case 0x47: log("dstore_0");
				fr.vars[0] = fr.stack.pop(); break;
			case 0x48: log("dstore_1");
				fr.vars[1] = fr.stack.pop(); break;
			case 0x49: log("dstore_2");
				fr.vars[2] = fr.stack.pop(); break;
			case 0x4A: log("dstore_3");
				fr.vars[3] = fr.stack.pop(); break;
			case 0x4B: log("astore_0"); break;
			case 0x4C: log("astore_1"); break;
			case 0x4D: log("astore_2"); break;
			case 0x4E: log("astore_3"); break;
			case 0x4F: log("iastore"); break;
			case 0x50: log("lastore"); break;
			case 0x51: log("fastore"); break;
			case 0x52: log("dastore"); break;
			case 0x53: log("aastore"); break;
			case 0x54: log("bastore"); break;
			case 0x55: log("castore"); break;
			case 0x56: log("sastore"); break;
			case 0x57: log("pop [" + fr.stack.pop() + "]");
			//	fr.stack.pop();
				break;
			case 0x58: log("pop2");
				fr.stack.pop();
				fr.stack.pop();
				break;
			case 0x59: log("dup"); break;
			case 0x5A: log("dup_x1"); break;
			case 0x5B: log("dup_x2"); break;
			case 0x5C: log("dup2"); break;
			case 0x5D: log("dup2_x1"); break;
			case 0x5E: log("dup2_x2"); break;
			case 0x5F: log("swap"); break;
			case 0x60: log("iadd");
				var operand = fr.stack.pop();
				fr.stack.push(fr.stack.pop() + operand);
				break;
			case 0x61: log("ladd"); break;
			case 0x62: log("fadd"); break;
			case 0x63: log("dadd"); break;
			case 0x64: log("isub");
				var operand = fr.stack.pop();
				fr.stack.push(fr.stack.pop() - operand);
				break;
			case 0x65: log("lsub"); break;
			case 0x66:
				var operand = fr.stack.pop();
				fr.stack.push(fr.stack.pop() - operand);
				
				var tmp = fr.stack.pop();
				log("fsub " + tmp);
				fr.stack.push(tmp);
				break;
			case 0x67: log("dsub"); break;
			case 0x6A: case 0x68: 
				if (code[k] == 0x68)
					log("imul");
				else if (code[k] == 0x6A)
					log("fmul");
				var operand = fr.stack.pop();
				log(" -- operand: " + operand);
				fr.stack.push(fr.stack.pop() * operand);
				break;
			case 0x69: log("lmul"); break;
				
				break;
			case 0x6B: log("dmul"); break;
			case 0x6C: log("idiv");
				var operand = fr.stack.pop();
				fr.stack.push(fr.stack.pop() / operand);
				break;
			case 0x6D: log("ldiv"); break;
			case 0x6E: log("fdiv"); break;
			case 0x6F: log("ddiv"); break;
			case 0x70: log("irem"); break;
			case 0x71: log("lrem"); break;
			case 0x72: log("frem"); break;
			case 0x73: log("drem"); break;
			case 0x74: log("ineg"); break;
			case 0x75: log("lneg"); break;
			case 0x76: log("fneg"); break;
			case 0x77: log("dneg"); break;
			case 0x78: log("ishl"); break;
			case 0x79: log("lshl"); break;
			case 0x7A: log("ishr"); break;
			case 0x7B: log("lshr"); break;
			case 0x7C: log("iushr"); break;
			case 0x7D: log("lushr"); break;
			case 0x7E: log("iand"); break;
			case 0x7F: log("land"); break;
			case 0x80: log("ior"); break;
			case 0x81: log("lor"); break;
			case 0x82: log("ixor"); break;
			case 0x83: log("lxor"); break;
			case 0x84: log("iinc " + code[k+1] + " " + code[k+2]);
				fr.vars[code[++k]] += code[++k]; break;
			case 0x85: log("i2l"); break;
			case 0x86: log("i2f"); break;
			case 0x87: log("i2d"); break;
			case 0x88: log("l2i"); break;
			case 0x89: log("l2f"); break;
			case 0x8A: log("l2d"); break;
			case 0x8B: log("f2i"); break;
			case 0x8C: log("f2l"); break;
			case 0x8D: log("f2d"); break;
			case 0x8E: log("d2i"); break;
			case 0x8F: log("d2l"); break;
			case 0x90: log("d2f"); break;
			case 0x91: log("i2b"); break;
			case 0x92: log("i2c"); break;
			case 0x93: log("i2s"); break;
			case 0x94: log("lcmp"); break;
			case 0x95: case 0x96:
				var v1 = fr.stack.pop();
				var v2 = fr.stack.pop();
				var ifNaN;
				if (code[k] == 0x95) {
					ifNaN = -1;
					log("fcmpl");
				} else {
					ifNan = 1;
					log("fcmpg");
				}
				
				if (v1.toString() == 'NaN' ||
						v2.toString() == 'NaN')
					fr.stack.push(ifNaN);
				if (v1 == v2) {
					log(v1 + " == " + v2);
					fr.stack.push(0);
				} else if (v1 > v2) {
					log(v1 + " > " + v2);
					fr.stack.push(1);
				} else if (v1 < v2) {
					log(v1 + " < " + v2);
					fr.stack.push(-1);
				}
				break;
				break;
			case 0x97: log("dcmpl"); break;
			case 0x98: log("dcmpg"); break;
			case 0x99: case 0x9A: case 0x9B: case 0x9C:
			case 0x9D: case 0x9E:
				var v = fr.stack.pop();
				var jump;
				
				if (code[k] == 0x99) {
					jump = v == 0;
					log("ifeq");
				} else if (code[k] == 0x9A) {
					jump = v != 0;
					log("ifne (" + v + " != 0: " + jump + ")");
				} else if (code[k] == 0x9B) {
					jump = v < 0;
					log("iflt");
				} else if (code[k] == 0x9C) {
					jump = v >= 0;
					log("ifge");
				} else if (code[k] == 0x9D) {
					jump = v > 0;
					log("ifgt");
				} else if (code[k] == 0x9E) {
					jump = v <= 0;
					log("ifle");
				}
				
				if (jump)
					k += sToI16b(fcc(code[++k],code[++k])) - 1;
				else
					k += 2;
				break;
			case 0x9F: case 0xA0: case 0xA1: case 0xA2:
					case 0xA3: case 0xA4:
				var toLog, jump;
				var v1 = fr.stack.pop();
				var v2 = fr.stack.pop();
				
				if (code[k] == 0x9F) {
					jump = fr.stack.pop() == fr.stack.pop();
					toLog = "if_icmpeq";
				} else if (code[k] == 0xA0) {
					jump = v2 != v1;
					toLog = "if_icmpne";
				} else if (code[k] == 0xA1) {
					jump = v2 < v1;
					toLog = "if_icmplt";
				} else if (code[k] == 0xA2) {
					jump = v2 >= v1;
					toLog = "if_icmpge";
				} else if (code[k] == 0xA3) {
					jump = v2 > v1;
					toLog = "if_icmpgt";
				} else if (code[k] == 0xA4) {
					jump = v2 <= v1;
					toLog = "if_icmple";
				}
				log(toLog + " " + sToI16b(fcc(code[k+1],code[k+2])));
				log("  - " + v1 + " op " + v2 + ": " + jump);
				
				if (jump)
					k += sToI16b(fcc(code[k+1],
							code[k+2])) - 1;
				else
					k += 2;
				break;
			case 0xA5: log("if_acmpeq"); break;
			case 0xA6: log("if_acmpne"); break;
			case 0xA7: log("goto " + sToI16b(fcc(code[k+1],code[k+2])));
				k += sToI16b(fcc(code[k+1],code[k+2])) - 1;
				log(" -- code[k+1]: " + code[k+1]);
				break;
			case 0xA8: log("jsr"); break;
			case 0xA9: log("ret"); break;
			case 0xAA: log("tableswitch"); break;
			case 0xAB: log("lookupswitch"); break;
			case 0xAC: case 0xAD: case 0xAE: case 0xAF:
				var val = fr.stack.pop();
				if (code[k] == 0xAC)
					log("ireturn " + val);
				else if (code[k] == 0xAD)
					log("lreturn " + val);
				else if (code[k] == 0xAE)
					log("freturn " + val);
				else if (code[k] == 0xAF)
					log("dreturn " + val);
				return val;
			case 0xB0: log("areturn"); break;
			case 0xB1: log("return");
				log("\nStack: " + fr.stack);
				log("Vars: " + fr.vars);
				return;
			case 0xB2: log("getstatic");
				var pData = data.constantPool[sToU16b(
						fcc(code[++k], code[++k]))-1];
				var className = data.constantPool[data.
						constantPool[pData.classIdx-1].
						nameIdx-1].str;
				var fieldName = data.constantPool[data.
						constantPool[pData.nameAndTypeIdx-
						1].nameIdx-1].str;
				var field = classes.getField(className, fieldName);
				log("Testy: " + field);
				break;
			case 0xB3: log("putstatic"); break;
			case 0xB4: log("getfield"); break;
			case 0xB5: log("putfield"); break;
			case 0xB6: log("invokevirtual"); break;
			case 0xB7: log("invokespecial"); break;
			case 0xB8:
				var methodref = data.constantPool[sToU16b(
						fcc(code[++k], code[++k]))-1];
				var class = data.constantPool[data.
						constantPool[methodref.classIdx-1].
						nameIdx-1].str;
				var methodName = data.constantPool[data.
						constantPool[methodref.
						nameAndTypeIdx-1].nameIdx-1].str;
				var methodDesc = data.constantPool[data.
						constantPool[methodref.
						nameAndTypeIdx-1].descIdx-1].str;
				
				var nArgs = classes.getMethod(class, methodName,
						methodDesc).nArgs;
				var args = [];
				for (var i=0; i<nArgs; i++)
					args[i] = fr.stack.pop();
				log("invokestatic [" + class + " " +
						methodName + " " +
						methodDesc + "]");
				var value = interpret(classes, class, methodName,
						methodDesc, args);
				if (typeof(value) != 'undefined')
					fr.stack.push(value);
				break;
			case 0xB9: log("invokeinterface"); break;
			case 0xBA: log("xxxunusedxxx"); break;
			case 0xBB: log("new"); break;
			case 0xBC: log("newarray"); break;
			case 0xBD: log("anewarray"); break;
			case 0xBE: log("arraylength"); break;
			case 0xBF: log("athrow"); break;
			case 0xC0: log("checkcast"); break;
			case 0xC1: log("instanceof"); break;
			case 0xC2: log("monitorenter"); break;
			case 0xC3: log("monitorexit"); break;
			case 0xC4: log("wide"); break;
			case 0xC5: log("multianewarray"); break;
			case 0xC6: log("ifnull"); break;
			case 0xC7: log("ifnonnull"); break;
			case 0xC8: log("goto_w"); break;
			case 0xC9: log("jsr_w"); break;
			
				// Reserved opcodes.
			case 0xCA: log("breakpoint"); break;
			case 0xFE: log("impdep1"); break;
			case 0xFF: log("impdep2"); break;
		}
	}
}

//function cca(s,i){return s.charCodeAt(i)}
function nToF(s,e,f,el,fl,o){
	var n = 0;
	for (var i=0; i<fl; i++)
		if ((f & (1<<i)) != 0)
			n += Math.pow(2, i-fl);
	return n*-(s*4-2)*Math.pow(2,e-el);
}

function read(s, n){return s.substr((readPointer+=n)-n, n)}
function sToU8b(s){return s.charCodeAt(0)}
function sToU16b(s){return s.charCodeAt(0)*256+s.charCodeAt(1)}
function sToU32b(s){return s.charCodeAt(0)*16777216+s.charCodeAt(1)*65536+s.charCodeAt(2)*256+s.charCodeAt(3)}
function sToI16b(s){n=s.charCodeAt(0)*256+s.charCodeAt(1);return n<32767?n:-((~n&0xFFFF)+1)}
function cToI8b(c){return c<128?c:-((~c&0xFF)+1)}

function sToF32b(s){
	var b=sToU32b(s),s=b>>31,e=(b>>23)&0xFF,f=(b&0x7FFFFF)|(1<<23);
	if (e==0) {
		if (f==0)
			return s==0?0:-0;
		else
			return nToF(s,1,f,127,24,0);
	} else if (e==255) {
		if (f==0)
			return s==0?0/0:-0/0;
		else
			return Integer.parseInt("a");
	} else {
		return nToF(s,e,f,127,24,1);
	}
}
function iToF32b(i){
	var s=i>>31,e=(i>>23)&0xFF,f=(i&0x7FFFFF)|(1<<23);
	
	if (e==0) {
		if (f==0)
			return s==0?0:-0;
		else
			return nToF(s,1,f,127,24,0);
	} else if (e==255) {
		if (f==0)
			return s==0?0/0:-0/0;
		else
			return Integer.parseInt("a");
	} else {
		return nToF(s,e,f,127,24,1);
	}
}

function ssToF64b(s,t){
	var l=sToU32b(t),s=h>>31,e=(h>>20)&0xFF,f=((sToU32b(s)&0xFFFFF)|(1<<20))<<32|l;
	if (e==0) {
		if (f==0)
			return s==0?0:-0;
		else
			return nToF(s,1,f,1023,53,0);
	} else if (e==255) {
		if (f==0)
			return s==0?0/0:-0/0;
		else
			return Integer.parseInt("a");
	} else {
		return nToF(s,e,f,1023,53,1);
	}
}