Google
 

Trailing-Edge - PDP-10 Archives - BB-P363B-SM_1985 - mcb/nml/nmldlw.b16
There are no other files named nmldlw.b16 in the archive.
!RATS:<BANKS.MCB.NCPNML>NMLDLW.B16.5 19-Nov-84 13:01:46, Edit by BANKS
!
! Ident 23.
!  Fix still more errors in Ident 21 (cause: ignorance of Bliss).
!
!RATS:<BANKS.MCB.NCPNML>NMLDLW.B16.4 16-Nov-84 10:39:15, Edit by BANKS
!
! Ident 22.
!  Fix errors in Ident 21.
!
!RATS:<BANKS.MCB.NMLMCB>NMLDLW.B16.3 30-Oct-84 15:00:55, Edit by BANKS
!
! Ident 21.
!  More work on autodumping.  If the received Memory Dump Service Requested
!  message contains a dump count, copy this count ala NICE into the 
!  request block that we queue to the dumper.
!
!RATS:<BANKS.MCB.NMLMCB>NMLDLW.B16.2 23-Oct-84 15:23:56, Edit by BANKS
!
! Ident 20.
!  DLW_TASK is making some assumptions about the size of a received Request
!  Dump Service message.  If the message contains an optional field (present
!  in messages received from DMR-11s), the message will be ignored because
!  it isn't one of the valid lengths.  Change the code to allow any message
!  greater than or equal to the presumed eight bytes in length.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2  3-Oct-82 17:39:21, Edit by PECKHAM
!
! Ident 19.
!  Fix DLW_TASK to handle passive loopback independent of SERVICE parameter:
!    Add service block parameter SB_SERVICE.
!    Make SET_SERVICE_BLOCK set SB_SERVICE from VDB.
!    Take out check of SERVICE parameter before MOP message has been read.
!    Check SB_SERVICE for ENABLED only for LOAD and DUMP requests.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.4 27-Aug-82 11:06:06, Edit by PECKHAM
!
! Ident 18.
!  Re-enable auto-dumping.
!  A reponse buffer was being lost because NMLDTL allocates a buffer
!  on top of the one NMLDLW allocates.  Fix this by de-allocating any
!  response buffers before doing a NML$REQUEST_ENTER.
!
!NET:<BRANDT.DEVELOPMENT>NMLDLW.B16.1 9-Jul-82 18:28:22, Edit by BRANDT
!
! Ident 17.
!   Disable autodumping
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2 26-Jun-82 16:28:22, Edit by PECKHAM
!
! Ident 16.
! Truncate ERROR TEXT to 72 bytes for event parameter in LOG_SERVICE_EVENT.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2 23-Jun-82 12:21:20, Edit by PECKHAM
!
! Ident 15.
! Set privledged bits in request block.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.4 21-Jun-82 23:15:55, Edit by PECKHAM
!
! Ident 14.
! Make the MOP prompt message (ENTER MOP MODE) as a one byte message.
! This conforms to the VMS/RSX agreed-upon protocol.
! Add support for SERVICE DEVICE checking during service requests.
! Add LOG_ABORTED_REQUEST for unrecognized requests.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2  5-Jun-82 14:10:59, Edit by PECKHAM
!
! Ident 13.
! Increase MOP buffer length from 25 to 500 for loopback.
! Only use MOP buffer in DLW_TASK (Do not put in RB_DATA).
! (It was being left allocated during the entire loading process)
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2 22-May-82 18:44:47, Edit by PECKHAM
!
! Ident 12.
! Add SETUP_SERVICE_BLOCK to initialize various items including substate.
! Do not set substates AUTODUMPING or AUTOLOADING (let NMLDTL do it).
! Make sure not to start a service operation if substate is not
! cleared or AUTOSERVICE.  Optimize linkages.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2 10-May-82 15:47:56, Edit by PECKHAM
!
! Ident 11.
! Interlock service function on circuit substate.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2 28-Apr-82 12:06:12, Edit by PECKHAM
!
! Ident 10.
! Change NMU$DLX_* routine references to $NMU_DLX_* macro references.
! Add receive timeout values where needed.
!
!NET:<PECKHAM.DEVELOPMENT>N-NMLDLW.B16.16  9-Mar-82 15:45:47, Edit by PECKHAM
!
! Ident 09.
! Fix MEMORY_GET handling.
! Create seperate DLW_TASK for service operations so as not to hold up
! event logging task.
! Check SERVICE parameter *after* link is open instead of before
! (this is to cycle the circuit back to transport so it will restart).
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.2  9-Feb-82 09:04:34, Edit by PECKHAM
!
! Ident 08.
! Remove EV_ETM (NMLEVT now can use TIME_* routines).
! Replace calls to LW_EVT by direct call to NML$DECLARE_EVENT.
!
!NET:<PECKHAM.DEVELOPMENT>NMLDLW.B16.8 18-Jan-82 21:27:32, Edit by PECKHAM
!
! Ident 07.
! Add "automatic line service" events
! Note : The event definitions cannot be obtained because they are in NMLLIB.
!        The routine LW_EVT will be put into a module which uses NMLLIB.
! LW_EVT is in NMLEVT for now (see NMLDLW.B16).
! Temporary EV_ETM routine for NMLEVT to get time (until it uses NMULIB).
!
!NET:<DECNET20-V3P0.XPT>NMLDLW.B16.2  6-Jan-82 08:47:25, Edit by WEBBER
!
! Ident 06.
! Fix bug in DLW_CALL, getting the service state (wrong ID passed).  Also,
! DLW_CALL should not service a circuit if it has an undefined service state.
!
!NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLDLW.B16.24 21-Oct-81 11:31:19, Edit by PECKHAM
!
! Ident 05.
! Send MOP_MODE_RUNNING message when we get the link.
!
!NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLDLW.B16.20 13-Oct-81 22:03:44, Edit by PECKHAM
!
! Ident 04.
! Avoid buffering on the stack by allocating REQuest block early
! and using its buffers.
! Add substate handling.
!
!NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLDLW.B16.16 12-Oct-81 10:32:34, Edit by PECKHAM
!
! Ident 03.
! Do not initiate service function unless SERVICE ENABLED.
!
!NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLDLW.B16.14 11-Oct-81 16:43:53, Edit by PECKHAM
!
! Ident 02.
! Add recognition of load/dump/loop requests.
!
module NMLDLW (		                        ! Data link watcher task
		ident = 'X00.23'
		) =
begin

!
!                    COPYRIGHT (c) 1980, 1981, 1982, 1985
!                    DIGITAL EQUIPMENT CORPORATION
!                        Maynard, Massachusetts
!
!     This software is furnished under a license and may  be  used
!     and copied only in accordance with the terms of such license
!     and with the inclusion of the above copyright notice.   This
!     software  or any other copies thereof may not be provided or
!     otherwise made available to any other person.  No  title  to
!     and ownership of the software is hereby transferred.
!
!     The information  in  this  software  is  subject  to  change
!     without  notice  and should not be construed as a commitment
!     by DIGITAL EQUIPMENT CORPORATION.
!
!     DIGITAL assumes no responsibility for the use or reliability
!     of  its  software  on  equipment  which  is  not supplied by
!     DIGITAL.
!
!++
!
! Facility: LSG DECnet Management
!
! Abstract: This module contains the code for the MCB
!           Data Link Watcher.  When a event (Class 96 + 5,
!           type 13) comes into the event processor, it calls
!           these routines to process any MOP messages.
!
! Environment: MCB
!
! Author: Steven M. Jenness	Creation date: 30-May-81
!
!--
!
! Include files
!

library 'NMULIB';                       ! Get all required definitions

!
! Global routines
!

%if %bliss (bliss16)
%then linkage NML_LKG_DB = jsr (register = 5);
%else macro NML_LKG_DB = bliss36c %;
%fi

forward routine
    NML$DLW_CALL : novalue,             ! DLW event call
    NML$DLW_INITIALIZE : novalue;       ! DLW task creation/initialization

!
! Local routines
!

forward routine
    DLW_TASK : novalue,                       ! Data link watcher task
    DLW_RUNNING : NML_LKG_DB novalue,         ! Send MOP_MODE_RUNNING message
    DLW_DEQUEUE : novalue,                    ! Post process load/dump response
    SETUP_SERVICE_BLOCK : NML_LKG_DB novalue, ! Set info in service block
    LOG_ABORTED_REQUEST : NML_LKG_DB novalue, ! Log "aborted service request"
    LOG_PASSIVE_LOOPBACK : NML_LKG_DB novalue,! Log "passive loopback"
    LOG_SERVICE_EVENT : NML_LKG_DB novalue,   ! Log "automatic line service"
    SET_SUBSTATE : NML_LKG_DB novalue;        ! Set circuit substate


!
! Macros
!

macro
     CH$$ASCIC [ ] =                     !A pointer to an ASCIC string.
         ch$ptr(uplit(%string(%char(%charcount(%remaining)),%remaining))) % ;

macro
     CH$BYTE [B1, B2] =
         (B1
          %if not %null (B2)
          %then
               or (B2 ^ 8)
          %fi) %;

!
! Literal values
!

literal
    RESPONSE_LENGTH = 256,              ! Length of response buffer in bytes
    RESPONSE_SIZE = ch$allocation (RESPONSE_LENGTH,8),
    RESPONSE_ALLOCATION = RESPONSE_SIZE * %upval ;

$field
    SB_FIELDS =
        set
        SB_BLOCK = [$sub_block (0)],
        SB_RESPONSE_PTR = [$pointer],
        SB_LINK_ID = [$short_integer],
        SB_ENTITY_TYPE = [$short_integer],
        SB_ENTITY_PTR = [$pointer],
        SB_SUBSTATE = [$integer],
        SB_SERVICE = [$integer]
        tes;

literal
    SB_SIZE = $field_set_size;

macro
    SERVICE_BLOCK = block [SB_SIZE] field (SB_FIELDS) %;

!
! Circuit parameter numbers
!

literal
    CPARM_STATE = 0,                    ! Circuit STATE
    CPARM_SUBSTATE = 1,                 ! Circuit SUBSTATE
    CPARM_SERVICE = 100;                ! Circuit SERVICE

!
! Circut STATE values
!

literal
    CIRCUIT_ON = 0,
    CIRCUIT_OFF = 1,
    CIRCUIT_SERVICE = 2,
    CIRCUIT_CLEARED = 3;

!
! Circuit SUBSTATE values
!

literal
    CIRCUIT_NO_SUBSTATE = -1,
    CIRCUIT_STARTING = 0,
    CIRCUIT_REFLECTING = 1,
    CIRCUIT_LOOPING = 2,
    CIRCUIT_LOADING = 3,
    CIRCUIT_DUMPING = 4,
    CIRCUIT_TRIGGERING = 5,
    CIRCUIT_AUTOSERVICE = 6,
    CIRCUIT_AUTOLOADING = 7,
    CIRCUIT_AUTODUMPING = 8,
    CIRCUIT_AUTOTRIGGERING = 9,
    CIRCUIT_SYNCHRONIZING = 10,
    CIRCUIT_FAILED = 11;

!
! Circuit SERVICE values
!

literal
    CIRCUIT_ENABLED = 0,
    CIRCUIT_DISABLED = 1;

!
! Own Storage
!

own
    SERVICE_REQUEST_QUEUE : SQ_HEADER;  ! Service Request queue...

!
! External routines
!

external routine
    NMU$MEMORY_MANAGER,                 ! Memory get/release routines
    NML$REQUEST_MANAGER,                ! NML NICE function management
    NMU$SCHED_MANAGER,                  ! Scheduler
    NML$GET_VDB_PARAMETER,              ! Get volatile data base parameter
    NML$SET_VDB_PARAMETER,              ! Set volatile data base parameter
    NML$CLR_VDB_PARAMETER,              ! Clear volatile data base parameter
    NML$DECLARE_EVENT : novalue;        ! Declare event internally to NML

external
    %debug_data_base;
%global_routine ('NML$DLW_CALL', CIRCUIT_ID) : novalue =

!++
! Functional description:
!
!    This routine is the main body of the Data Link Watcher task.
!
!    It is called when a "Maintenance mode message received
!    during normal operations" event is received by the event
!    processor.  The circuit id is passed to indicate which
!    circuit the event occured on.
!
! Formal parameters:
!
!    .CIRCUIT_ID    Byte pointer to ASCIC circuit id
!
! Routine value: none
!
!--

    begin

    local
	 REQ : ref REQUEST_BLOCK;

    !
    ! Allocate request block
    ! Allocate response buffer
    !

    if (REQ = NMU$MEMORY_GET (REQUEST_BLOCK_ALLOCATION)) eqla 0
    then
        return;

    begin

    local
         NICE_PTR;                      ! Nice message pointer
                                        ! Allocate six extra bytes here for the
                                        ! possible later addition of the dump
                                        ! count.  The original code here
                                        ! allocated six bytes for the circuit
                                        ! entity field.  Why so much?
    REQ [RB_NICE_ALLOCATION] = ch$allocation (40, 8)*%upval;
    if (REQ [RB_NICE] = NMU$MEMORY_GET (.REQ [RB_NICE_ALLOCATION])) eqla 0
    then
        begin
        NMU$MEMORY_RELEASE (.REQ, REQUEST_BLOCK_ALLOCATION);
        return;
        end;
    NICE_PTR = REQ [RB_NICE_POINTER] = ch$ptr (.REQ [RB_NICE],, 8);

    PUTB (0, NICE_PTR);
    REQ [RB_NICE_ENTITY_TYPE] = CIRCUIT_;
    PUTB (.REQ [RB_NICE_ENTITY_TYPE], NICE_PTR);
    REQ [RB_NICE_ENTITY] = .NICE_PTR;
    NICE_PTR = ch$move (ch$rchar (.CIRCUIT_ID) + 1, .CIRCUIT_ID, .NICE_PTR);
    REQ [RB_NICE_LENGTH] = NICE_PTR = ch$diff (.NICE_PTR, .REQ [RB_NICE_POINTER]);
    end;
    NMU$SQUEUE_INSERT (SERVICE_REQUEST_QUEUE, .REQ);
    end;					! End of NML$DLW_CALL
%global_routine ('NML$DLW_INITIALIZE') : novalue =

!++
! Functional description:
!
!    This routine is called at startup time to initialize the Data
!    Link Watcher task.
!
! Formal parameters: none
!
! Routine value: none
!
!--

    begin
    NMU$SQUEUE_RESET (SERVICE_REQUEST_QUEUE);
    NMU$SCHED_CREATE (DLW_TASK, 200, 0, ch$asciz ('LINK-WATCHER'));
    end;					! End of NML$DLW_INITIALIZE
%routine ('DLW_TASK') : novalue =

!++
! Functional description:
!
!    This routine is the main body of the Data Link Watcher task.
!    It handles service requests queued from NML$DLW_CALL.
!
! Formal parameters: none
!
! Routine value: none
!
!--

    begin

    label
        SERVICE_TASK;

    literal
        MOP_BUFFER_LENGTH = 500,
        MOP_BUFFER_ALLOCATION = ch$allocation (MOP_BUFFER_LENGTH, 8)*%upval;

    local
        REQ : ref REQUEST_BLOCK;

!
! Run this task forever (until NML dies)
!
    while $true
    do
SERVICE_TASK :
      begin                             ! SERVICE_TASK block

      local
          SB : SERVICE_BLOCK,
          MOP_BUFFER_ADR,
          MOP_BUFFER_PTR;

      REQ = NMU$SQUEUE_REMOVE (SERVICE_REQUEST_QUEUE);

      REQ [RB_RESPONSE_ALLOCATION] = RESPONSE_ALLOCATION;
      if (REQ [RB_RESPONSE] = NMU$MEMORY_GET (.REQ [RB_RESPONSE_ALLOCATION])) eqla 0
      then
          leave SERVICE_TASK with DLW_DEQUEUE (.REQ);

      SETUP_SERVICE_BLOCK (SB [SB_BLOCK], .REQ);
      ch$wchar (NICE$_SUC, .SB [SB_RESPONSE_PTR]);

      REQ [RB_EXECUTOR] = 0;
      REQ [RB_COMPLETION] = DLW_DEQUEUE;
      REQ[RB_PRV_LOCAL] = $true;
      REQ[RB_PRV_SERVICE] = $true;

      if (MOP_BUFFER_ADR = NMU$MEMORY_GET (MOP_BUFFER_ALLOCATION)) eqla 0
      then
          leave SERVICE_TASK with DLW_DEQUEUE (.REQ);

      MOP_BUFFER_PTR = ch$ptr (.MOP_BUFFER_ADR,, 8);
      !
      ! Try to open the link.
      !

      if (SB [SB_LINK_ID] = $NMU_DLX_OPEN (DLX_OTHER, .SB [SB_ENTITY_PTR], .SB [SB_RESPONSE_PTR])) gtr 0
      then
          begin

          label
              AUTOSERVICE;

AUTOSERVICE :
          begin                         ! AUTOSERVICE block

          local
              MSG_LEN,
              SERVICE_DEVICE;

          !
          ! If the circuit is already being serviced, then ignore.
          !

          if .SB [SB_SUBSTATE] neq CIRCUIT_NO_SUBSTATE
          then
              begin
              DLW_DEQUEUE (.REQ);
              leave AUTOSERVICE;
              end;
          !
          ! Put the circuit in AUTOSERVICE substate
          !
          SB [SB_SUBSTATE] = CIRCUIT_AUTOSERVICE;
          SET_SUBSTATE (SB [SB_BLOCK]);
          !
          ! Get the service device for verification.
          !
          if not NML$GET_VDB_PARAMETER (CIRCUIT_, .SB [SB_ENTITY_PTR],
                                        112, SERVICE_DEVICE)
          then SERVICE_DEVICE = -1;

          $NMU_DLX_WRITE (.SB [SB_LINK_ID], DLX_OTHER,
                          ch$ptr (uplit (%char(MOP_MODE_RUNNING)),, 8), 1,
                          .SB [SB_RESPONSE_PTR]);

          while (MSG_LEN = $NMU_DLX_READ (.SB [SB_LINK_ID], DLX_DATA,
                                         .MOP_BUFFER_PTR, MOP_BUFFER_LENGTH,,
                                         .SB [SB_RESPONSE_PTR])) gtr 0
          do

              selectone ch$rchar (.MOP_BUFFER_PTR) of
                  set
                  [ENTER_MOP_MODE] :    ! Enter MOP Mode
                      DLW_RUNNING (SB [SB_BLOCK], .MOP_BUFFER_PTR);
                  [REQUEST_PROGRAM] :   ! Request Program
                      begin
                      macro
                          DEVTYPE = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 1)) %,
                          MOPVER = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 2)) %,
                          PGMTYPE = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 3)) %;

                      !
                      ! If service is not enabled, ignore this request.
                      !

                      if .SB [SB_SERVICE] neq CIRCUIT_ENABLED
                      then
                          begin
                          DLW_DEQUEUE (.REQ);
                          leave AUTOSERVICE;
                          end;

                      if MSG_LEN lss 4
                      then
                          ch$wchar (0, ch$plus (.MOP_BUFFER_PTR, 3));

                      if (.SERVICE_DEVICE geq 0) and
                         (DEVTYPE neq .SERVICE_DEVICE)
                      then
                          begin         ! DEVTYPE does not match
                          LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                          exitloop;
                          end;

                      if MOPVER neq 1
                      then
                          begin         ! MOPVER not 1
                          LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                          exitloop;
                          end;

                      selectone PGMTYPE of
                          set
                          [0 to 2] :
                              ;
                          [otherwise] :
                              begin     ! PGMTYPE invalid
                              LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                              exitloop;
                              end;
                          tes;

                      ch$wchar (0, .SB [SB_RESPONSE_PTR]);
                      LOG_SERVICE_EVENT (SB [SB_BLOCK], LOAD_);
                      ch$wchar (LOAD_, .REQ [RB_NICE_POINTER]);
                      if .REQ [RB_RESPONSE] neqa 0
                      then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE], .REQ [RB_RESPONSE_ALLOCATION]);
                      NML$REQUEST_ENTER (.REQ);
                      leave AUTOSERVICE;
                      end;
                  [MOP_MODE_RUNNING] :  ! MOP Mode Running

                      if .MSG_LEN neq 1
                      then
                          begin
                          if .MSG_LEN geq 8
                          then
                              begin
                              macro
                                  DEVTYPE = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 1)) %,
                                  MOPVER = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 2)) %;

                              local
                                  FEATURES;

                              !
                              ! If service is not enabled, ignore this request.
                              !

                              if .SB [SB_SERVICE] neq CIRCUIT_ENABLED
                              then
                                  begin
                                  DLW_DEQUEUE (.REQ);
                                  leave AUTOSERVICE;
                                  end;

                              if (.SERVICE_DEVICE geq 0) and
                                 (DEVTYPE neq .SERVICE_DEVICE)
                              then
                                  begin ! DEVTYPE does not match
                                  LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                                  exitloop;
                                  end;

                              if MOPVER neq 1
                              then
                                  begin ! MOPVER not 1
                                  LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                                  exitloop;
                                  end;

                              FEATURES = ch$rchar (ch$plus (.MOP_BUFFER_PTR, 7));

                              if .FEATURES <1, 1>
                              then
                                  begin
                                  local
                                       NICE_PTR,
                                       DUMP_COUNT: THIRTY_TWO_BIT;
                                  ch$wchar (0, .SB [SB_RESPONSE_PTR]);
                                  LOG_SERVICE_EVENT (SB [SB_BLOCK], DUMP_);
                                  ch$wchar (DUMP_, .REQ [RB_NICE_POINTER]);
                                  if .REQ [RB_RESPONSE] neqa 0
                                  then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE], .REQ [RB_RESPONSE_ALLOCATION]);
                                  NICE_PTR = ch$plus (.MOP_BUFFER_PTR, 3);
                                  GET32 (NICE_PTR, DUMP_COUNT);
                                  if ((.DUMP_COUNT or .(DUMP_COUNT+2)) nequ 0) and
                                     (.REQ[RB_NICE] neqa 0) and
                                     (.REQ[RB_NICE_LENGTH] + 6 leq .REQ[RB_NICE_ALLOCATION])
                                  then
                                      begin
                                      NICE_PTR = ch$plus (.REQ[RB_NICE_POINTER], .REQ[RB_NICE_LENGTH]);
                                      PUTB (136, NICE_PTR);       ! Code for dump count
                                      PUTB (0, NICE_PTR);         !  rounded out to 16 bits
                                      PUT32 (DUMP_COUNT, NICE_PTR); ! Store the dump count
                                      REQ[RB_NICE_LENGTH] = .REQ[RB_NICE_LENGTH] + 6;
                                      end;
                                  NML$REQUEST_ENTER (.REQ);
                                  leave AUTOSERVICE;
                                  end;

			      exitloop;
                              end
                          else
                              begin
                              LOG_ABORTED_REQUEST (SB [SB_BLOCK], 3);
                              exitloop;
                              end;
                          end;

                  [LOOPBACK_TEST] :     ! Loopback Test
                      begin             ! Perform passive loopback algorithm:
                      SB [SB_SUBSTATE] = CIRCUIT_REFLECTING;
                      SET_SUBSTATE (SB [SB_BLOCK]);
                      LOG_PASSIVE_LOOPBACK (SB [SB_BLOCK]);

                      do
                          begin
                          ch$wchar (LOOPED_DATA, .MOP_BUFFER_PTR);

                          if not $NMU_DLX_WRITE (.SB [SB_LINK_ID], DLX_OTHER,
                                                 .MOP_BUFFER_PTR, .MSG_LEN,
                                                 .SB [SB_RESPONSE_PTR])
                          then exitloop;

                          MSG_LEN = $NMU_DLX_READ (.SB [SB_LINK_ID], DLX_DATA,
                                                   .MOP_BUFFER_PTR, MOP_BUFFER_LENGTH,,
                                                   .SB [SB_RESPONSE_PTR]);
                          if .MSG_LEN leq 0 then exitloop;
                          end
                      while ch$rchar (.MOP_BUFFER_PTR) eql LOOPBACK_TEST;

                      SB [SB_SUBSTATE] = CIRCUIT_AUTOSERVICE;
                      SET_SUBSTATE (SB [SB_BLOCK]);
                      LOG_PASSIVE_LOOPBACK (SB [SB_BLOCK]);
                      if .MSG_LEN leq 0 then exitloop;
                      DLW_RUNNING (SB [SB_BLOCK], .MOP_BUFFER_PTR);
                      end;
                  [LOOPED_DATA] :
                      exitloop;
                  tes;

          if .SB [SB_SUBSTATE] neq CIRCUIT_NO_SUBSTATE
          then
              begin
              SB [SB_SUBSTATE] = CIRCUIT_NO_SUBSTATE;
              SET_SUBSTATE (SB [SB_BLOCK]);
              end;

          DLW_DEQUEUE (.REQ);
          end;                          ! AUTOSERVICE block

          $NMU_DLX_CLOSE (.SB [SB_LINK_ID]);
          NMU$MEMORY_RELEASE (.MOP_BUFFER_ADR, MOP_BUFFER_ALLOCATION);
          end;

      end;                              ! SERVICE_TASK block

    end;					! End of DLW_TASK
%routine ('DLW_RUNNING', SB : ref SERVICE_BLOCK, MOP_BUFFER) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine responds to an ENTER_MOP_MODE message with a
!    MOP_MODE_RUNNING message.
!
! Formal parameters:
!
!    .SB           Address of service block
!    .MOP_BUFFER   Address of allocated MOP buffer.
!
! Routine value: none
!
!--

    begin
    local
         MOP_PTR;

    MOP_PTR = ch$ptr (.MOP_BUFFER,, 8);
    PUTB (MOP_MODE_RUNNING,MOP_PTR);    ! CODE: MOP Mode Running
    PUTB (20,MOP_PTR);                  ! DEVTYPE: DTE device (TEMPORARY)
    PUTB (1,MOP_PTR);                   ! MOPVER: format version 1
    PUTB (%o'000',MOP_PTR);             ! MEMSIZE: 760000 (TEMPORARY)
    PUTB (%o'340',MOP_PTR);
    PUTB (%o'003',MOP_PTR);
    PUTB (%o'000',MOP_PTR);
    PUTB (1^2,MOP_PTR);                 ! FEATURES: Loopback
    MOP_PTR = ch$diff (.MOP_PTR, ch$ptr (.MOP_BUFFER,, 8));
    $NMU_DLX_WRITE (.SB [SB_LINK_ID], DLX_OTHER,
                   ch$ptr (.MOP_BUFFER,, 8), .MOP_PTR,
                   .SB [SB_RESPONSE_PTR]);
    end;					! End of DLW_RUNNING
%routine ('DLW_DEQUEUE', REQ : ref REQUEST_BLOCK) : novalue =

!++
! Functional description:
!
!    This routine processing the response from a DLW NICE function
!    request.  If it is a DUMP_ request it will issue the corresponding
!    LOAD_ request.  If it is a LOAD_ request it will set the CIRCUIT
!    status to the appropriate value.  It will also free up any
!    memory allocated for the request.
!
! Formal parameters:
!
!    .REQ    Request block address
!
! Routine value: none
!
!--

    begin
    local
         NICE_PTR,
         SB : SERVICE_BLOCK;

    SETUP_SERVICE_BLOCK (SB [SB_BLOCK], .REQ);
    NICE_PTR = .REQ [RB_NICE_POINTER];
    begin

    local
        FUNCTION_CODE;

    if (FUNCTION_CODE = ch$rchar_a (NICE_PTR)) neq 0
    then LOG_SERVICE_EVENT (SB [SB_BLOCK], .FUNCTION_CODE);

!
! If a dump was just done .. try to do a LOAD
! If a load was just done .. set the circuit substate
!

    if .SB [SB_SUBSTATE] eql CIRCUIT_AUTOSERVICE
    then
        if .FUNCTION_CODE eql DUMP_
        then
            begin
            NICE_PTR = ch$plus (.NICE_PTR, -1);
            ch$wchar (LOAD_, .NICE_PTR);
            if .REQ [RB_RESPONSE] neqa 0
            then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE], .REQ [RB_RESPONSE_ALLOCATION]);
            NML$REQUEST_ENTER (.REQ);
            return;
            end
        else
            begin
            SB [SB_SUBSTATE] = CIRCUIT_NO_SUBSTATE;
            SET_SUBSTATE (SB [SB_BLOCK]);
            end;

    end;

    !
    ! Release all memory allocated for request
    !

    if .REQ [RB_RESPONSE] neqa 0
    then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE], .REQ [RB_RESPONSE_ALLOCATION]);

    if .REQ [RB_NICE] neqa 0
    then NMU$MEMORY_RELEASE (.REQ [RB_NICE], .REQ [RB_NICE_ALLOCATION]);

    NMU$MEMORY_RELEASE (.REQ, REQUEST_BLOCK_ALLOCATION);
    end;					! End of DLW_DEQUEUE
%routine ('SETUP_SERVICE_BLOCK', SB : ref SERVICE_BLOCK, REQ : ref REQUEST_BLOCK) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine sets up pertinent information from the request block into
!    the internal service block.
!
! Formal parameters:
!
!    .SB     Service block
!    .REQ    Request block
!
! Routine value: none
!
!--

    begin
    SB [SB_RESPONSE_PTR] = ch$ptr (.REQ [RB_RESPONSE],, 8);
    SB [SB_ENTITY_TYPE] = .REQ [RB_NICE_ENTITY_TYPE];
    SB [SB_ENTITY_PTR] = .REQ [RB_NICE_ENTITY];

    if not NML$GET_VDB_PARAMETER (.SB [SB_ENTITY_TYPE], .SB [SB_ENTITY_PTR],
                                  CPARM_SUBSTATE, SB [SB_SUBSTATE])
    then SB [SB_SUBSTATE] = CIRCUIT_NO_SUBSTATE;

    if not NML$GET_VDB_PARAMETER (CIRCUIT_, .SB [SB_ENTITY_PTR],
                                  CPARM_SERVICE, SB [SB_SERVICE])
    then SB [SB_SERVICE] = CIRCUIT_DISABLED;
                                  
    end;					! End of SETUP_SERVICE_BLOCK
%routine ('LOG_ABORTED_REQUEST', SB : ref SERVICE_BLOCK, REASON) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine initiates an "aborted service request" event
!    for the specified entity.
!
! Formal parameters:
!
!    SB            Service block
!    .REASON       REASON code parameter value
!
! Routine value: none
!
!--

    begin

    local
        DATA : vector [ch$allocation (4, 8)],
        DATA_PTR;

    DATA_PTR = ch$ptr (DATA,, 8);
    begin                               ! Parameter #3

    literal
        DATA_ID = 0^15 + 3,
        DATA_TYPE = 1^7 + 0^6 + 1;

    ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
    ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
    ch$wchar_a (DATA_TYPE, DATA_PTR);
    ch$wchar_a (.REASON, DATA_PTR);
    end;
    begin

    local
        REB : RAW_EVENT_BLOCK;

    REB [REB_EVENT_CODE] = 0^6 + 7;
    REB [REB_ENTITY_TYPE] = .SB [SB_ENTITY_TYPE];
    REB [REB_ENTITY_POINTER] = .SB [SB_ENTITY_PTR];
    REB [REB_DATA_POINTER] = ch$ptr (DATA,, 8);
    REB [REB_DATA_LENGTH] = ch$diff (.DATA_PTR, ch$ptr (DATA,, 8));
    REB [REB_TIME_BLOCK] = 0;
    NML$DECLARE_EVENT (REB);
    end;
    end;					! End of LOG_ABORTED_REQUEST
%routine ('LOG_PASSIVE_LOOPBACK', SB : ref SERVICE_BLOCK) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine
!
! Formal parameters:
!
!    STATUS = (INITIATED, TERMINATED)
!
! Routine value: none
!
!--

    begin

    literal
        DATA_ID = 0^15 + 2,
        DATA_TYPE = 1^7 + 0^6 + 1,
        INITIATED = 0,
        TERMINATED = 1;

    bind
        DATA_LEN = 4,
        INITIATED_PTR = ch$ptr (uplit (ch$byte (
            (DATA_ID and %o'377'),
            (DATA_ID^-8 and %o'377'),
            DATA_TYPE,
            INITIATED)),, 8),
        TERMINATED_PTR = ch$ptr (uplit (ch$byte (
            (DATA_ID and %o'377'),
            (DATA_ID^-8 and %o'377'),
            DATA_TYPE,
            TERMINATED)),, 8);

    local
        REB : RAW_EVENT_BLOCK;

    REB [REB_EVENT_CODE] = 0^6 + 6;
    REB [REB_ENTITY_TYPE] = .SB [SB_ENTITY_TYPE];
    REB [REB_ENTITY_POINTER] = .SB [SB_ENTITY_PTR];

    if .SB [SB_SUBSTATE] eql CIRCUIT_REFLECTING
    then
        REB [REB_DATA_POINTER] = INITIATED_PTR
    else
        REB [REB_DATA_POINTER] = TERMINATED_PTR;

    REB [REB_DATA_LENGTH] = DATA_LEN;
    REB [REB_TIME_BLOCK] = 0;
    NML$DECLARE_EVENT (REB);
    end;					! End of LOG_PASSIVE_LOOPBACK
%routine ('LOG_SERVICE_EVENT', SB : ref SERVICE_BLOCK, FUNC_CODE) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine initiates an "automatic line service" event
!    for the specified entity.
!
! Formal parameters:
!
!    SB            Service block
!    .FUNC_CODE    Function code (LOAD_, DUMP_)
!
! Routine value: none
!
!--

    begin

    local
        DATA : vector [ch$allocation (86, 8)],
        DATA_PTR;

    DATA_PTR = ch$ptr (DATA,, 8);
    begin                               ! Parameter #0

    literal
        DATA_ID = 0^15 + 0,
        DATA_TYPE = 1^7 + 0^6 + 1;

    ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
    ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
    ch$wchar_a (DATA_TYPE, DATA_PTR);
    ch$wchar_a ((if .FUNC_CODE eql LOAD_ then 0 else 1), DATA_PTR);
    end;
    begin                               ! Parameter #1

    literal
        DATA_ID = 0^15 + 1,
        AI_DATA_TYPE = 0^7 + 1^6,
        C_1_DATA_TYPE = 1^7 + 0^6 + 1,
        C_2_DATA_TYPE = 1^7 + 0^6 + 2,
        CM_1_DATA_TYPE = 1^7 + 1^6 + 1,
        CM_2_DATA_TYPE = 1^7 + 1^6 + 2,
        CM_3_DATA_TYPE = 1^7 + 1^6 + 3;

    local
        CM_PTR,
        RESP_PTR;

    RESP_PTR = .SB [SB_RESPONSE_PTR];
    ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
    ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
    CM_PTR = .DATA_PTR;
    ch$wchar_a (CM_1_DATA_TYPE, DATA_PTR);
    ch$wchar_a (C_1_DATA_TYPE, DATA_PTR);
    ch$wchar_a (ch$rchar (.RESP_PTR), DATA_PTR);

    if ch$rchar_a (RESP_PTR) gtr 127
    then
        begin

        local
            LEN;

        ch$wchar (CM_2_DATA_TYPE, .CM_PTR);
        ch$wchar_a (C_2_DATA_TYPE, DATA_PTR);
        ch$wchar_a (ch$rchar_a (RESP_PTR), DATA_PTR);
        ch$wchar_a (ch$rchar_a (RESP_PTR), DATA_PTR);

        if (LEN = ch$rchar_a (RESP_PTR)) neq 0
        then
            begin
            LEN = min (.LEN, 72);
            ch$wchar (CM_3_DATA_TYPE, .CM_PTR);
            ch$wchar_a (AI_DATA_TYPE, DATA_PTR);
            ch$wchar_a (.LEN, DATA_PTR);
            DATA_PTR = ch$move (.LEN, .RESP_PTR, .DATA_PTR);
            end;

        end;

    end;
    begin

    local
        REB : RAW_EVENT_BLOCK;

    REB [REB_EVENT_CODE] = 0^6 + 3;
    REB [REB_ENTITY_TYPE] = .SB [SB_ENTITY_TYPE];
    REB [REB_ENTITY_POINTER] = .SB [SB_ENTITY_PTR];
    REB [REB_DATA_POINTER] = ch$ptr (DATA,, 8);
    REB [REB_DATA_LENGTH] = ch$diff (.DATA_PTR, ch$ptr (DATA,, 8));
    REB [REB_TIME_BLOCK] = 0;
    NML$DECLARE_EVENT (REB);
    end;
    end;					! End of LOG_SERVICE_EVENT
%routine ('SET_SUBSTATE', SB : ref SERVICE_BLOCK) : NML_LKG_DB novalue =

!++
! Functional description:
!
!    This routine
!
! Formal parameters:
!
!    .SB     Service block
!
! Routine value: none
!
!--

    begin

    if .SB [SB_SUBSTATE] neq CIRCUIT_NO_SUBSTATE
    then
        NML$SET_VDB_PARAMETER (.SB [SB_ENTITY_TYPE], .SB [SB_ENTITY_PTR],
                               CPARM_SUBSTATE,
                               SB [SB_SUBSTATE])
    else
        NML$CLR_VDB_PARAMETER (.SB [SB_ENTITY_TYPE], .SB [SB_ENTITY_PTR],
                               CPARM_SUBSTATE);
                                  
    end;					! End of SET_SUBSTATE

end                                     ! End of NMLDLW
eludom
! Local Modes:
! Mode:Bliss
! Comment Start:!
! Comment Column:40
! Comment Rounding:+1
! Auto Save Mode:2
! End: