private LInstruction readInstruction(SPOpcode op) throws Exception {
    switch (op) {
      case load_pri:
      case load_alt:
        {
          Register reg = (op == SPOpcode.load_pri) ? Register.Pri : Register.Alt;
          return new LLoadGlobal(trackGlobal(readInt32()), reg);
        }

      case load_s_pri:
      case load_s_alt:
        {
          Register reg = (op == SPOpcode.load_s_pri) ? Register.Pri : Register.Alt;
          return new LLoadLocal(trackStack(readInt32()), reg);
        }

      case lref_s_pri:
      case lref_s_alt:
        {
          Register reg = (op == SPOpcode.lref_s_pri) ? Register.Pri : Register.Alt;
          return new LLoadLocalRef(trackStack(readInt32()), reg);
        }

      case stor_s_pri:
      case stor_s_alt:
        {
          Register reg = (op == SPOpcode.stor_s_pri) ? Register.Pri : Register.Alt;
          return new LStoreLocal(reg, trackStack(readInt32()));
        }

      case sref_s_pri:
      case sref_s_alt:
        {
          Register reg = (op == SPOpcode.sref_s_pri) ? Register.Pri : Register.Alt;
          return new LStoreLocalRef(reg, trackStack(readInt32()));
        }

      case load_i:
        return new LLoad(4);

      case lodb_i:
        return new LLoad(readInt32());

      case const_pri:
      case const_alt:
        {
          Register reg = (op == SPOpcode.const_pri) ? Register.Pri : Register.Alt;
          return new LConstant(readInt32(), reg);
        }

      case addr_pri:
      case addr_alt:
        {
          Register reg = (op == SPOpcode.addr_pri) ? Register.Pri : Register.Alt;
          return new LStackAddress(trackStack(readInt32()), reg);
        }

      case stor_pri:
      case stor_alt:
        {
          Register reg = (op == SPOpcode.stor_pri) ? Register.Pri : Register.Alt;
          return new LStoreGlobal(trackGlobal(readInt32()), reg);
        }

      case stor_i:
        return new LStore(4);

      case strb_i:
        return new LStore(readInt32());

      case lidx:
        return new LLoadIndex(4);

      case lidx_b:
        return new LLoadIndex(readInt32());

      case idxaddr:
        return new LIndexAddress(2);

      case idxaddr_b:
        return new LIndexAddress(readInt32());

      case move_pri:
      case move_alt:
        {
          Register reg = (op == SPOpcode.move_pri) ? Register.Pri : Register.Alt;
          return new LMove(reg);
        }

      case xchg:
        return new LExchange();

      case push_pri:
      case push_alt:
        {
          Register reg = (op == SPOpcode.push_pri) ? Register.Pri : Register.Alt;
          return new LPushReg(reg);
        }

      case push_c:
        return new LPushConstant(readInt32());

      case push:
        return new LPushGlobal(trackGlobal(readInt32()));

      case push_s:
        return new LPushLocal(trackStack(readInt32()));

      case pop_pri:
      case pop_alt:
        {
          Register reg = (op == SPOpcode.pop_pri) ? Register.Pri : Register.Alt;
          return new LPop(reg);
        }

      case stack:
        return new LStack(readInt32());

      case retn:
        return new LReturn();

      case call:
        {
          long addr = readInt32();
          file_.addFunction(addr);
          return new LCall(addr);
        }

      case jump:
        {
          long offset = readUInt32();
          return new LJump(prepareJumpTarget(offset), offset);
        }

      case jeq:
      case jneq:
      case jnz:
      case jzer:
      case jsgeq:
      case jsless:
      case jsgrtr:
      case jsleq:
        {
          long offset = readUInt32();
          if (offset == pc_) return new LJump(prepareJumpTarget(offset), offset);
          return new LJumpCondition(op, prepareJumpTarget(offset), prepareJumpTarget(pc_), offset);
        }

      case sdiv_alt:
      case sub_alt:
        return new LBinary(op, Register.Alt, Register.Pri);

      case add:
      case and:
      case or:
      case smul:
      case shr:
      case shl:
      case sub:
      case sshr:
      case xor:
        return new LBinary(op, Register.Pri, Register.Alt);

      case not:
      case neg:
      case invert:
        return new LUnary(op, Register.Pri);

      case add_c:
        return new LAddConstant(readInt32());

      case smul_c:
        return new LMulConstant(readInt32());

      case zero_pri:
      case zero_alt:
        {
          Register reg = (op == SPOpcode.zero_pri) ? Register.Pri : Register.Alt;
          return new LConstant(0, reg);
        }

      case zero_s:
        return new LZeroLocal(trackStack(readInt32()));

      case zero:
        return new LZeroGlobal(trackGlobal(readInt32()));

      case eq:
      case neq:
      case sleq:
      case sgeq:
      case sgrtr:
      case sless:
        return new LBinary(op, Register.Pri, Register.Alt);

      case eq_c_pri:
      case eq_c_alt:
        {
          Register reg = (op == SPOpcode.eq_c_pri) ? Register.Pri : Register.Alt;
          return new LEqualConstant(reg, readInt32());
        }

      case inc:
        return new LIncGlobal(trackGlobal(readInt32()));

      case dec:
        return new LDecGlobal(trackGlobal(readInt32()));

      case inc_s:
        return new LIncLocal(trackStack(readInt32()));

      case dec_s:
        return new LDecLocal(trackStack(readInt32()));

      case inc_i:
        return new LIncI();

      case inc_pri:
      case inc_alt:
        {
          Register reg = (op == SPOpcode.inc_pri) ? Register.Pri : Register.Alt;
          return new LIncReg(reg);
        }

      case dec_pri:
      case dec_alt:
        {
          Register reg = (op == SPOpcode.dec_pri) ? Register.Pri : Register.Alt;
          return new LDecReg(reg);
        }

      case dec_i:
        return new LDecI();

      case fill:
        return new LFill(readInt32());

      case bounds:
        return new LBounds(readInt32());

      case swap_pri:
      case swap_alt:
        {
          Register reg = (op == SPOpcode.swap_pri) ? Register.Pri : Register.Alt;
          return new LSwap(reg);
        }

      case push_adr:
        return new LPushStackAddress(trackStack(readInt32()));

      case sysreq_c:
        {
          long index = readUInt32();
          long prePeep = pc_;
          SPOpcode nextOp = readOp();
          int nextValue = readInt32();
          // Assert, that we really clear the stack from the arguments after this.
          assert (nextOp == SPOpcode.stack
              && nextValue
                  == ((LPushConstant) lir_.instructions.get(lir_.instructions.size() - 1)).val() * 4
                      + 4);
          // Skip the stack op, if it's popping the arguments again.
          if (nextOp != SPOpcode.stack
              || nextValue
                  != ((LPushConstant) lir_.instructions.get(lir_.instructions.size() - 1)).val() * 4
                      + 4) pc_ = prePeep;
          return new LSysReq(file_.natives()[(int) index]);
        }

      case sysreq_n:
        {
          long index = readUInt32();
          add(new LPushConstant((int) readUInt32()));
          return new LSysReq(file_.natives()[(int) index]);
        }

      case dbreak:
        return new LDebugBreak();

      case endproc:
        return null;

      case push2_s:
        {
          add(new LPushLocal(trackStack(readInt32())));
          return new LPushLocal(trackStack(readInt32()));
        }

      case push2_adr:
        {
          add(new LPushStackAddress(trackStack(readInt32())));
          return new LPushStackAddress(trackStack(readInt32()));
        }

      case push2_c:
        {
          add(new LPushConstant(readInt32()));
          return new LPushConstant(readInt32());
        }

      case push2:
        {
          add(new LPushGlobal(trackGlobal(readInt32())));
          return new LPushGlobal(trackGlobal(readInt32()));
        }

      case push3_s:
        {
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          return new LPushLocal(trackStack(readInt32()));
        }

      case push3_adr:
        {
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          return new LPushStackAddress(trackStack(readInt32()));
        }

      case push3_c:
        {
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          return new LPushConstant(readInt32());
        }

      case push3:
        {
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          return new LPushGlobal(trackGlobal(readInt32()));
        }

      case push4_s:
        {
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          return new LPushLocal(trackStack(readInt32()));
        }

      case push4_adr:
        {
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          return new LPushStackAddress(trackStack(readInt32()));
        }

      case push4_c:
        {
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          return new LPushConstant(readInt32());
        }

      case push4:
        {
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          return new LPushGlobal(trackGlobal(readInt32()));
        }

      case push5_s:
        {
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          add(new LPushLocal(trackStack(readInt32())));
          return new LPushLocal(trackStack(readInt32()));
        }

      case push5_c:
        {
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          add(new LPushConstant(readInt32()));
          return new LPushConstant(readInt32());
        }

      case push5_adr:
        {
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          add(new LPushStackAddress(trackStack(readInt32())));
          return new LPushStackAddress(trackStack(readInt32()));
        }

      case push5:
        {
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          add(new LPushGlobal(trackGlobal(readInt32())));
          return new LPushGlobal(trackGlobal(readInt32()));
        }

      case load_both:
        {
          add(new LLoadGlobal(trackGlobal(readInt32()), Register.Pri));
          return new LLoadGlobal(trackGlobal(readInt32()), Register.Alt);
        }

      case load_s_both:
        {
          add(new LLoadLocal(trackStack(readInt32()), Register.Pri));
          return new LLoadLocal(trackStack(readInt32()), Register.Alt);
        }

      case const_:
        {
          return new LStoreGlobalConstant(trackGlobal(readInt32()), readInt32());
        }

      case const_s:
        {
          return new LStoreLocalConstant(trackStack(readInt32()), readInt32());
        }

      case heap:
        {
          return new LHeap(readInt32());
        }

      case movs:
        {
          return new LMemCopy(readInt32());
        }

      case switch_:
        {
          long table = readUInt32();
          long savePc = pc_;
          pc_ = table;

          SPOpcode casetbl = SPOpcode.values()[(int) readUInt32()];
          assert (casetbl == SPOpcode.casetbl);

          int ncases = readInt32();
          long defaultCase = readUInt32();
          LinkedList<SwitchCase> cases = new LinkedList<SwitchCase>();
          boolean bMultipleValues;
          for (int i = 0; i < ncases; i++) {
            int value = readInt32();
            long pc = readUInt32();
            LBlock target = prepareJumpTarget(pc);
            // Check if there are multiple values for the same case
            // case 1, 2, 3:
            bMultipleValues = false;
            for (int j = 0; j < cases.size(); j++) {
              if (cases.get(j).target.equals(target)) {
                cases.get(j).addValue(value);
                bMultipleValues = true;
                break;
              }
            }
            if (!bMultipleValues) cases.add(new SwitchCase(value, target));
          }
          pc_ = savePc;
          return new LSwitch(prepareJumpTarget(defaultCase), cases);
        }

      case casetbl:
        {
          int ncases = readInt32();
          pc_ += (long) ncases * 8 + 4;
          return new LDebugBreak();
        }

      case genarray:
        {
          int dims = readInt32();
          return new LGenArray(dims, false);
        }

      case genarray_z:
        {
          int dims = readInt32();
          return new LGenArray(dims, true);
        }

      case tracker_pop_setheap:
        {
          return new LTrackerPopSetHeap();
        }

      case tracker_push_c:
        {
          int value = readInt32();
          return new LTrackerPushC(value);
        }

      case stradjust_pri:
        {
          return new LStradjustPri();
        }

      case stackadjust:
        {
          int value = readInt32();
          return new LStackAdjust(value);
        }

      case nop:
        {
          return new LDebugBreak();
        }

      case halt:
        {
          readInt32();
          return new LDebugBreak();
        }

      default:
        throw new Exception("Unrecognized opcode: " + op);
    }
  }
 private SPOpcode readOp() {
   return SPOpcode.values()[(int) readUInt32()];
 }