class VM {

    public void step(Program program) {

        program.fullTrace();

        if (CONFIG.vmTrace())
            program.saveOpTrace();
    	
        try {
            OpCode op = OpCode.code(program.getCurrentOp());
            
            if (op == null)
            	throw program.new IllegalOperationException();
            
            program.setLastOp(op.val());

            long oldMemSize = program.getMemSize();
            BigInteger newMemSize = BigInteger.ZERO;
            Stack<DataWord> stack = program.getStack();

            String hint = "";
            long callGas = 0, memWords = 0; // parameters for logging
            long gasCost = GasCost.STEP;
            long gasBefore = program.getGas().longValue();
            int stepBefore = program.getPC();
            
            program.stackRequire(op.require());
            
    		// Calculate fees and spend gas
            switch (op) {
                case STOP: case SUICIDE:
                    // The ops that don't charge by step
                	gasCost = GasCost.STOP;
                    break;
        		case SSTORE:
        			DataWord newValue = stack.get(stack.size()-2);
                    DataWord oldValue =  program.storageLoad(stack.peek());
                    if (oldValue == null && !newValue.isZero())
                    	gasCost = GasCost.SSTORE * 2;
                    else if (oldValue != null && newValue.isZero())
                        gasCost = GasCost.SSTORE * 0;
                    else
                        gasCost = GasCost.SSTORE;
        			break;
                case SLOAD:
                    gasCost = GasCost.SLOAD;
                    break;
                case BALANCE:
                    gasCost = GasCost.BALANCE;
                    break;
                    
        		// These all operate on memory and therefore potentially expand it:
        		case MSTORE:
        			newMemSize = memNeeded(stack.peek(), new DataWord(32));
        			break;
        		case MSTORE8:
        			newMemSize = memNeeded(stack.peek(), new DataWord(1));
        			break;
        		case MLOAD:
        			newMemSize = memNeeded(stack.peek(), new DataWord(32));
        			break;
        		case RETURN:
        			newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2));
        			break;
        		case SHA3:
        			gasCost = GasCost.SHA3;
        			newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-2));
        			break;
        		case CALLDATACOPY:
        			newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
        			break;
        		case CODECOPY:
        			newMemSize = memNeeded(stack.peek(), stack.get(stack.size()-3));
        			break;
        		case EXTCODECOPY:
        			newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-4));
        			break;
        		case CALL: case CALLCODE:
        			gasCost = GasCost.CALL;
        			DataWord callGasWord = stack.get(stack.size()-1);
        			if(callGasWord.compareTo(program.getGas()) == 1) {
        				throw program.new OutOfGasException();
                    }
        			callGas = callGasWord.longValue();
        			BigInteger in = memNeeded(stack.get(stack.size()-4), stack.get(stack.size()-5)); // in offset+size
    				BigInteger out = memNeeded(stack.get(stack.size()-6), stack.get(stack.size()-7)); // out offset+size
        			newMemSize = in.max(out);
        			break;
        		case CREATE:
        			gasCost = GasCost.CREATE;
        			newMemSize = memNeeded(stack.get(stack.size()-2), stack.get(stack.size()-3));
        			break;
                default:
                    break;
            }
            program.spendGas(gasCost, op.name());
            
            // Avoid overflows
            if(newMemSize.compareTo(MAX_GAS) == 1) {
            	throw program.new OutOfGasException();
            }
            
            // memory gas calc
            long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32;            
	        if (memoryUsage > oldMemSize) {
	        	memWords = (memoryUsage - oldMemSize) / 32;
	        	long memGas = GasCost.MEMORY * memWords;
				program.spendGas(memGas, op.name() + " (memory usage)");
				gasCost += memGas;
	        }

			// Log debugging line for VM
    		if(program.getNumber().intValue() == CONFIG.dumpBlock())
    			this.dumpLine(op, gasBefore, gasCost+callGas, memWords, program);
	    		
            // Execute operation
            switch (op) {
                /**
                 * Stop and Arithmetic Operations
                 */
                case STOP:{
                    program.setHReturn(ByteBuffer.allocate(0));
                    program.stop();
                }	break;
                case ADD:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " + " + word2.value();

                    word1.add(word2);
                    program.stackPush(word1);
                    program.step();

                }	break;
                case MUL:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " * " + word2.value();

                    word1.mul(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case SUB:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " - " + word2.value();

                    word1.sub(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case DIV:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " / " + word2.value();

                    word1.div(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case SDIV:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.sValue() + " / " + word2.sValue();

                    word1.sDiv(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case MOD:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " % " + word2.value();

                    word1.mod(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case SMOD:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.sValue() + " #% " + word2.sValue();

                    word1.sMod(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case EXP:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " ** " + word2.value();

                    word1.exp(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case NEG:{
                	DataWord word1 = program.stackPop();
                    word1.negate();

                    if (logger.isInfoEnabled())
                        hint = "" + word1.value();

                    program.stackPush(word1);
                    program.step();
                }	break;
                case LT:{
                	// TODO: can be improved by not using BigInteger
                    DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " < " + word2.value();

                    if (word1.value().compareTo(word2.value()) == -1) {
                        word1.and(DataWord.ZERO);
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }
                    program.stackPush(word1);
                    program.step();
                }	break;
                case SLT:{
                	// TODO: can be improved by not using BigInteger
                    DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.sValue() + " < " + word2.sValue();

                    if (word1.sValue().compareTo(word2.sValue()) == -1) {
                        word1.and(DataWord.ZERO);
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }
                    program.stackPush(word1);
                    program.step();
                }	break;
                case SGT:{
                	// TODO: can be improved by not using BigInteger
                    DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.sValue() + " > " + word2.sValue();

                    if (word1.sValue().compareTo(word2.sValue()) == 1) {
                        word1.and(DataWord.ZERO);
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }
                    program.stackPush(word1);
                    program.step();
                }	break;
                case GT:{
                	// TODO: can be improved by not using BigInteger
                    DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " > " + word2.value();

                    if (word1.value().compareTo(word2.value()) == 1) {
                        word1.and(DataWord.ZERO);
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }
                    program.stackPush(word1);
                    program.step();
                }	break;
                case EQ:{
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " == " + word2.value();

                    if (word1.xor(word2).isZero()) {
                        word1.and(DataWord.ZERO);
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }
                    program.stackPush(word1);
                    program.step();
                }	break;
                case NOT: {
                	DataWord word1 = program.stackPop();
                    if (word1.isZero()) {
                        word1.getData()[31] = 1;
                    } else {
                        word1.and(DataWord.ZERO);
                    }

                    if (logger.isInfoEnabled())
                        hint = "" + word1.value();

                    program.stackPush(word1);
                    program.step();
                }	break;

                /**
                 * Bitwise Logic Operations
                 */
                case AND:{              	
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " && " + word2.value();

                    word1.and(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case OR: {             	
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " || " + word2.value();

                    word1.or(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case XOR: {              	
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = word1.value() + " ^ " + word2.value();

                    word1.xor(word2);
                    program.stackPush(word1);
                    program.step();
                }	break;
                case BYTE:{              	
                	DataWord word1 = program.stackPop();
                    DataWord word2 = program.stackPop();
                    DataWord result = null;
                    if (word1.value().compareTo(_32_) == -1) {
                        byte tmp = word2.getData()[word1.intValue()];
                        word2.and(DataWord.ZERO);
                        word2.getData()[31] = tmp;
                        result = word2;
                    } else {
                        result = new DataWord();
                    }

                    if (logger.isInfoEnabled())
                        hint = "" + result.value();

                    program.stackPush(result);
                    program.step();
                }	break;
				case ADDMOD:{
                	DataWord word1 = program.stackPop();
                	DataWord word2 = program.stackPop();
                	DataWord word3 = program.stackPop();
                	word1.addmod(word2, word3);
                	program.stackPush(word1);
                	program.step();
				}	break;
				case MULMOD:{
                	DataWord word1 = program.stackPop();
                	DataWord word2 = program.stackPop();
                	DataWord word3 = program.stackPop();
                	word1.mulmod(word2, word3);
                	program.stackPush(word1);
                	program.step();
				}	break;

                /**
                 * SHA3
                 */
                case SHA3:{               	
                	DataWord memOffsetData  = program.stackPop();
                    DataWord lengthData     = program.stackPop();
                    ByteBuffer buffer = program.memoryChunk(memOffsetData, lengthData);

                    byte[] encoded = HashUtil.sha3(buffer.array());
                    DataWord word = new DataWord(encoded);

                    if (logger.isInfoEnabled())
                        hint = word.toString();

                    program.stackPush(word);
                    program.step();
                }	break;

                /**
                 * Environmental Information
                 */
                case ADDRESS:{
                    DataWord address = program.getOwnerAddress();

                    if (logger.isInfoEnabled())
                        hint = "address: " + Hex.toHexString(address.getLast20Bytes());

                    program.stackPush(address);
                    program.step();
                }	break;
                case BALANCE:{
                	DataWord address = program.stackPop();
                    DataWord balance = program.getBalance(address);

                    if (logger.isInfoEnabled())
						hint = "address: "
								+ Hex.toHexString(address.getLast20Bytes())
								+ " balance: " + balance.longValue();

                    program.stackPush(balance);
                    program.step();
                }	break;
                case ORIGIN:{
                    DataWord originAddress = program.getOriginAddress();

                    if (logger.isInfoEnabled())
                        hint = "address: " + Hex.toHexString(originAddress.getLast20Bytes());

                    program.stackPush(originAddress);
                    program.step();
                }	break;
                case CALLER:{
                    DataWord callerAddress = program.getCallerAddress();

                    if (logger.isInfoEnabled())
                        hint = "address: " + Hex.toHexString(callerAddress.getLast20Bytes());

                    program.stackPush(callerAddress);
                    program.step();
                }	break;
                case CALLVALUE:{
                    DataWord callValue = program.getCallValue();

                    if (logger.isInfoEnabled())
                        hint = "value: " + callValue;

                    program.stackPush(callValue);
                    program.step();
                }	break;
                case CALLDATALOAD:{         	
                	DataWord dataOffs  = program.stackPop();
                    DataWord value = program.getDataValue(dataOffs);

                    if (logger.isInfoEnabled())
                        hint = "data: " + value;

                    program.stackPush(value);
                    program.step();
                }	break;
                case CALLDATASIZE:{
                    DataWord dataSize = program.getDataSize();

                    if (logger.isInfoEnabled())
                        hint = "size: " + dataSize.value();

                    program.stackPush(dataSize);
                    program.step();
                }	break;
                case CALLDATACOPY:{             	
                    DataWord memOffsetData  = program.stackPop();
                    DataWord dataOffsetData = program.stackPop();
                    DataWord lengthData     = program.stackPop();
                    
                    byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);

                    if (logger.isInfoEnabled())
                        hint = "data: " + Hex.toHexString(msgData);

                    program.memorySave(memOffsetData.intValue(), msgData);
                    program.step();
                }	break;
                case CODESIZE: case EXTCODESIZE: {
                	
                	int length;
                	if (op == OpCode.CODESIZE)
                		length = program.getCode().length;
                	else {
                		DataWord address = program.stackPop();
                		length = program.getCodeAt(address).length;
                	}
                    DataWord codeLength = new DataWord(length);

                    if (logger.isInfoEnabled())
                        hint = "size: " + length;

                    program.stackPush(codeLength);
                    program.step();
                }	break;              
                case CODECOPY: case EXTCODECOPY: {
                    byte[] fullCode;
                    if (op == OpCode.CODECOPY)
                		fullCode = program.getCode();
                	else {
                    	DataWord address = program.stackPop();
                    	fullCode = program.getCodeAt(address);
                	}

                	DataWord memOffsetData    = program.stackPop();
                	BigInteger codeOffsetData = program.stackPop().value();
                	BigInteger lengthData     = program.stackPop().value();

                	if (fullCode == null
							|| BigInteger.valueOf(fullCode.length).compareTo(
									codeOffsetData.add(lengthData)) < 0) {
                        program.stop();
                        break;
                    }

                    int length     = lengthData.intValue();
                    int codeOffset = codeOffsetData.intValue();

                    byte[] codeCopy = new byte[length];
                    System.arraycopy(fullCode, codeOffset, codeCopy, 0, length);

                    if (logger.isInfoEnabled())
                        hint = "code: " + Hex.toHexString(codeCopy);

                    program.memorySave(memOffsetData.intValue(), codeCopy);
                    program.step();
                }	break;
                case GASPRICE:{
                    DataWord gasPrice = program.getGasPrice();

                    if (logger.isInfoEnabled())
                        hint = "price: " + gasPrice.toString();

                    program.stackPush(gasPrice);
                    program.step();
                }   break;

                /**
                 * Block Information
                 */
                case PREVHASH: {
                    DataWord prevHash = program.getPrevHash();

                    if (logger.isInfoEnabled())
                        hint = "prevHash: " + prevHash;

                    program.stackPush(prevHash);
                    program.step();
                }   break;
                case COINBASE: {
                    DataWord coinbase = program.getCoinbase();

                    if (logger.isInfoEnabled())
                        hint = "coinbase: " + Hex.toHexString(coinbase.getLast20Bytes());

                    program.stackPush(coinbase);
                    program.step();
                }   break;
                case TIMESTAMP:{
                    DataWord timestamp = program.getTimestamp();

                    if (logger.isInfoEnabled())
                        hint = "timestamp: " + timestamp.value();

                    program.stackPush(timestamp);
                    program.step();
                }   break;
                case NUMBER:{
                    DataWord number = program.getNumber();

                    if (logger.isInfoEnabled())
                        hint = "number: " + number.value();

                    program.stackPush(number);
                    program.step();
                }   break;
                case DIFFICULTY:{
                    DataWord difficulty = program.getDifficulty();

                    if (logger.isInfoEnabled())
                        hint = "difficulty: " + difficulty;

                    program.stackPush(difficulty);
                    program.step();
                }   break;
                case GASLIMIT:{
                    DataWord gaslimit = program.getGaslimit();

                    if (logger.isInfoEnabled())
                        hint = "gaslimit: " + gaslimit;

                    program.stackPush(gaslimit);
                    program.step();
                }   break;
                case POP:{
                	program.stackPop();
                    program.step();
                }	break;
                case DUP1: case DUP2: case DUP3: case DUP4:
                case DUP5: case DUP6: case DUP7: case DUP8: 
                case DUP9: case DUP10: case DUP11: case DUP12: 
                case DUP13: case DUP14: case DUP15: case DUP16:{

					int n = op.val() - OpCode.DUP1.val() + 1;
					DataWord word_1 = stack.get(stack.size() - n);
					program.stackPush(word_1.clone());
					program.step();
					
                }	break;
                case SWAP1: case SWAP2: case SWAP3: case SWAP4:
                case SWAP5: case SWAP6: case SWAP7: case SWAP8: 
                case SWAP9: case SWAP10: case SWAP11: case SWAP12: 
                case SWAP13: case SWAP14: case SWAP15: case SWAP16:{

        			int n = op.val() - OpCode.SWAP1.val() + 2;
                	DataWord word_1 = stack.peek();
        			stack.set(stack.size() - 1, stack.get(stack.size() - n));
        			stack.set(stack.size() - n, word_1);
        			program.step();

                }	break;
                case MLOAD:{
                	DataWord addr =  program.stackPop();
                    DataWord data =  program.memoryLoad(addr);

                    if (logger.isInfoEnabled())
                        hint = "data: " + data;

                    program.stackPush(data);
                    program.step();
                }	break;
                case MSTORE:{
                	DataWord addr  =  program.stackPop();
                    DataWord value =  program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = "addr: " + addr + " value: " + value;

                    program.memorySave(addr, value);
                    program.step();
                }	break;
                case MSTORE8:{
                	DataWord addr  =  program.stackPop();
                    DataWord value =  program.stackPop();
                    byte[] byteVal = {value.getData()[31]};
                    program.memorySave(addr.intValue(), byteVal);
                    program.step();
                }	break;
                case SLOAD:{
                	DataWord key =  program.stackPop();
                    DataWord val = program.storageLoad(key);

                    if (logger.isInfoEnabled())
					hint = "key: " + key + " value: " + val;

                    if (val == null) {
                        val = key.and(DataWord.ZERO);
                    }
                    program.stackPush(val);
                    program.step();
                }	break;
                case SSTORE:{
                	DataWord addr  =  program.stackPop();
                    DataWord value =  program.stackPop();

                    if (logger.isInfoEnabled())
                        hint = "addr: " + addr + " value: " + value;

                    program.storageSave(addr, value);
                    program.step();
                }	break;
                case JUMP:{
                	DataWord pos  =  program.stackPop();
                	int nextPC = pos.intValue(); // possible overflow
                	if (nextPC != 0 && program.getOp(nextPC-1) != OpCode.JUMPDEST.val())
        				throw new BadJumpDestinationException();

                    if (logger.isInfoEnabled())
                        hint = "~> " + nextPC;

        			program.setPC(nextPC);

                }	break;
                case JUMPI:{
                	DataWord pos   =  program.stackPop();
                    DataWord cond  =  program.stackPop();
                    
                    if (!cond.isZero()) {
                    	int nextPC = pos.intValue(); // possible overflow
                    	if (nextPC != 0 && program.getOp(nextPC-1) != OpCode.JUMPDEST.val())
            				throw new BadJumpDestinationException();

                        if (logger.isInfoEnabled())
                            hint = "~> " + nextPC;

                        program.setPC(nextPC);
                    } else {
                        program.step();
                    }
                	
                }	break;
                case PC:{
                    int pc = program.getPC();
                    DataWord pcWord = new DataWord(pc);

                    if (logger.isInfoEnabled())
                        hint = pcWord.toString();

                    program.stackPush(pcWord);
                    program.step();
                }	break;
                case MSIZE:{
                    int memSize = program.getMemSize();
                    DataWord wordMemSize = new DataWord(memSize);

                    if (logger.isInfoEnabled())
                        hint = "" + memSize;

                    program.stackPush(wordMemSize);
                    program.step();
                }	break;
                case GAS:{
                    DataWord gas = program.getGas();

                    if (logger.isInfoEnabled())
                        hint = "" + gas;

                    program.stackPush(gas);
                    program.step();
                }   break;

                case PUSH1:  case PUSH2:  case PUSH3:  case PUSH4:  case PUSH5:  case PUSH6:  case PUSH7:  case PUSH8:
                case PUSH9:  case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
                case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
                case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:{
                    program.step();
                    int nPush = op.val() - PUSH1.val() + 1;

                    byte[] data = program.sweep(nPush);
                    hint = "" + Hex.toHexString(data);

                    program.stackPush(data);
                }	break;
                case JUMPDEST:{
                	program.step();
                }	break;
                case CREATE:{
                	DataWord value      =  program.stackPop();
                    DataWord inOffset   =  program.stackPop();
                    DataWord inSize     =  program.stackPop();

                    if (logger.isInfoEnabled())
						logger.info(logString, program.getPC(),
								String.format("%-12s", op.name()),
								program.getGas().value(),
								program.invokeData.getCallDeep(), hint);
                    
                    program.createContract(value, inOffset, inSize);

                    program.step();
                }	break;
                case CALL: case CALLCODE: {
                	DataWord gas        =  program.stackPop();
                    DataWord codeAddress =  program.stackPop();
                    DataWord value      =  program.stackPop();

                    DataWord inDataOffs =  program.stackPop();
                    DataWord inDataSize =  program.stackPop();

                    DataWord outDataOffs =  program.stackPop();
                    DataWord outDataSize =  program.stackPop();
                    
                    if (logger.isInfoEnabled()) {
                    	hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes()) 
                    			+ " gas: " + gas.shortHex()
                    			+ " inOff: " + inDataOffs.shortHex()
                    			+ " inSize: " + inDataSize.shortHex();
						logger.info(logString, program.getPC(),
								String.format("%-12s", op.name()),
								program.getGas().value(),
								program.invokeData.getCallDeep(), hint);
                    }

					MessageCall msg = new MessageCall(
							op.equals(CALL) ? MsgType.CALL : MsgType.STATELESS,
							gas, codeAddress, value, inDataOffs, inDataSize,
							outDataOffs, outDataSize);
                    program.callToAddress(msg);

                    program.step();
                }	break;
                case RETURN:{
                	DataWord offset   =  program.stackPop();
                    DataWord size     =  program.stackPop();

                    ByteBuffer hReturn = program.memoryChunk(offset, size);
                    program.setHReturn(hReturn);

                    if (logger.isInfoEnabled())
                        hint = "data: " + Hex.toHexString(hReturn.array())
                        		+ " offset: " + offset.value()
                        		+ " size: " + size.value();

                    program.step();
                    program.stop();
                }	break;
                case SUICIDE:{
                	DataWord address =  program.stackPop();
                    program.suicide(address);
                    
                    if (logger.isInfoEnabled())
                        hint = "address: " + Hex.toHexString(program.getOwnerAddress().getLast20Bytes());
                    
                    program.stop();
                }	break;
                default:
                	break;
            }
            
			if (logger.isInfoEnabled() && !op.equals(CALL)
					&& !op.equals(CREATE))
				logger.info(logString, stepBefore, String.format("%-12s",
						op.name()), program.getGas().longValue(),
						program.invokeData.getCallDeep(), hint);
			
			vmCounter++;
        } catch (RuntimeException e) {
        	if(e instanceof OutOfGasException)
               	program.spendAllGas();
            logger.warn("VM halted", e.getMessage());
           	program.stop();
           	throw e;
        } finally {
            program.fullTrace();
        }
    }

}

class VMTest {

    private ProgramInvoke invoke;
    private Program program;

    @Test(expected=RuntimeException.class)  // XOR OP mal data
    public void testXOR_3() {


        VM vm = new VM();
        program =  new Program(Hex.decode("60C012"), invoke);
        try {
            vm.step(program);
            vm.step(program);
            vm.step(program);
        } finally {
            assertTrue(program.isStopped());
        }
    }

}