Google
 

Trailing-Edge - PDP-10 Archives - BB-P363B-SM_1985 - t20/nmlt20/nmldtl.bli
There are 2 other files named nmldtl.bli in the archive. Click here to see a list.
! UPD ID= 339, SNARK:<6.1.NML>NMLDTL.BLI.49,  15-Aug-85 12:41:10 by MCCOLLUM
!  Rewrite some of the LOAD_NODE and it subroutines to handle MOP
!   requests properly. This makes it possible to do downline loading
!   under high load averages.
!
! UPD ID= 335, SNARK:<6.1.NML>NMLDTL.BLI.48,  10-Jul-85 13:57:47 by MCCOLLUM
!  1. Don't TRIGGER after unsucessful LOAD for now. We have a race when 2
!     KL's try to load and keep TRIGGERing.
!  2. Generate 0.3 events in LOAD_NODE so we get six events for each
!     complete load operation. Each request program now generates a 0.3
!     REQUEST and either a 0.3 success or 0.7 failure event.
!  3. Wait a while after each file is loaded in LOAD_NODE for the response.
!     We weren't waiting long enough and were getting 0.7 events when we
!     shouldn't have.
!  4. Assume success in LOAD_NODE instead of failure and make sure all
!     errorr returns set up an response code for the specific error. This
!     will prevent us from getting ambiguous "Operation Failures".
!
! UPD ID= 325, SNARK:<6.1.NML>NMLDTL.BLI.26,  10-May-85 15:35:57 by GLINDELL
!  1. Delay sending 'ASSISTANCE VOLOUNTEER' until NMLDTL has opened the
!     load file at least.  This should greatly reduce the risk of NMLDTL
!     volounteering to load without having all the proper parameters.
!
! UPD ID= 291, SNARK:<6.1.NML>NMLDTL.BLI.25,  15-Mar-85 13:14:06 by GLINDELL
!  1. TRIGGER after unsuccessful LOAD even if AUTOSERVICE.
!  2. Assume failure instead of success in LOAD_NODE.  This will cover us
!     when LOAD_NODE returns failure without setting up a response code.
!
! UPD ID= 285, SNARK:<6.1.NML>NMLDTL.BLI.24,  13-Mar-85 14:19:58 by GLINDELL
!  Work on loading of MAINTENANCE images.  Support a -2 as software ID.
!  Add flag LB_LOAD_MAINT_IMAGE flag to service block.
!
! UPD ID= 283, SNARK:<6.1.NML>NMLDTL.BLI.23,  12-Mar-85 13:56:06 by GLINDELL
!  TRIGGER code left the circuit state in TRIGGERING if a SERVICE password
!  wasn't present.  Need to reset RTN_COD in EXECUTE_TRIGGER before 2nd try.
!
! UPD ID= 281, SNARK:<6.1.NML>NMLDTL.BLI.22,  10-Mar-85 14:26:40 by GLINDELL
!  1. Remove edit history prior to phase IV
!  2. Rearrange order of routines.
!
! UPD ID= 278, SNARK:<6.1.NML>NMLDTL.BLI.21,   6-Mar-85 23:11:13 by GLINDELL
!  NML$DLW_READ_LDAQ now has the additional argument of the hardware address
!  we are interested in.
!
! UPD ID= 267, SNARK:<6.1.NML>NMLDTL.BLI.20,   1-Mar-85 10:02:37 by GLINDELL
!  Lots of work to support POSEIDON's, dumping on the NI and clean up of DTE.
!
! UPD ID= 259, SNARK:<6.1.NML>NMLDTL.BLI.19,  10-Feb-85 14:49:35 by GLINDELL
!  Remove definitions of STATE/SUBSTATE/SERVICE values
!  Change all LINK_ to CIRCUIT_
!
! UPD ID= 248, SNARK:<6.1.NML>NMLDTL.BLI.18,  25-Jan-85 14:01:09 by HALPIN
! Add support of downline loading of DECSA's Remote Console Microcode.
!
! UPD ID= 220, SNARK:<6.1.NML>NMLDTL.BLI.17,  17-Dec-84 10:32:31 by HALPIN
! Make sure NML$LOAD prints an error message before calling EXECUTE_TRIGGER.
! Dumps via the NI only use the HARDWARE ADDRESS.
! Get MONSYM Library file out of default directory, not BLI:
! Add PHYSICAL ADDRESS (10) and HARDWARE ADDRESS (114) to the list of 
! Acceptable Parameters in NML$DUMP.
!
! UPD ID= 183, SNARK:<6.1.NML>NMLDTL.BLI.16,   7-Dec-84 09:09:35 by HALPIN
! Change IMAGE_LOAD on Tertiary Loaders, the  last message is a "MEMORY LOAD
! WITH TRANSFER ADDRESS" with a 'null' memory load address.  System Images
! still send "PARAMETER LOAD WITH TRANSFER ADDRESS" as last message.
! Don't call $NMU_DLX_CLOSE after LOAD and DUMP requests.  NI Data
! Link Watcher keeps the NI Open all the time watching for Load
! and Dump requests.
! Add SB_EXECUTE_TRIGGER bit flag to the service block.
! LOAD/DUMPS via the NI now call NML$DLW_READ_LDAQ, instead of
! $NMU_DLX_READ.  The NI Data Link Watcher Task reads all messages
! from the LOAD/DUMP Portal and queues up any messages meant for current
! Load/Dump Requests.
! Make different MOP_LOAD_DATA_LENGTH symbols for DTE and NI's.
! Add PSI channel numbers in call to $NMU_DLX_OPEN
!
! UPD ID= 170, SNARK:<6.1.NML>NMLDTL.BLI.15,  19-Nov-84 09:12:26 by HALPIN
! Fix comparison for Secondary Loader too large.
! Modify edit 157.  After calling EXECUTE_TRIGGER exit the Load routines
! and rely on the Data Link Watcher process to start a new load request.
!
! UPD ID= 161, SNARK:<6.1.NML>NMLDTL.BLI.14,   9-Nov-84 12:50:46 by HALPIN
! NML$LOAD node uses HARDWARE ADDRESS instead of PHYSICAL ADDRESS parameter.
!
! UPD ID= 157, SNARK:<6.1.NML>NMLDTL.BLI.13,   5-Nov-84 12:53:39 by HALPIN
! Have LOAD_NODE routine try to TRIGGER the target node if the first
! attempt at doing a Secondary Load fails.
!
! UPD ID= 156, SNARK:<6.1.NML>NMLDTL.BLI.12,   2-Nov-84 16:43:17 by HALPIN
! Move code from NML$TRIGGER to new routine EXECUTE_TRIGGER. 
!
! UPD ID= 155, SNARK:<6.1.NML>NMLDTL.BLI.11,   2-Nov-84 14:14:25 by HALPIN
! Add SB_SRVC_CKT_DEVICE, Service circuit device type field to the 
! Service Block. Call $NMU_DLX_DEVICE_TYPE to get the circuit device
! type code.
! Add DEVTYP_NI definition.
! Increase MOP_BUFFER_LENGTH to 1498.  Keep DTE's from using more than
! 522 bytes of it.
! Call NMU$CALCULATE_CIRCUIT_STATE in CIRCUIT_OPEN before getting the
! State and Substate from VDB.
! Add HARDWARE ADDRESS to list of acceptable parameters in Load routine.
!
! UPD ID= 154, SNARK:<6.1.NML>NMLDTL.BLI.10,   1-Nov-84 17:55:50 by GUNN
! Fix DTL_SETUP to handle PHYSICAL and HARDWARE ADDRESS parameters.
! Fix TRIGGER_NODE to get SERVICE PASSWORD from VDB. It didn't work.
!
! UPD ID= 151, SNARK:<6.1.NML>NMLDTL.BLI.9,  31-Oct-84 10:20:25 by GUNN
! Change length of MOP message passed in call to NMU$DLX_WRITE from
! TRIGGER_NODE from 5 to 12 bytes. This is the MOP V3.0 BOOT message
! length. 
! Remove setting of response code in response message in TRIGGER_NODE
! on failure of NMU$DLX_WRITE. The response message should be set up
! prior to the failure return.
!
! UPD ID= 148, SNARK:<6.1.NML>NMLDTL.BLI.8,  30-Oct-84 17:38:30 by GUNN
! Add DLX_TRIGGER case to CIRCUIT_OPEN.
! Add code in TRIGGER_NODE to produce MOP V3.0 BOOT message.
!
! UPD ID= 144, SNARK:<6.1.NML>NMLDTL.BLI.7,  29-Oct-84 13:46:19 by GUNN
! Change NML$TRIGGER to set SB_USAGE to DLX_TRIGGER rather than DLX_OTHER.
!
! UPD ID= 135, SNARK:<6.1.NML>NMLDTL.BLI.6,  24-Oct-84 16:13:30 by GUNN
! Add code to do trigger boot for Ethernet nodes.
! Add SB_PHY_ADDR field to service data block structure and pass
! on call to $NMU_DLX_OPEN. Remove $MCB conditionals. 
! Add SB_PHY_ADDR as case in FETCH_PARAMETERS
!
! UPD ID= 122, SNARK:<6.1.NML>NMLDTL.BLI.5,   5-Oct-84 14:32:05 by HALPIN
! Allow a circuit to be opened if the Substate is STARTING.
!
! UPD ID= 85, SLICE:<6.1.NML>NMLDTL.BLI.4,  18-Sep-84 14:59:18 by GUNN
! WORK:<GUNN.NML>NMLDTL.BLI.3 24-Aug-84 16:56:54, Edit by GUNN
!
! Change call to $NMU_DLX_OPEN to accomodate new PHYSICAL ADDRESS
! parameter needed for Ethernet.
!
! WORK:<GUNN.NML>NMLDTL.BLI.2 21-Aug-84 12:04:25, Edit by GUNN
!
! Change to accomodate new LIBRARY conventions. MONSYM.L36 and JLNKG.L36
! are now explicity declared here rather than in NMULIB.
!
! UPD ID= 36, SNARK:<6.1.NML>NMLDTL.BLI.3,  24-May-84 16:27:49 by GLINDELL
! Fix dot bug in IMAGE_LOAD with regard to SERVICE NODE VERSION
!
! PH4:<HALPIN>NMLDTL.BLI.5 30-Nov-83 11:26:25, Edit by HALPIN
! Ident 53.
!  IMAGE_LOAD the the value of SERVICE NODE VERSION while building
!  the TARGET SYSTEM and HOST SYSTEM ADDRESSes in the PARAMETER LOAD
!  WITH TRANSFER ADDRESS message
!
! Ident 52.
!  New style entity id type names. Change in $NMU_DLX_OPEN invocation.
!
module NMLDTL	(
		ident = 'X03.53'
		) =
begin

!
!		  COPYRIGHT (c) 1980, 1981, 1984, 1985 BY
!	      DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
!
! 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:	DECnet-10/20 V3.0 Network Management Layer (NML)
!
! ABSTRACT:
!
!	Performs remote node dump, trigger boot, and load.
!
! ENVIRONMENT:	TOPS-10/20 & MCB/RSX11 User mode under NML
!
! AUTHOR: Dale C. Gunn & Steven Jenness , CREATION DATE: 12-Nov-80
!
! MODIFIED BY:
!
!	, : VERSION
! 01	-
!--
!
! INCLUDE FILES:
!

library 'NMLLIB';                       ! All required definitions

library 'MONSYM';			! Monitor symbols

library 'JLNKG';			! JSYS linkage definitions

require 'NMLEXT';                       ! External routine declarations

!
! TABLE OF CONTENTS
!

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

forward routine
        NML$DUMP,
        NML$LOAD,
        NML$TRIGGER,
        DTL_SETUP,
        DTL_CLEANUP,
        CIRCUIT_CLOSE : NML_LKG_DB,
        CIRCUIT_OPEN : NML_LKG_DB,
        CIRCUIT_PROMPT : NML_LKG_DB,
        CPU_IS_PDP11 : NML_LKG_DB,
        DUMP_NODE : NML_LKG_DB,
        FETCH_PARAMETERS,
        FILE_CLOSE : NML_LKG_DB,
        FILE_OPEN : NML_LKG_DB,
        FILE_READ_LABEL : NML_LKG_DB,
        GET_NEXT_PARAMETER,
        IMAGE_LOAD : NML_LKG_DB,
        WRITE_ASSISTANCE_VOLUNTEER : NML_LKG_DB,
        WRITE_MOP_MESSAGE : NML_LKG_DB, 
        CONSOLE_IMAGE_LOAD : NML_LKG_DB,
        LOAD_NODE : NML_LKG_DB novalue,
        SECONDARY_LOAD : NML_LKG_DB,
        SET_SUBSTATE : NML_LKG_DB novalue,
        EXECUTE_TRIGGER : NML_LKG_DB novalue,
        TRIGGER_NODE : NML_LKG_DB,
        GET_VDB;

!
! LOAD, DUMP & TRIGGER data block structure definitions
!
$show (fields) $show (literals)

$field
      SERVICE_DATA_FIELDS =
      set

! Common data for LOAD, DUMP and TRIGGER

      SB_CIRCUIT_HANDLE = [$integer],   ! Link handle for circuit
      SB_RESPONSE_PTR = [$pointer],     ! Pointer to response buffer
      SB_BUFFER = [$address],           ! Address of MOP buffer
      SB_BUFFER_PTR = [$pointer],       ! Pointer to MOP buffer
      SB_NODE_DATA = [$pointer],        ! Pointer to node entity ID
      SB_PHY_ADDR = [$pointer],         ! Node parameter  10
      SB_ETHER_ADDR_BUFFER = [$bytes (6)], ! Buffer for Ethernet Address string
      SB_SERVICE_CIRCUIT = [$pointer],  ! 110 Pointer to service circuit-id
      SB_SERVICE_PASSWORD = [$pointer], ! 111 Pointer to hexadecimal passwd
      LB_LOAD_FILE = [$pointer],        ! 120 Pointer to system image
      LB_SECONDARY_LOADER = [$pointer], ! 121 Pointer to secondary loader
      LB_TERTIARY_LOADER = [$pointer],  ! 122 Pointer to tertiary loader
      LB_DIAGNOSTIC_FILE = [$pointer],  ! 123 Pointer to diagnostic file
      LB_CONSOLE_SECONDARY_LOADER = [$pointer], ! 2501 Pointer to console secondary loader
      LB_CONSOLE_LOAD_FILE = [$pointer], ! 2502 Pointer to Console Carrier Microcode
      LB_SOFTWARE_IDENTIFICATION = [$pointer], ! 126 Pointer to id string
      DB_DUMP_FILE = [$pointer],           ! 130 Pointer to dump image file 
      DB_SECONDARY_DUMPER = [$pointer],    ! 131 Pointer to secondary dumper
      LB_NAME = [$pointer],                ! 500 Pointer to node name block
      LB_ADDRESS = [$pointer],             ! 502 Pointer to node address block
      DB_DUMP_ADDRESS = [$bytes(4)],       ! 135 4 byte dump address
      DB_DUMP_COUNT = [$bytes(4)],         ! 136 4 byte dump count
      SB_HARDWARE_ADDRESS = [$pointer],    ! 114 Pointer to ETHERNET Hardware
                                           !     Address
      LB_HOST = [$bytes (NODE_ID_BUFFER_LENGTH)],
                                           ! 140 HOST node id block
      LB_SERVICE_DEVICE = [$tiny_integer], ! 112 Device type
      LB_CPU = [$tiny_integer],            ! 113 CPU type
      LB_SOFTWARE_TYPE = [$tiny_integer],  ! 125 Software type
      LB_PROCESSOR = [$byte],              ! Processor to be loaded
      LB_DL_BUFFER_SIZE = [$integer],      ! Data Link Buffer Size
      LB_LOAD_MAINT_IMAGE = [$bit],        ! Use MAINT image if this bit set
      SB_USAGE = [$tiny_integer],          ! Function being performed
      SB_SUBSTATE = [$tiny_integer],       ! Current substate
      SB_NODE_VERSION = [$tiny_integer],   ! Service Node Version
      SB_SRVC_CKT_DEVICE = [$tiny_integer],! Service Circuit Device Type
      SB_EXECUTE_TRIGGER = [$bit],         ! Secondary Load failed, call EXECUTE_TRIGGER
      SB_AUTOSERVICE = [$bit],             ! True if auto-service
      SB_NEED_RPM = [$bit]                 ! Set if we need a RPM before going
      tes;

literal
       SERVICE_DATA_BLOCK_SIZE = $field_set_size,
       SERVICE_DATA_BLOCK_ALLOCATION = $field_set_units ;

macro
     SERVICE_DATA_BLOCK = block [SERVICE_DATA_BLOCK_SIZE]
                       field (SERVICE_DATA_FIELDS) %;

! End of service structure definition

!%( *** TEMPORARY - until NMLLIB is modified			!***
macro RB_DTL_DATA = RB_LOAD_DATA%;				!***
macro RB_DTL_DATA_ALLOCATION = RB_LOAD_DATA_ALLOCATION%;	!***


!
! File handling structure definition
!

$field
      FILE_DATA_FIELDS =
      set
      FB_FILE_HANDLE = [$integer],
      FB_RESPONSE_PTR = [$pointer],
      FB_FILE = [$pointer],
      FB_CURRENT_ADDRESS = [$bytes (4)],
      FB_BYTES_LEFT = [$bytes (4)],
      FB_TRANSFER_ADDRESS = [$bytes (4)],
      FB_FILE_TYPE = [$byte]
      tes;

literal
       FILE_DATA_BLOCK_SIZE = $field_set_size,
       FILE_DATA_BLOCK_ALLOCATION = $field_set_units ;

macro
     FILE_DATA_BLOCK = block [FILE_DATA_BLOCK_SIZE]
                       field (FILE_DATA_FIELDS) %;

! End of file structure definition

$show (none)
!
! Macros:
!

macro
     CH$HEX [ ] =
         ch$ptr (uplit (X_CHAR(%explode(%remaining))),,8) %;

macro
     X_CHAR [B1,B2,B3,B4,B5,B6,B7,B8] =
            ((%X B1 ^ (%bpval-4))
              %if not %null(B2)
              %then
                   or (%X B2 ^ (%bpval-8))
              %if not %null(B3)
              %then
                   or (%X B3 ^ (%bpval-12))
              %if not %null(B4)
              %then
                   or (%X B4 ^ (%bpval-16))
              %if not %null(B5)
              %then
                   or (%X B5 ^ (%bpval-20))
              %if not %null(B6)
              %then
                   or (%X B6 ^ (%bpval-24))
              %if not %null(B7)
              %then
                   or (%X B7 ^ (%bpval-28))
              %if not %null(B8)
              %then
                   or (%X B8 ^ (%bpval-32))
              %fi %fi %fi %fi %fi %fi %fi) %;

!	CH$ASCIC (STRING, ...)
!
!	    Return a pointer to an ASCIC character position sequence
!           formed by the characters represented by the string-params, 
!           which are interpreted as for the %string lexical function.


macro
     DLW$ASCIC [ ] =
         ch$ptr (uplit (%string (%char (%charcount (%remaining)), %remaining))) %;

!
! Equated symbols:
!

literal
       DEVTYP_NI = 15,                  ! NI Device Type code
       DEVTYP_DTE = 20,                 ! DTE Device type code

       RESPONSE_BUFFER_LENGTH = 256,    ! NICE response buffer in bytes
       RESPONSE_BUFFER_SIZE = ch$allocation (RESPONSE_BUFFER_LENGTH,8),
       RESPONSE_BUFFER_ALLOCATION = RESPONSE_BUFFER_SIZE * %upval,

       MOP_BUFFER_LENGTH = 1506,         ! MOP message buffer in bytes
       MOP_BUFFER_SIZE = ch$allocation (MOP_BUFFER_LENGTH, 8),
       MOP_BUFFER_ALLOCATION = MOP_BUFFER_SIZE * %upval,

       DTE_MOP_LOAD_DATA_LENGTH = (254 - 6),! Max bytes in image in load message
       NI_MOP_LOAD_DATA_LENGTH = (512 - 6),! Max bytes in image in load message
       MOP_RESPONSE_LENGTH = 32;        ! Max bytes in ACK to load

!
! RSX .SYS file label definitions
!

literal
    L$BSA = %o'10',                 ! Base address of image
    L$BLDZ = %o'16',                ! Load size of image
    L$BFLG = %o'30',                ! Header flags word
    L$BXFR = %o'350',               ! Transfer address
!   L$BHRB = %o'356',               ! Starting block of image (0 relative)
    L$BBLK = %o'360';               ! Number of label blocks

!
! STATE/SUBSTATE/SERVICE definitions
!

literal
    CPARM_STATE = 0,
    CPARM_SUBSTATE = 1,
    CPARM_SERVICE = 100;

literal
    SYSTEM_PROCESSOR = 0,
    COMMUNICATION_PROCESSOR = 1;
!
! External references:
!

external routine
         NML$FIND_NDB,                  ! Find node entity for circuit
         NML$PARAMETER_DATA,            ! Get NICE parameter data
         NML$CLR_VDB_PARAMETER,         ! Clear parameter in VDB
         NML$GET_VDB_PARAMETER,         ! Obtain parameter data from VDB
         NML$SET_VDB_PARAMETER,         ! Set parameter in VDB
         NML$DLW_READ_LDAQ,             ! Read the Load/Dump Assistance Queue
         NML$CALCULATE_CIRCUIT_STATE : novalue,
         NML$DLW_SERVICE_EVENT : novalue, ! Queue a service event
         NMU$NETWORK_LOCAL,             ! Get local node number/name
         NMU$FILE_ROUTINES;             ! File access routines
%global_routine ('NML$LOAD', REQ : ref REQUEST_BLOCK) =

!++
! Functional description:
!
!	This routine performs the NICE load request function.
!       It is responsible for extracting the parameter data,
!       required for the load function, from the volatile data
!       base and merging this data with any parameter data
!       supplied with the load request. It calls LOAD_NODE to
!       actually perform the function.
!
! Formal parameters:
!
!	.REQ	NML Request Block address.
!
! Implicit inputs: none
!
! Routine value:
!
!	NICE return code.
!
! Side effects: none
!
!--

    begin
    bind
        ACCEPTABLE_PARAMETERS = plit (10, 110, 111, 112, 113, 114, 115,
                                      120, 121, 122, 123, 125 %(, 126)% ,
                                      141, 500, 502, 2511, 2512);

    macro                               ! Synonym for response buffer pointer
         RESPONSE_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8) %;

    local
         RTN_COD;

    if not .REQ[RB_PRV_SERVICE] then return NICE$_PRV;

    !
    ! Set up the load service block.
    !

    if DTL_SETUP (.REQ, ACCEPTABLE_PARAMETERS) lss 0
    then
        return (ch$rchar(RESPONSE_PTR));

    !
    ! Access service block
    !

    begin

    local
         LB : ref SERVICE_DATA_BLOCK;

    RTN_COD = NICE$_SUC;
    LB = .REQ[RB_DTL_DATA];
    LB [SB_USAGE] = DLX_LOAD;

    LB [SB_PHY_ADDR] = .LB [SB_HARDWARE_ADDRESS];  ! Use HARDWARE ADDR for LOAD

    !
    ! Open the link if we can
    !

    if not CIRCUIT_OPEN (.LB)
    then
        RTN_COD = ch$rchar (.LB [SB_RESPONSE_PTR]);

    !
    ! Execute the load.
    ! Deallocate the buffers.
    !

    if .RTN_COD eql NICE$_SUC
    then
	begin
        local TEMP_RTN_COD;
	LOAD_NODE (.LB);
        RTN_COD = ch$rchar (RESPONSE_PTR);
        CIRCUIT_CLOSE (.LB);
	end;

    end;
    DTL_CLEANUP (.REQ, .RTN_COD)
    end;				!End of NML$LOAD
%global_routine ('NML$DUMP', REQ : ref REQUEST_BLOCK) =

!++
! Functional description:
!
!	This routine performs the NICE dump request function.
!       It is responsible for extracting the parameter data,
!       required for the dump function, from the volatile data
!       base and merging this data with any parameter data
!       supplied with the dump request. It calls DUMP_NODE to
!       actually perform the function.
!
! Formal parameters:
!
!	.REQ	NML Request Block address
!
! Implicit inputs: none
!
! Routine value:
!
!	NICE return code.
!
! Side effects: none
!
!--

    begin
    bind
        ACCEPTABLE_PARAMETERS = plit (10, 110, 111, 114, 130, 131 %(, 135, 136)% );

    macro                               ! Synonym for response buffer pointer
         RESPONSE_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8) %;

    local
         RTN_COD;

    if not .REQ[RB_PRV_SERVICE] then return NICE$_PRV;

    !
    ! Set up the dump service block
    !

    if DTL_SETUP (.REQ, ACCEPTABLE_PARAMETERS) lss 0
    then
        return (ch$rchar(RESPONSE_PTR));

    !
    ! Access service block
    !

    begin

    local
         DB : ref SERVICE_DATA_BLOCK;

    DB = .REQ[RB_DTL_DATA];
    DB [SB_USAGE] = DLX_DUMP;
    RTN_COD = NICE$_SUC;

    DB [SB_PHY_ADDR] = .DB [SB_HARDWARE_ADDRESS];  ! Use HARDWARE ADDR for LOAD

    !
    ! Open the link if we can
    !

    if not CIRCUIT_OPEN (.DB)
    then
        RTN_COD = ch$rchar (RESPONSE_PTR);

    !
    ! Execute the dump.
    !

    if .RTN_COD eql NICE$_SUC
    then
	begin
	DUMP_NODE (.DB);
        RTN_COD = ch$rchar (RESPONSE_PTR);
        CIRCUIT_CLOSE (.DB);
	end;

    end;
    DTL_CLEANUP (.REQ, .RTN_COD)
    end;				!End of NML$DUMP
%global_routine ('NML$TRIGGER', REQ : ref REQUEST_BLOCK) =

!++
! Functional description:
!
!	This routine performs the NICE trigger request function.
!       It is responsible for extracting the parameter data,
!       required for the trigger function, from the volatile data
!       base and merging this data with any parameter data
!       supplied with the trigger request. It calls TRIGGER_NODE to
!       actually perform the function.
!
! Formal parameters:
!
!	.REQ	NML Request Block address.
!
! Implicit inputs: none
!
! Routine value:
!
!	NICE return code.
!
! Side effects: none
!
!--

    begin
    bind
        ACCEPTABLE_PARAMETERS = plit (10, 110, 111);

    macro                               ! Synonym for response buffer pointer
         RESPONSE_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8) %;

    local
         RTN_COD;

    if not .REQ[RB_PRV_SERVICE] then return NICE$_PRV;

    !
    ! Set up the trigger service block.
    !

    if DTL_SETUP (.REQ, ACCEPTABLE_PARAMETERS) lss 0
    then
        return (ch$rchar(RESPONSE_PTR));

    !
    ! Access service block
    !

    begin

    local
         TB : ref SERVICE_DATA_BLOCK;

    RTN_COD = NICE$_SUC;
    TB = .REQ[RB_DTL_DATA];

    EXECUTE_TRIGGER (.TB, RTN_COD);

    end;

    DTL_CLEANUP (.REQ, .RTN_COD)
    end;				!End of NML$TRIGGER
%routine ('DTL_SETUP', REQ: ref REQUEST_BLOCK, ACCEPTABLE_PARAMETERS: VECTOR) =

!++
! Functional description:
!
!	This routine does setup for LOAD, DUMP and TRIGGER.  It allocates
!	the service block that is used to control the process and sets up
!	its parameters.  All parameters that have no default value and that
!	have not been specified will be given "NA" values: -1 for $integer's
!	and the null pointer for pointers.
!
!
! Formal parameters:
!
!	.REQ			NML Request Block address.
!	.ACCEPTABLE_PARAMETERS	A list of the acceptable parameters for
!				the request type being done.
!
! Implicit inputs: none
!
! Routine value:
!
!	 1	Success: setup done
!	-1	Failure: one or more essential parameters are missing, or an
!			 specified parameter was invalid.  The MOP buffer,
!			 the service block and the NICE response buffer have
!			 been allocated.
!	-3	Failure: the routine was not able to allocate a service block
!			 plus a MOP buffer.  The NICE response buffer has been
!			 allocated.
!	-4	Failure: the routine was not able to allocate a NICE response
!			 buffer.  The service block and the MOP buffer were
!			 not allocated.
!
! Side effects: none.
!
!--

    begin

    label
        CHECK_PARAMETERS;

    local
	RET,
	BUFFER,
        DLW_SR : ref DLWQ_BLOCK,
	SB: ref SERVICE_DATA_BLOCK;

    macro                               ! Synonym for response buffer pointer
         RESPONSE_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8) %;

    REQ[RB_DTL_DATA] = 0;
    RET = 1;

    !
    ! Allocate NICE response buffer.
    !

    REQ[RB_RESPONSE_ALLOCATION] = RESPONSE_BUFFER_ALLOCATION ;
    if (REQ[RB_RESPONSE] = NMU$MEMORY_GET (.REQ[RB_RESPONSE_ALLOCATION])) eql 0
    then begin                          ! Allocation failure
	REQ[RB_RETURN_CODE] = NICE$_REE;
	return -4
	end;

    !
    ! Allocate a SERVICE_DATA_BLOCK plus transmit and receive MOP buffers
    !

    REQ[RB_DTL_DATA_ALLOCATION] = SERVICE_DATA_BLOCK_ALLOCATION
        + MOP_BUFFER_ALLOCATION;

    if (SB = NMU$MEMORY_GET (.REQ[RB_DTL_DATA_ALLOCATION])) eql 0
    then                                ! Allocation failure
        begin
        $RESPONSE (RESPONSE_PTR,NICE$_REE,0);
	return -3;
        end;


    DLW_SR = .REQ [RB_DLW_DATA];

CHECK_PARAMETERS:
    begin                               ! CHECK_PARAMETERS
    REQ[RB_DTL_DATA] = .SB;
    SB[SB_BUFFER] = vector [.SB, SERVICE_DATA_BLOCK_SIZE];
    SB[SB_BUFFER_PTR] = ch$ptr (.SB [SB_BUFFER],, 8);

    !
    ! Merge information in SERVICE_DATA_BLOCK from NML volatile data base
    ! for this node, and any parameters supplied on the dump request.
    !

    SB [SB_RESPONSE_PTR] = RESPONSE_PTR;

    selectone .REQ[RB_NICE_ENTITY_TYPE] of
        set
        [ENTITY_NODE]:                   ! NODE entity
            begin
            SB[SB_NODE_DATA] = .REQ[RB_NICE_ENTITY] ; ! NODE for this load
            if .DLW_SR neq 0
            then SB[SB_SERVICE_CIRCUIT] =
                       ch$ptr (DLW_SR [DLWQ_CIRCUIT_NAME],,8);
            end;
        [ENTITY_CIRCUIT]:                ! CIRCUIT entity
            begin
            SB[SB_SERVICE_CIRCUIT] = .REQ[RB_NICE_ENTITY] ; ! CIRCUIT for this
                                                            ! load

            ! If this is a DTE load, then try to find the node that has this
            ! DTE circuit as its service circuit
            if $NMU_DLX_DEVICE_TYPE (.SB [SB_SERVICE_CIRCUIT]) eql DEVTYP_DTE
            then SB[SB_NODE_DATA] = NML$FIND_NDB (.REQ[RB_NICE_ENTITY])
            end;
        tes;

    !
    ! Set default (NA) values for all parameter fields.
    !

        SB[SB_PHY_ADDR] = 0;
        SB[SB_SERVICE_PASSWORD] = 0;
        SB[LB_LOAD_FILE] = 0;
        SB[LB_SECONDARY_LOADER] = 0;		! Set all pointer
        SB[LB_TERTIARY_LOADER] = 0;		! fields to the
        SB[LB_SOFTWARE_IDENTIFICATION] = 0;	! null pointer
        SB[LB_CONSOLE_SECONDARY_LOADER] = 0;
        SB[LB_CONSOLE_LOAD_FILE] = 0;
        SB[SB_HARDWARE_ADDRESS] = 0;
        SB[DB_DUMP_FILE] = 0;
        SB[DB_SECONDARY_DUMPER] = 0;

        begin
             local PTR;
             PTR = ch$ptr (SB[LB_HOST],, 8);
             ch$wchar_a (0, PTR);            ! Node number = 0
             ch$wchar_a (0, PTR);
             ch$wchar_a (0, PTR);            ! Node name = ''
             end;

        SB[LB_NAME] = 0;
        SB[LB_ADDRESS] = 0;

        SB[LB_SERVICE_DEVICE] = -1;
        SB[SB_NODE_VERSION] = -1;
        SB[LB_CPU] = -1;
        SB[LB_SOFTWARE_TYPE] = -1;		! Set all
        %movi32 (0, SB[DB_DUMP_ADDRESS]);	! numeric fields
        %subi32 (1, SB[DB_DUMP_ADDRESS]);	! to -1
        %movi32 (0, SB[DB_DUMP_COUNT]);
        %subi32 (1, SB[DB_DUMP_COUNT]);
        SB[LB_LOAD_MAINT_IMAGE] = $false;

        if .DLW_SR neq 0
        then begin

             SB[LB_PROCESSOR] = .DLW_SR [DLWQ_PROCESSOR];

             if .DLW_SR [DLWQ_DL_BUFSIZ] neq 0
             then SB[LB_DL_BUFFER_SIZE] = .DLW_SR [DLWQ_DL_BUFSIZ]
             else SB[LB_DL_BUFFER_SIZE] = 256 - 6;

             SB[LB_SOFTWARE_TYPE] = .DLW_SR [DLWQ_PROG_TYPE];

             if .DLW_SR [DLWQ_MEMORY_SIZE] neq 0
             then SB [DB_DUMP_COUNT] = .DLW_SR [DLWQ_MEMORY_SIZE];

             SB [SB_NEED_RPM] = .DLW_SR [DLWQ_NEED_RPM];
    
             ! If Software ID is -2, then use maintenance image
             ! If it's greater than 0, then it's a image name
             ! If it's 0, ignore it, and -1 we don't support
             if .DLW_SR [DLWQ_SID_FORM] eql -2
             then SB [LB_LOAD_MAINT_IMAGE] = $true;

             if .DLW_SR [DLWQ_SID_FORM] gtr 0
             then begin

                  ! If a software ID is present, build a filespec
                  !  like SYS:<software-ID>.SYS and use that as load file

                  local F_PTR, T_PTR, X_PTR, COUNT, TC;

                  F_PTR = ch$ptr (DLW_SR [DLWQ_SOFTWARE_ID],,8);
                  T_PTR = ch$ptr (DLW_SR [DLWQ_SUPPLIED_FILE],,8);

                  ! T_PTR is destination pointer.  Move past where we are
                  ! going to store count when we're done

                  T_PTR = ch$plus (.T_PTR,1);

                  ! Copy SYS:
                  X_PTR = DLW$ASCIC ('SYS:');
                  COUNT = GETB (X_PTR);
                  ch$move (.COUNT, .X_PTR, .T_PTR);
                  T_PTR = ch$plus (.T_PTR, .COUNT);

                  ! Copy the filename off the software ID field
                  TC = .DLW_SR [DLWQ_SID_FORM];

                  ! TOPS-10 has a maximum of 6 chars in file name
                  %if $tops10 %then if .TC gtr 6 then TC = 6; %fi

                  ch$move (.TC, .F_PTR, .T_PTR);
                  T_PTR = ch$plus (.T_PTR, .TC);
                  COUNT = .COUNT + .TC;

                  ! Copy extension .SYS
                  X_PTR = DLW$ASCIC ('.SYS');
                  TC = GETB (X_PTR);
                  ch$move (.TC, .X_PTR, .T_PTR);
                  COUNT = .COUNT + .TC;

                  ! Now store count in first byte
                  X_PTR = T_PTR = ch$ptr (DLW_SR [DLWQ_SUPPLIED_FILE],,8);
                  PUTB (.COUNT, T_PTR);

                  ! Set secondary, tertiary or system load file name
                  ! depending on software type
                  selectone .DLW_SR [DLWQ_PROG_TYPE] of
                  set

                  [0]: SB [LB_SECONDARY_LOADER] = .X_PTR;

                  [1]: SB [LB_TERTIARY_LOADER] = .X_PTR;

                  [2]: SB [LB_LOAD_FILE] = .X_PTR;

                  tes;

                  end;

             end;

        SB[SB_EXECUTE_TRIGGER] = $false;

        !
        ! Get PHYSICAL and HARDWARE ADDRESS Parameters for this NODE
        !

        ! Get hardware address
        SB [SB_HARDWARE_ADDRESS] =
           (if GET_VDB (ENTITY_NODE,        ! NODE for this load
                       .SB[SB_NODE_DATA],
                       114,                 ! HARDWARE ADDRESS
                       SB [SB_HARDWARE_ADDRESS])
           then ch$plus (.SB [SB_HARDWARE_ADDRESS],1)
           else if .DLW_SR neq 0
                then ch$ptr (DLW_SR[DLWQ_HARDWARE_ADDRESS],,8)
                else 0);

        !
        ! Build Ethernet physical address for this node
        ! in the SB.
        !
        begin
             local
                  ADDRESS,
                  NOD_PTR;


             if .SB [SB_NODE_DATA] neq 0
             then begin

                  NOD_PTR = .SB[SB_NODE_DATA];
                  ADDRESS = GETW (NOD_PTR);

                  NOD_PTR = ch$ptr (SB [SB_ETHER_ADDR_BUFFER],,8);
                  NOD_PTR = ch$move (4, ! Move HI-Order Ethernet address
                                     CH$HEX ('AA000400'),
                                     .NOD_PTR);

                  PUTW (ADDRESS, NOD_PTR);

                  SB [SB_PHY_ADDR] = ch$ptr (SB [SB_ETHER_ADDR_BUFFER],,8);
                  end
             else SB [SB_PHY_ADDR] = .SB [SB_HARDWARE_ADDRESS];
        end;                  

    !
    ! Merge any parameters specified in the NICE request message.
    !

    if FETCH_PARAMETERS (.REQ, .SB, .ACCEPTABLE_PARAMETERS) lss 0
    then leave CHECK_PARAMETERS with RET = -1; ! Problem with a parameter

    end;                                ! CHECK_PARAMETERS
    !
    ! If there was an error, release everything we got (except the
    ! response buffer).
    !

    if .RET lss 0
    then begin
         NMU$MEMORY_RELEASE (.REQ[RB_DTL_DATA], .REQ[RB_DTL_DATA_ALLOCATION]);
         REQ[RB_DTL_DATA] = 0;
         end;

    .RET

    end;			! End of DTL_SETUP
%routine ('DTL_CLEANUP', REQ: ref REQUEST_BLOCK, RTN_COD) =

!++
! Functional description:
!
!	None
!
! Formal parameters:
!
!	.REQ			NML Request Block address.
!       RTN_COD                 Current NICE return code
!
! Implicit inputs: none
!
! Routine value:
!
!	return code
! 
! Side effects: none.
!
!--

    begin

    macro                               ! Synonym for response buffer pointer
         RESPONSE_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8) %;

    !
    ! Deallocate service blocks
    !

    if .REQ[RB_DTL_DATA] neqa 0
    then NMU$MEMORY_RELEASE (.REQ[RB_DTL_DATA],.REQ[RB_DTL_DATA_ALLOCATION]) ;

    !
    ! If successful, build NICE repsonse message with success code.
    !

    if .RTN_COD eql NICE$_SUC
    then begin
         REQ [RB_RESPONSE_LENGTH] = $RESPONSE (RESPONSE_PTR, NICE$_SUC, 0) ;
         NML$REQUEST_FINISH (.REQ) ;
         end
    else if ch$rchar (RESPONSE_PTR) neq 0
         then
             REQ [RB_RESPONSE_LENGTH] = $RESPONSE_LENGTH (RESPONSE_PTR);

    .RTN_COD 
    end;			! End of DTL_CLEANUP
%routine ('CIRCUIT_OPEN', DB : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine opens the service circuit.
!
! Formal parameters
!
!    .DB      Address of the service data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    Service circuit successfully opened
!    $false   Error in opening circuit - response set
!
! Side effects: none
!
!--

    begin
    local
         CIRCUIT_HANDLE,
         SERVICE_CIRCUIT;

    if .DB [SB_SERVICE_CIRCUIT] eqla 0
    then
        if not GET_VDB (ENTITY_NODE,    ! CIRCUIT for this load
                        .DB[SB_NODE_DATA],
                        110,     ! SERVICE CIRCUIT
                        DB [SB_SERVICE_CIRCUIT])
        then
            begin
            $RESPONSE_X (.DB[SB_RESPONSE_PTR],NICE$_PAM,110);
            return $false;
            end;

    !
    ! Find out what type of Service Circuit we have (NI or DTE)
    !

    DB [SB_SRVC_CKT_DEVICE] = $NMU_DLX_DEVICE_TYPE (.DB [SB_SERVICE_CIRCUIT]);

    !
    ! Check the circuit state for ON or SERVICE
    !


    begin

    local
        CIRCUIT_STATE;

    NML$CALCULATE_CIRCUIT_STATE (.DB[SB_SERVICE_CIRCUIT]);

    if not NML$GET_VDB_PARAMETER (ENTITY_CIRCUIT,
                                  .DB [SB_SERVICE_CIRCUIT],
                                  CPARM_STATE,
                                  CIRCUIT_STATE)
    then CIRCUIT_STATE = CIRCUIT_ON;

    selectoneu .CIRCUIT_STATE of
        set
        [CIRCUIT_ON, CIRCUIT_SERVICE]:
            ;
        [otherwise]:
            begin
            $RESPONSE (.DB[SB_RESPONSE_PTR],NICE$_CWS,ENTITY_CIRCUIT);
            return $false;
            end;
        tes;

    end;

    !
    ! Check the service state for ENABLED
    !

    begin

    local
        CIRCUIT_SERVICE;

    if not NML$GET_VDB_PARAMETER (ENTITY_CIRCUIT, .DB [SB_SERVICE_CIRCUIT],
                                  CPARM_SERVICE, CIRCUIT_SERVICE)
    then CIRCUIT_SERVICE = CIRCUIT_DISABLED;

    selectoneu .CIRCUIT_SERVICE of
        set
        [CIRCUIT_ENABLED]:
            ;
        [otherwise]:
            begin
            $RESPONSE (.DB[SB_RESPONSE_PTR],NICE$_CWS,ENTITY_CIRCUIT);
            return $false;
            end;
        tes;

    end;

    !
    ! Check the circuit substate.
    ! If no substate, then we can go ahead.
    ! If autoservice, remember the fact.
    ! Otherwise abort the request.
    !

    begin

    local
        CIRCUIT_SUBSTATE;


    if not NML$GET_VDB_PARAMETER (ENTITY_CIRCUIT, .DB [SB_SERVICE_CIRCUIT],
                                  CPARM_SUBSTATE, CIRCUIT_SUBSTATE)
    then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;

    DB[SB_SUBSTATE] = .CIRCUIT_SUBSTATE;

    selectoneu .CIRCUIT_SUBSTATE of
        set
        [CIRCUIT_NO_SUBSTATE, CIRCUIT_FAILED, CIRCUIT_STARTING]:
            DB[SB_AUTOSERVICE] = $false;
        [CIRCUIT_AUTOSERVICE]:
            DB[SB_AUTOSERVICE] = $true;
        [otherwise]:
            begin
            $RESPONSE (.DB[SB_RESPONSE_PTR],NICE$_CWS,ENTITY_CIRCUIT);
            return $false;
            end;
        tes;

    end;
    CIRCUIT_HANDLE = $NMU_DLX_OPEN (.DB [SB_USAGE],
                                    CIRCUIT_, 
                                    .DB [SB_SERVICE_CIRCUIT],
                                    .DB [SB_PHY_ADDR],
                                    -1,
                                    -1,
                                    -1,
                                    .DB [SB_RESPONSE_PTR]);

    if .CIRCUIT_HANDLE leq 0 then return $false;

    DB [SB_CIRCUIT_HANDLE] = .CIRCUIT_HANDLE;

    !
    ! Set the substate for the requested operation
    !

    selectone .DB [SB_USAGE] of
        set
        [DLX_LOAD]:
            if .DB[SB_AUTOSERVICE]
            then DB[SB_SUBSTATE] = CIRCUIT_AUTOLOADING
            else DB[SB_SUBSTATE] = CIRCUIT_LOADING;
        [DLX_DUMP]:
            if .DB[SB_AUTOSERVICE]
            then DB[SB_SUBSTATE] = CIRCUIT_AUTODUMPING
            else DB[SB_SUBSTATE] = CIRCUIT_DUMPING;
        [DLX_TRIGGER]:
            if .DB[SB_AUTOSERVICE]
            then DB[SB_SUBSTATE] = CIRCUIT_AUTOTRIGGERING
            else DB[SB_SUBSTATE] = CIRCUIT_TRIGGERING;
        [otherwise]:
            return $false;
        tes;

    SET_SUBSTATE (.DB);
    $true
    end;				!End of CIRCUIT_OPEN
%routine ('CIRCUIT_CLOSE', DB : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine closes the circuit.
!
! Formal parameters
!
!    .DB      Address of the service data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    Circuit successfully closed
!
! Side effects: none
!
!--

    begin

    !
    ! Set the substate back to something reasonable
    !

    if .DB[SB_AUTOSERVICE]
    then DB[SB_SUBSTATE] = CIRCUIT_AUTOSERVICE
    else DB[SB_SUBSTATE] = CIRCUIT_NO_SUBSTATE;

    SET_SUBSTATE (.DB);

    if (.DB [SB_USAGE] eql DLX_TRIGGER) or 
       (.DB [SB_SRVC_CKT_DEVICE] eql DEVTYP_DTE) 
    then $NMU_DLX_CLOSE (.DB[SB_CIRCUIT_HANDLE]);

%if $TOPS20 or $TOPS10
%then
    !
    ! If circuit state is ON then initialize the circuit
    !

    if (.DB [SB_USAGE] eql DLX_LOAD) and
       (ch$rchar (.DB[SB_RESPONSE_PTR]) eql NICE$_SUC)
    then begin

         local
             CIRCUIT_STATE;

         if not NML$GET_VDB_PARAMETER (ENTITY_CIRCUIT,
                                       .DB [SB_SERVICE_CIRCUIT],
                                       CPARM_STATE,
                                       CIRCUIT_STATE)
         then CIRCUIT_STATE = CIRCUIT_ON;

         if .CIRCUIT_STATE eql CIRCUIT_ON
         then return $NMU_DLX_START_PROTOCOL (.DB[SB_CIRCUIT_HANDLE],
                                              .DB[SB_RESPONSE_PTR]);

         end;
%fi

    $true
    end;				!End of CIRCUIT_CLOSE
%routine ('CIRCUIT_PROMPT', DB : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine prompts for a message retransmission over the circuit
!        by sending a truncated MOP_MODE_RUNNING message.
!
! Formal parameters
!
!    .DB      Address of the service data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    Prompted successfully
!    $false   Error in transmission - response set
!
! Side effects: none
!
!--

    begin
    $NMU_DLX_WRITE (.DB[SB_CIRCUIT_HANDLE], DLX_OTHER,
                    ch$ptr (uplit (%char(MOP_MODE_RUNNING)),, 8), 1,
                    .DB [SB_RESPONSE_PTR])
    end;				!End of CIRCUIT_PROMPT
%routine ('LOAD_NODE', LOAD_DATA : ref SERVICE_DATA_BLOCK) : NML_LKG_DB novalue =

!++
! Functional description:
!
!	This routine is called after the NICE request message
!	has been decomposed.  It performs the actual load
!	sequence by transferring MOP messages across the specified
!	circuit with the target node.
!
! Formal parameters:
!
!	.LOAD_DATA	Address of load data block
!
!		Necessary parameters (must be specified in request or on VDB):
!
!		Parameter	Name
!		---------	----
!
!			(one of):
!
!		121		Secondary Loader ID
!		122		Tertiary Loader ID
!		120		Load File ID
!               123             Diagnostic File ID
!              2511             Console Secondary Loader ID
!              2512             Console Load File ID
!
!		Parameters that may be defaulted:
!
!		Parameter	Name		Default Value
!		---------	----		-------------
!
!		125		Software Type	SECONDARY_LOADER
!
! Implicit inputs: none
!
! Routine value: none
! Side effects: none
!
!--

    begin

    local
        RETURN_CODE,
        CURRENT_LOAD;

!
! Setup the initial load file type. Default to secondary loader.
!

    begin
    local
         SOFTWARE_TYPE;
    bind
         PARAMETER_TO_GET = uplit (121,122,120,2511,2512,123): vector;
    local
         WHERE_TO_PUT_IT: vector[6];

    WHERE_TO_PUT_IT[0] = LOAD_DATA[LB_SECONDARY_LOADER];
    WHERE_TO_PUT_IT[1] = LOAD_DATA[LB_TERTIARY_LOADER];
    WHERE_TO_PUT_IT[2] = LOAD_DATA[LB_LOAD_FILE];
    WHERE_TO_PUT_IT[3] = LOAD_DATA[LB_CONSOLE_SECONDARY_LOADER];
    WHERE_TO_PUT_IT[4] = LOAD_DATA[LB_CONSOLE_LOAD_FILE];
    WHERE_TO_PUT_IT[5] = LOAD_DATA[LB_DIAGNOSTIC_FILE];

    ! Assume succesful load. All failures will set up error responses
    $RESPONSE (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_SUC);

    ! We don't do trigger after load failure anymore.
    ! Make sure the flag is turned off.
    LOAD_DATA [SB_EXECUTE_TRIGGER] = $false;

!
! If any important parameters haven't been specified in the request,
! try to obtain them from the data base.
!

    incr I from 0 to 5 do
        if ..WHERE_TO_PUT_IT[.I] eql 0
        then
            GET_VDB (ENTITY_NODE,
                     .LOAD_DATA [SB_NODE_DATA],
                     .PARAMETER_TO_GET[.I],
                     .WHERE_TO_PUT_IT[.I]);

    SOFTWARE_TYPE = .LOAD_DATA [LB_SOFTWARE_TYPE];
    if .SOFTWARE_TYPE eql -1
    then
        if not GET_VDB (ENTITY_NODE,
                        .LOAD_DATA [SB_NODE_DATA],
                        125,
                        SOFTWARE_TYPE)
        then
            SOFTWARE_TYPE = SECONDARY_LOADER;


    CURRENT_LOAD = .SOFTWARE_TYPE;
    end;

!
! Load until one of the load routines indicates the loading
! has stopped. CURRENT_LOAD must never be -1 upon entry to this
! section of the code.
!

    RETURN_CODE = -1;

    while $true do
	begin
        local RESULT;

        !
        ! Convert from a MOP program type to a internal
        ! file type code
        !

        if not
           begin
           case .CURRENT_LOAD from -1 to 3 of
               set
               [-1] : ! Leave CURRENT_LOAD as is, allow loop on $NMU_DLX_READ
                   $true;
               [SECONDARY_LOADER] :
               if .LOAD_DATA[LB_PROCESSOR] eql COMMUNICATION_PROCESSOR
               then if .LOAD_DATA[LB_CONSOLE_SECONDARY_LOADER] neq 0
                    then
                        SECONDARY_LOAD (.LOAD_DATA, FILE_SECONDARY_LOADER,
                                        .LOAD_DATA [LB_CONSOLE_SECONDARY_LOADER],
                                        LOAD_)
                    else (RETURN_CODE = .CURRENT_LOAD; $false)
               else if .LOAD_DATA[LB_SECONDARY_LOADER] neq 0
                    then
                        SECONDARY_LOAD (.LOAD_DATA, FILE_SECONDARY_LOADER,
                                        .LOAD_DATA [LB_SECONDARY_LOADER],
                                        LOAD_)
                    else
                       (RETURN_CODE = .CURRENT_LOAD; $false);

               [TERTIARY_LOADER] :
               if .LOAD_DATA[LB_PROCESSOR] eql COMMUNICATION_PROCESSOR
               then begin
                    $RESPONSE_X (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_MPE, 0,
                                 'SOFTWARE TYPE parameter invalid', 105);
                    $false
                    end
               else if .LOAD_DATA[LB_TERTIARY_LOADER] neq 0
                    then
                        IMAGE_LOAD (.LOAD_DATA, FILE_TERTIARY_LOADER,
                                    .LOAD_DATA [LB_TERTIARY_LOADER])
                    else
                        (RETURN_CODE = .CURRENT_LOAD; $false);

               [SYSTEM_IMAGE] :
               if .LOAD_DATA[LB_PROCESSOR] eql COMMUNICATION_PROCESSOR
               then if .LOAD_DATA[LB_CONSOLE_LOAD_FILE] neq 0
                    then
                        CONSOLE_IMAGE_LOAD (.LOAD_DATA, FILE_SYSTEM_IMAGE,
                                            .LOAD_DATA [LB_CONSOLE_LOAD_FILE])
                    else (RETURN_CODE = .CURRENT_LOAD; $false)
                else
                     if .LOAD_DATA [LB_LOAD_MAINT_IMAGE]
                     then
                         if .LOAD_DATA [LB_DIAGNOSTIC_FILE] neq 0
                         then
                             IMAGE_LOAD (.LOAD_DATA,
                                         FILE_SYSTEM_IMAGE,
                                         .LOAD_DATA [LB_DIAGNOSTIC_FILE])
                         else (RETURN_CODE = .CURRENT_LOAD; $false)
                     else 
                          if .LOAD_DATA[LB_LOAD_FILE] neq 0
                          then
                              IMAGE_LOAD (.LOAD_DATA, FILE_SYSTEM_IMAGE,
                                          .LOAD_DATA [LB_LOAD_FILE])
                          else (RETURN_CODE = .CURRENT_LOAD; $false);

               [inrange, outrange] :
                   begin
                   $RESPONSE_X (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_MPE, 0,
                                'SOFTWARE TYPE parameter invalid', 105);
                   $false
                   end;
               tes
           end
        then
            exitloop;

        !
        ! If the above load succeeded, the subroutine will have read the
        ! response from the load and data will be available in the MOP
        ! buffer.
        !
        ! Get the function code from the response message.
        ! If it is a request program load .. go load the appropriate
        ! image.  If it is a request memory load .. it was a successful
        ! load.  If anything else .. who knows what happened (assume it
        ! has died).
        !

        begin
        local
             BUFFER_PTR,
             MOP_FUNCTION,
             MOP_ERROR;

	BUFFER_PTR = .LOAD_DATA [SB_BUFFER_PTR];
	MOP_FUNCTION = GETB (BUFFER_PTR);
        MOP_ERROR =$false;

	selectone .MOP_FUNCTION of
	    set
	    [REQUEST_PROGRAM] :
                if (.CURRENT_LOAD eql SECONDARY_LOADER) or
                   (.CURRENT_LOAD eql TERTIARY_LOADER)
		then begin
                     BUFFER_PTR = ch$plus (.BUFFER_PTR, 2);
                     CURRENT_LOAD = GETB (BUFFER_PTR);
                     !
                     ! We only generate events if the circuit is AUTO_LOADING.
                     !
                     if .LOAD_DATA [SB_SUBSTATE] eql CIRCUIT_AUTOLOADING
                     then begin
                          !
                          ! Generate a 0.3 event to show successful load
                          !
                          NML$DLW_SERVICE_EVENT (LOAD_,
                                                 .LOAD_DATA [SB_HARDWARE_ADDRESS],
                                                 .CURRENT_LOAD,
                                                 .LOAD_DATA [SB_SERVICE_CIRCUIT],
                                                 .LOAD_DATA [SB_RESPONSE_PTR]);
                          !
                          ! Generate a 0.3 event that says
                          ! we started a new load.
                          !
                          NML$DLW_SERVICE_EVENT (LOAD_,
                                            .LOAD_DATA [SB_HARDWARE_ADDRESS],
                                            .CURRENT_LOAD,
                                            .LOAD_DATA [SB_SERVICE_CIRCUIT],
                                            0);
                          end;
                     end
                else MOP_ERROR = $true;
	    [REQUEST_MEMORY_LOAD] :
                if .CURRENT_LOAD eql SYSTEM_IMAGE
                then begin
                     $RESPONSE (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_SUC);
                     LOAD_DATA [SB_EXECUTE_TRIGGER] = $false;
                     exitloop;
                     end
                else MOP_ERROR = $true;
            [CHK11_ASCII_TEXT] :
                if .LOAD_DATA [SB_SRVC_CKT_DEVICE] neq DEVTYP_NI
                    then CURRENT_LOAD = -1
                else MOP_ERROR = $true;
	    [otherwise] :
                MOP_ERROR = $true;
	    tes;

            if .MOP_ERROR then
            begin
                 $RESPONSE_X (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                              'Unexpected MOP response, MOP function code = %D', .MOP_FUNCTION, .MOP_FUNCTION);
%(
  'Unexpected MOP response, expecting program request', 106);
)%
                exitloop;
	    end;


        end;
	end;

!
! If we fell out due to a request for a software type we don't have
! a parameter for, record an error.
!

    if .RETURN_CODE geq 0 then
       $RESPONSE_X (.LOAD_DATA[SB_RESPONSE_PTR], NICE$_IPV,
           (case .RETURN_CODE from SECONDARY_LOADER to SYSTEM_IMAGE of
               set
               [SECONDARY_LOADER]:     121;
               [TERTIARY_LOADER]:      122;
               [SYSTEM_IMAGE]:         120;
           tes), 'File not specified');

    end;
%routine ('SECONDARY_LOAD', LOAD_DATA : ref SERVICE_DATA_BLOCK, FILE_TYPE, FILE, MOP_FUNCTION) : NML_LKG_DB =

!++
! Functional description:
!
!	This routine sends a SECONDARY LOADER to the target node.
!	The whole loader is sent in one MEMORY LOAD WITH TRANSFER ADDRESS
!	MOP message.
!
! Formal parameters
!
!	.LOAD_DATA	Address of load data block
!       .FILE_TYPE      Type of file for error response
!       .FILE           Pointer to ASCIZ file name
!       MOP_FUNCTION    LOAD_ or DUMP_
!
! Implicit inputs: none
!
! Routine value:
!
!	$true	secondary boot was transmitted
!	$false	failure occured while sending boot
!
! Side effects: none
!
!--

    begin
    macro
         ABORT (FB) =
             begin
             FILE_CLOSE (FB);
             return $false
             end %;

    local
        FB : FILE_DATA_BLOCK,
        RESULT;

    FB[FB_RESPONSE_PTR] = .LOAD_DATA [SB_RESPONSE_PTR];
    FB[FB_FILE_TYPE] = .FILE_TYPE;
    FB[FB_FILE] = .FILE;

    if .LOAD_DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_DTE
    then if not CPU_IS_PDP11 (.LOAD_DATA) then return $false;

!
! Open the secondary loader file.
!

    if not FILE_OPEN (FB) then return $false;

!
! Read the image base address, size of image and transfer address.
!

    if not FILE_READ_LABEL (FB) then ABORT (FB);

    if .LOAD_DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
    then
    (if %cmpi32 (FB[FB_BYTES_LEFT], gtr, 1490)
     then
     begin
     $RESPONSE_X (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_IFC, .FB[FB_FILE_TYPE],
                  'Secondary image is too large, >1490 bytes', 110);
     ABORT (FB);
     end)
    else
    if %cmpi32 (FB[FB_BYTES_LEFT], gtr, 512)
    then
        begin
        $RESPONSE_X (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_IFC, .FB[FB_FILE_TYPE],
                   'Secondary image is too large, >512 bytes', 110);
        ABORT (FB);
        end;

    begin
    local
        BUFFER_PTR;

!
! Make a pointer to the buffer used to construct MOP messages.
!
    BUFFER_PTR = .LOAD_DATA [SB_BUFFER_PTR];
!
! Put in the header information for a "load memory with transfer address"
! Function, load number 0, load address
!
    PUTB (LOAD_WITH_TRANSFER, BUFFER_PTR);
    PUTB (0, BUFFER_PTR);
    PUT32 (FB[FB_CURRENT_ADDRESS], BUFFER_PTR);

!
! Put in the secondary image
!
    begin
    local
         LENGTH;

    %movf32 (FB[FB_BYTES_LEFT], LENGTH);

    if 0 geq (LENGTH = NMU$FILE_READ (.FB[FB_FILE_HANDLE], .BUFFER_PTR,
                                      .LENGTH, .FB[FB_RESPONSE_PTR]))
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        ABORT (FB);
        end;

    BUFFER_PTR = ch$plus (.BUFFER_PTR, .LENGTH);
    end;
!
! Put in the transfer address of the secondary boot
!

    PUT32 (FB[FB_TRANSFER_ADDRESS], BUFFER_PTR);

!
! Send the boot to the target node.
!

    BUFFER_PTR = ch$diff (.BUFFER_PTR, .LOAD_DATA [SB_BUFFER_PTR]);

    if not $NMU_DLX_WRITE (.LOAD_DATA [SB_CIRCUIT_HANDLE], DLX_SECONDARY,
                            .LOAD_DATA [SB_BUFFER_PTR], .BUFFER_PTR,
                            .LOAD_DATA [SB_RESPONSE_PTR])
    then ABORT (FB);

    !
    ! Read the response from the load.
    !

    if .MOP_FUNCTION eql LOAD_
    then begin

         RESULT = (if .LOAD_DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
                   then
                   (NML$DLW_READ_LDAQ (.LOAD_DATA [SB_CIRCUIT_HANDLE],
                                       DLX_DATA, .LOAD_DATA [SB_BUFFER_PTR],
                                       MOP_BUFFER_LENGTH,
                                       .LOAD_DATA [SB_RESPONSE_PTR],
                                       .LOAD_DATA [SB_HARDWARE_ADDRESS]))
                   else
                   $NMU_DLX_READ (.LOAD_DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                  .LOAD_DATA [SB_BUFFER_PTR],
                                  MOP_BUFFER_LENGTH,,
                                  .LOAD_DATA [SB_RESPONSE_PTR]));

         if .RESULT lss 0
         then begin
              $RESPONSE (.LOAD_DATA [SB_RESPONSE_PTR], NICE$_LCE);
              ABORT (FB);
              end;

         end;

    end;
!
! Return indicating that the load has been sent.
!
    FILE_CLOSE (FB);
    $true
    end;
%routine ('IMAGE_LOAD', DATA : ref SERVICE_DATA_BLOCK, FILE_TYPE, FILE) : NML_LKG_DB =

!++
! Functional description:
!
!	This routine is called by the LOAD_NODE routine to perform
!	a core image transfer TO a target node.  The images that
!	this routine sends are for either a TERTIARY LOADER or
!	SYSTEM IMAGE.
!
! Formal parameters
!
!	.DATA           Address of load data block
!       .FILE_TYPE      Type of file for error response
!       .FILE           Pointer to ASCIZ file name
!
! Implicit inputs: none
!
! Routine value:
!
!	$true	image was sent to node successfully
!	$false	failure while sending image
!
! Side effects: none
!
!--

    begin
    macro
         ABORT (FB) =
             begin
             FILE_CLOSE (FB);
             return $false
             end %;

    local
         FB : FILE_DATA_BLOCK,
         TARGET_NAME,
         TARGET_NUMBER,
         JOB_NUMBER;
!
! Get the needed information for the load
!
    FB[FB_RESPONSE_PTR] = .DATA [SB_RESPONSE_PTR];
    FB[FB_FILE_TYPE] = .FILE_TYPE;
    FB[FB_FILE] = .FILE;

    if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_DTE
    then if not CPU_IS_PDP11 (.DATA) then return $false;

    begin
    local TEMP;

    if .DATA[SB_NODE_VERSION] eql -1         ! Get SERVICE NODE VERSION if
    then                                     ! we don't have one already
        if GET_VDB (ENTITY_NODE, .DATA[SB_NODE_DATA], 115, TEMP)
        then DATA[SB_NODE_VERSION] = .TEMP
        else DATA[SB_NODE_VERSION] = 1;     ! Default to 1 (PHASE IV)
    
    end;

    if (TARGET_NUMBER = .DATA [LB_ADDRESS]) eql 0
    then
        TARGET_NUMBER =  .DATA [SB_NODE_DATA];

    TARGET_NAME =
           (if (TARGET_NAME = .DATA [LB_NAME]) neq 0
           then ch$plus (.TARGET_NAME,2)
           else if .DATA [SB_NODE_DATA] neq 0
                then ch$plus (.DATA [SB_NODE_DATA],2)
                else 0);

    begin
    local PTR, TEMP;

    PTR = ch$ptr (DATA [LB_HOST],, 8);
    if (ch$rchar_a (PTR) eqlu 0) and
       (ch$rchar_a (PTR) eqlu 0) and
       (ch$rchar_a (PTR) eqlu 0)
    then
        if GET_VDB (ENTITY_NODE, .DATA [SB_NODE_DATA], 140, TEMP)
        then
            begin
            PTR = .TEMP;                ! (for optimization)
            ch$move (ch$rchar (ch$plus (.PTR, 2)) + 3, .PTR, ch$ptr (DATA [LB_HOST],, 8));
            end;

    TEMP = NODE_ID_BUFFER_LENGTH;
    $NML$MAP_NODE_ID (TEMP, ch$ptr (DATA [LB_HOST],, 8));

    PTR = ch$ptr (DATA [LB_HOST],, 8);
    if (ch$rchar_a (PTR) eqlu 0) and
       (ch$rchar_a (PTR) eqlu 0)
    then
        begin
        PTR = NMU$NETWORK_LOCAL();
        ch$move (ch$rchar (ch$plus (.PTR, 2)) + 3, .PTR, ch$ptr (DATA [LB_HOST],, 8));
        end;

    end;

!
! Open the image file.
!

    if not FILE_OPEN (FB) then return $false;

!
! Read the image base address, size of image and transfer address.
! If anything looks wrong .. make the load fail.
!

    if not FILE_READ_LABEL (FB) then ABORT (FB);
!
! If we need a RPM before loading, try to get it now.
! First send an ASSISTANCE VOLUNTEER

    if .DATA [SB_NEED_RPM] then
       if not (WRITE_ASSISTANCE_VOLUNTEER (.DATA)) then ABORT (FB);
!
! Do the load
!

    begin
    local
        LOAD_NUMBER;

!
! Send the image file to the target system.
! If no failure occured then start the program just
! loaded.
!

    begin
    local
         MOP_MSG_LEN,
         LENGTH;

    if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
    then MOP_MSG_LEN = NI_MOP_LOAD_DATA_LENGTH
    else MOP_MSG_LEN = DTE_MOP_LOAD_DATA_LENGTH;

!
! Start at the beginning of the load.
!
    LOAD_NUMBER = 0;
    LENGTH = 0;
!
! Loop reading from the image file and passing it on
! to the target system.
!
    until %cmpi32 (FB[FB_BYTES_LEFT], leq, 0) do
	begin

!
! Read in the next load message if the load number has
! been incremented.
!
	if .LENGTH eql 0
	then
	    begin
            local
                 BUFFER_PTR;

            BUFFER_PTR = .DATA [SB_BUFFER_PTR];
            PUTB (LOAD_WITHOUT_TRANSFER, BUFFER_PTR);
            PUTB (.LOAD_NUMBER, BUFFER_PTR);
            PUT32 (FB[FB_CURRENT_ADDRESS], BUFFER_PTR);
            LENGTH = .MOP_MSG_LEN;

            if %cmpi32 (FB[FB_BYTES_LEFT], leq, .LENGTH)
            then %movf32 (FB[FB_BYTES_LEFT], LENGTH);

	    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .BUFFER_PTR,
                                    .LENGTH, .FB[FB_RESPONSE_PTR])
	    then begin
                 $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                              'Could not read image file', 119);
                 ABORT (FB);
                 end;

            %addi32 (.LENGTH, FB[FB_CURRENT_ADDRESS]);
            LENGTH = .LENGTH + 6;
	    end;

        %(do a $NMU_DLX_WRITE_READ)%
!
! Write the load memory message to the target node, and read the
! acknowledgement.
!
        begin
             local RESULT;

             RESULT = WRITE_MOP_MESSAGE (.DATA, .LENGTH, .LOAD_NUMBER);

             if (.RESULT lss 0) then ABORT (FB);
             if .RESULT neq REQUEST_MEMORY_LOAD
             then begin
                  $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                               'Invalid MOP response to MEMORY LOAD',
                               109);
                  ABORT (FB);
                  end;
        end;

        LOAD_NUMBER = (.LOAD_NUMBER + 1) and %o'377';
        %subi32 ((.LENGTH-6), FB[FB_BYTES_LEFT]);
        LENGTH  = 0;

	end;

    end;
    FILE_CLOSE (FB);
!
! Okay, image loaded. Now give him the parameters and start him up.
!
    begin
    local
         BUFFER_PTR, LENGTH;

    BUFFER_PTR = .DATA [SB_BUFFER_PTR];


    if .FILE_TYPE eql FILE_TERTIARY_LOADER
    then
        begin
             local LOAD_ADDRESS;
             LOAD_ADDRESS = 0;
             !
             ! Put "memory load with transfer" MOP message code
             ! and the load number into MOP buffer.
             !
             PUTB (LOAD_WITH_TRANSFER, BUFFER_PTR);

             PUTB (.LOAD_NUMBER, BUFFER_PTR);
             PUT32 (LOAD_ADDRESS, BUFFER_PTR);                       ! Null Load address
             PUT32 (FB[FB_TRANSFER_ADDRESS], BUFFER_PTR);
        end

    else
        begin
             !
             ! Put "parameters with transfer" MOP message code
             ! and the load number into MOP buffer.
             !
             PUTB (PARAMETERS_WITH_TRANSFER, BUFFER_PTR);

             PUTB (.LOAD_NUMBER, BUFFER_PTR);

             ! Put target node number, if we have any
             if .TARGET_NUMBER neq 0
             then begin

                  PUTB (2, BUFFER_PTR); ! Target node number
                  PUTB (2, BUFFER_PTR);
                  PUTB (GETB (TARGET_NUMBER), BUFFER_PTR);

                  ! Check SERVICE NODE VERSION
                  if .DATA[SB_NODE_VERSION] eql 1
                  then PUTB (GETB (TARGET_NUMBER), BUFFER_PTR) ! OK if PHASE IV
                  else begin
                       local TEMP;      ! Not PHASE IV....
                       TEMP = GETB (TARGET_NUMBER); ! Get high byte
                       TEMP = .TEMP and %B'00000011';  ! Only want the low two
                       PUTB (.TEMP, BUFFER_PTR); ! Put it in sans Area Number
                       end;
                  end;

             ! Now put in node name, if any
             if .TARGET_NAME neq 0
             then if (LENGTH = GETB (TARGET_NAME)) neq 0
                  then begin
                       PUTB (1, BUFFER_PTR);       ! Target node name
                       PUTB (.LENGTH, BUFFER_PTR);
                       BUFFER_PTR = ch$move (.LENGTH,
                                             .TARGET_NAME,
                                             .BUFFER_PTR);
                       end;


             ! There should always be a host name
             if (LENGTH = ch$rchar (ch$ptr (DATA [LB_HOST], 2, 8))) neq 0
             then begin
                  PUTB (3, BUFFER_PTR);       ! Host node name
                  PUTB (.LENGTH, BUFFER_PTR);
                  BUFFER_PTR = ch$move (.LENGTH,
                                        ch$ptr (DATA [LB_HOST], 3, 8),
                                        .BUFFER_PTR);
                  end;

             ! There should also always be a host number
             PUTB (4, BUFFER_PTR);           ! Host node number
             PUTB (2, BUFFER_PTR);
             PUTB (ch$rchar (ch$ptr (DATA [LB_HOST], 0, 8)), BUFFER_PTR);

             if .DATA[SB_NODE_VERSION] eql 1     ! Check SERVICE NODE VERSION
             then PUTB (ch$rchar (ch$ptr (DATA [LB_HOST], 1, 8)), BUFFER_PTR)
             else begin
                  local TEMP;                           ! Not PHASE IV....
                  TEMP = ch$rchar (ch$ptr (DATA [LB_HOST], 1, 8));    ! Get high byte of Host Number
                  TEMP = .TEMP and %B'00000011';  ! Only want the low two bits
                  PUTB (.TEMP, BUFFER_PTR);       ! Put it in sans Area Number
                  end;

             ! Put in END MARK
             PUTB (0, BUFFER_PTR);           ! End of parameters

             ! And then transfer address
             PUT32 (FB[FB_TRANSFER_ADDRESS], BUFFER_PTR);

    end;

    BUFFER_PTR = ch$diff (.BUFFER_PTR, .DATA [SB_BUFFER_PTR]);

!
! Write the message to the target node, and read the
! acknowledgement.
!
    begin
    local RESULT;

    RESULT = WRITE_MOP_MESSAGE (.DATA, .BUFFER_PTR, .LOAD_NUMBER);

    if .RESULT lss 0 then return $false;
    if (.RESULT eql REQUEST_MEMORY_LOAD and .FILE_TYPE neq FILE_SYSTEM_IMAGE)
    then
        begin
        $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                     'Invalid MOP response to MEMORY LOAD WITH TRANFER ADDRESS',
                     109);
        return $false;
        end;

    if (.RESULT eql REQUEST_PROGRAM and .FILE_TYPE neq FILE_TERTIARY_LOADER)
    then
        begin
        $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                     'Invalid MOP response to PARAMETER LOAD WITH TRANFER ADDRESS',
                     109);
        return $false;
        end;

    end;

    end;
    end;
    $true
    end;
%routine ('WRITE_ASSISTANCE_VOLUNTEER', DATA : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =
!++
! Functional description:
!
!        This routine writes an assistance volunteer to the target node and
!        attempts to read an RPM back.
!
! Formal parameters:
!
!        .DATA           Addres of load data block
!
! Implicit inputs: none
!
! Routine value:
!
!        $true     RPM received from target node
!        $false    No RPM received from target. Response code set up.
!
! Side effects: none
!
!--

begin

     local BUFFER_PTR, MOP_FUNCTION, RESULT;

     BUFFER_PTR = .DATA [SB_BUFFER_PTR];
     PUTB (ASSISTANCE_VOLUNTEER, BUFFER_PTR);

     if not ($NMU_DLX_WRITE ( .DATA [SB_CIRCUIT_HANDLE],
                               DLX_DATA,
                               .DATA [SB_BUFFER_PTR],
                               1,
                               .DATA [SB_RESPONSE_PTR]))
     then begin
          $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LCE, 0,
                       'Timed out waiting for confirming RPM');
          return $false;
          end;

     RESULT = NML$DLW_READ_LDAQ (.DATA [SB_CIRCUIT_HANDLE],
                                 DLX_DATA,
                                 .DATA [SB_BUFFER_PTR],
                                 MOP_BUFFER_LENGTH,
                                 .DATA [SB_RESPONSE_PTR],
                                 .DATA [SB_HARDWARE_ADDRESS]);

    if .RESULT lss 0
    then
        begin
        $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_LCE);
        return $false;
        end;

     BUFFER_PTR = .DATA [SB_BUFFER_PTR];
     MOP_FUNCTION = GETB (BUFFER_PTR);

     if .MOP_FUNCTION neq REQUEST_PROGRAM
     then begin
          $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                       'Expected RPM, received = %D', .MOP_FUNCTION);
          return $false;

          end;

     return $true;

end;                                    ! end of WRITE_ASSISTANCE_VOLUNTEER
%routine ('WRITE_MOP_MESSAGE', DATA : ref SERVICE_DATA_BLOCK, LENGTH, LOAD_NUMBER) : NML_LKG_DB =

!++
! Functional description:
!
!       This routine transmits writes a MOP message to the open circuit
!       and waits for a reply. This routine should only be used to send
!       MEMORY_LOAD, MEMORY_LOAD_WITH_TRANSFER_ADDRESS, and
!       PARAMETER_LOAD_WITH_TRANSFER messages. Because this routine
!       re-transmits it should not be called for seconday loaders.
!
! Formal parameters:
!
!        .DATA           Addres of load data block
!        LENGTH          Length of MOP_MESSAGE to write
!        LOAD_NUMBER     Current segment number in multi-segment load
!
! Implicit inputs: none
!
! Routine value:
!
!         -1        Line protocol error(response pointer set up)
!         -2        Line communication error (response pointer set up)
!         >=0       Function of next MOP message
!
! Side effects: none
!
!--

begin
     local
        MOP_RESPONSE;

while $true do
   begin
   local
        RESULT,
        BUFFER_PTR;

   !
   ! Write the load memory message to the target node, and read the
   ! acknowledgement.  Retransmit until the proper message has been
   ! acknowledged.
   !
   decru TIMES from NMU$K_MOP_RETRY_LIMIT to 1 do
         begin
         NMU$SCHED_PAUSE();          ! Give others a chance
         if not $NMU_DLX_WRITE (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                .DATA [SB_BUFFER_PTR], .LENGTH,
                                .DATA [SB_RESPONSE_PTR])
         then exitloop RESULT = -1;

         RESULT = (if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
                   then
                   NML$DLW_READ_LDAQ (.DATA [SB_CIRCUIT_HANDLE],
                                      DLX_DATA,
                                      .DATA [SB_BUFFER_PTR],
                                      MOP_BUFFER_LENGTH,
                                      .DATA [SB_RESPONSE_PTR],
                                      .DATA [SB_HARDWARE_ADDRESS])
                   else
                   $NMU_DLX_READ (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                  .DATA [SB_BUFFER_PTR], MOP_BUFFER_LENGTH,,
                                  .DATA [SB_RESPONSE_PTR]));
                      
         if .RESULT neq -2 then exitloop;
         end;

         if .RESULT lss 0
         then
             begin
             $RESPONSE_X (.DATA[SB_RESPONSE_PTR], NICE$_LCE);
             return -2;
             end;
   !
   ! If a request for the next memory load comes in, then setup
   ! the memory load number.  Otherwise make the load fail.
   !
         BUFFER_PTR = .DATA [SB_BUFFER_PTR];
         MOP_RESPONSE = GETB (BUFFER_PTR);

         selectone .MOP_RESPONSE of
	    set

            [REQUEST_PROGRAM] :
                begin
                     exitloop;
                end;

	    [REQUEST_MEMORY_LOAD] :
                begin
                local
                     LOADNUM;

		LOADNUM = GETB (BUFFER_PTR);

                if .LOADNUM neq .LOAD_NUMBER
                then
                    begin

                    if .LOADNUM neq ((.LOAD_NUMBER+1) and %o'377')
                    then
                        begin
                        $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                                     'Invalid load number requested', 108);
                        return -1;
                        end

                    else exitloop;

                    end;

                end;
	    [otherwise] :
		begin
		$RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                           'Invalid MOP response to MEMORY LOAD', 109);
                return -1;
		end;
	    tes;

   end;

   return .MOP_RESPONSE;

end;
%routine ('CONSOLE_IMAGE_LOAD', DATA : ref SERVICE_DATA_BLOCK, FILE_TYPE, FILE) : NML_LKG_DB =

!++
! Functional description:
!
!	This routine is called by the LOAD_NODE routine to perform
!	a core image transfer TO a target node.  The images that
!	this routine sends are for REMOTE CONSOLE SYSTEM IMAGE.
!
! Formal parameters
!
!	.DATA           Address of load data block
!       .FILE_TYPE      Type of file for error response
!       .FILE           Pointer to ASCIZ file name
!
! Implicit inputs: none
!
! Routine value:
!
!	$true	image was sent to node successfully
!	$false	failure while sending image
!
! Side effects: none
!
!--

    begin
    macro
         ABORT (FB) =
             begin
             FILE_CLOSE (FB);
             return $false
             end %;

    local
         BUFFER_PTR,
         BYTE_BUFFER,
         FB : FILE_DATA_BLOCK,
         LOAD_NUMBER,
         PTR,
         RECORD_LEN,
         TEMP,
         TRANSFER_ADDRESS;

!
! Get the needed information for the load
!
    FB[FB_RESPONSE_PTR] = .DATA [SB_RESPONSE_PTR];
    FB[FB_FILE_TYPE] = .FILE_TYPE;
    FB[FB_FILE] = .FILE;

!
! Open the image file.
!

    if not FILE_OPEN (FB) then return $false;

!
! If we need a RPM before loading, try to get it now.
! First send an ASSISTANCE VOLUNTEER

    if .DATA [SB_NEED_RPM] then
        if not (WRITE_ASSISTANCE_VOLUNTEER (.DATA)) then ABORT (FB);

!
! Begin loading at 0
!

    LOAD_NUMBER = 0;

    while $true do
    begin
         PTR = ch$ptr (BYTE_BUFFER,, 8);

         if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
         then
             begin
             $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                          'Could not read image file', 119);
             ABORT (FB);
             end;

         TEMP = GETW (PTR);

         if .TEMP neq 1
         then begin
              $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                           'Could not read image file', 119);
              ABORT (FB);
              end;

         PTR = ch$ptr (BYTE_BUFFER,, 8);

         if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
         then
             begin
             $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                          'Could not read image file', 119);
             ABORT (FB);
             end;

         RECORD_LEN = GETW (PTR);

         PTR = ch$ptr (BYTE_BUFFER,, 8);

         if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
         then
             begin
             $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                          'Could not read image file', 119);
             ABORT (FB);
             end;

         TRANSFER_ADDRESS = GETW (PTR);

         if .RECORD_LEN eql 6
         then exitloop;


         BUFFER_PTR = .DATA [SB_BUFFER_PTR];
         PUTB (LOAD_WITHOUT_TRANSFER, BUFFER_PTR);
         PUTB (.LOAD_NUMBER, BUFFER_PTR);
         PUT32 (TRANSFER_ADDRESS, BUFFER_PTR);

 !
 ! We have already read 6 bytes out of the record that are included
 ! in the RECORD_LEN field, however, the Length value does not include
 ! the 1 byte checksum at the end of the record. Read that byte here,
 ! but it won't get sent with the MOP message.
 !

         if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .BUFFER_PTR,
                                 (.RECORD_LEN - 5), .FB[FB_RESPONSE_PTR])
         then
             begin
             $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                          'Could not read image file', 119);
             ABORT (FB);
             end;

        %(do a $NMU_DLX_WRITE_READ)%
!
! Write the load memory message to the target node, and read the
! acknowledgement.
!
        begin
             local RESULT;

             RESULT = WRITE_MOP_MESSAGE (.DATA, .RECORD_LEN, .LOAD_NUMBER);

             if (.RESULT lss 0) then ABORT (FB);

             if .RESULT neq REQUEST_MEMORY_LOAD
             then begin
                  $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_LPE, 0,
                               'Invalid MOP response to MEMORY_LOAD',
                               109);
                  ABORT (FB);
                  end;
        end;

        LOAD_NUMBER = (.LOAD_NUMBER + 1) and %o'377';

    end;                                ! while $true

    FILE_CLOSE (FB);

!
! Okay, image loaded. Now give him the parameters and start him up.
!
    begin
    local
         BUFFER_PTR, LENGTH;

         BUFFER_PTR = .DATA [SB_BUFFER_PTR];

         ! Put "parameters with transfer" MOP message code
         ! and the load number into MOP buffer.

         PUTB (PARAMETERS_WITH_TRANSFER, BUFFER_PTR);

         PUTB (.LOAD_NUMBER, BUFFER_PTR);

         PUTB (0, BUFFER_PTR);

         PUT32 (TRANSFER_ADDRESS, BUFFER_PTR);

         begin
              local RESULT;

              RESULT = WRITE_MOP_MESSAGE (.DATA, 7, .LOAD_NUMBER);

              if .RESULT lss 0 then return $false;

              if .RESULT neq REQUEST_MEMORY_LOAD
              then begin
                   $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                                'Invalid MOP response to PARAMETER LOAD WITH TRANFER ADDRESS',
                                109);
                   return $false;
                   end;

         end;

    end;

    $true

    end;
%routine ('DUMP_NODE', DATA : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description:
!
!        This routine is called after the NICE request message has been
!        decomposed.  It performs the actual dump of a target node
!        connected to this (EXECUTOR) node by some circuit.
!
! Formal parameters:
!
!    .DATA    Address of dump data block
!
!             Necessary parameters (must be specified in request or in VDB):
!
!	      Parameter		Name
!	      ---------		----
!
!		130	Dump Image File ID
!		131	Secondary Dumper ID
!
!	      Parameters that may be defaulted:
!
!	      Parameter		Name		Default Value
!	      ---------		----		-------------
!
!		135	Dump Beginning Address		0
!		136	Dump Count			gotten from target
!
! Implicit inputs: none
!
! Routine value: none
! Side effects: none
!
!--

    begin
    label
         DUMP_REQUEST;
    literal
           DUMPER_ASSUMED = 0,			! We hope the target can dump
           DUMPER_NEEDED = DUMPER_ASSUMED + 1,	! We need to load a secondary dumper
           DUMPER_RUNNING = DUMPER_NEEDED + 1,	! Secondary dumper is loaded
           DUMPER_READY = DUMPER_RUNNING + 1,	! Secondary dumper accepting dump requests
           DUMPER_BROKEN = DUMPER_READY + 1;	! Target can't be dumped
    local
         DUMP_COUNT_DEFAULTED,
         DUMPER_STATE,
         FB : FILE_DATA_BLOCK,
         SECONDARY_DUMPER,
         SERVICE_DEVICE;

    FB[FB_RESPONSE_PTR] = .DATA[SB_RESPONSE_PTR];
    FB[FB_FILE_TYPE] = FILE_DUMP_IMAGE;

    if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_DTE
    then if not CPU_IS_PDP11 (.DATA) then return $false;

    if (SERVICE_DEVICE = .DATA [LB_SERVICE_DEVICE]) lss 0
    then
        if not GET_VDB (ENTITY_NODE,
                        .DATA [SB_NODE_DATA],
                        112,
                        SERVICE_DEVICE)
        then
            SERVICE_DEVICE = -1;

    if (FB[FB_FILE] = .DATA [DB_DUMP_FILE]) eql 0
    then
        if not GET_VDB (ENTITY_NODE,
                        .DATA [SB_NODE_DATA],
                        130,
                        FB[FB_FILE])
        then
	    begin
	    $RESPONSE_X (.DATA[SB_RESPONSE_PTR], NICE$_PAM, 130);
	    return $false
	    end;

    if (SECONDARY_DUMPER = .DATA [DB_SECONDARY_DUMPER]) eql 0
    then
        if not GET_VDB (ENTITY_NODE,
                        .DATA [SB_NODE_DATA],
                        131,
                        SECONDARY_DUMPER)
        then
            SECONDARY_DUMPER = 0;

!
! Set dump limits.
!

    begin
    local
         PTR;
    own NEG1: THIRTY_TWO_BIT;
    %movi32 (0, NEG1);
    %subi32 (1, NEG1);

    %mov32 (DATA[DB_DUMP_ADDRESS], FB[FB_CURRENT_ADDRESS]);

    if %cmp32 (DATA[DB_DUMP_ADDRESS], eql, NEG1)
    then
        if not GET_VDB (ENTITY_NODE,
                        .DATA [SB_NODE_DATA],
                        135,
                        FB[FB_CURRENT_ADDRESS])
        then %movi32 (0, FB[FB_CURRENT_ADDRESS]);

    DUMP_COUNT_DEFAULTED = $true;

    %mov32 (DATA[DB_DUMP_COUNT], FB[FB_BYTES_LEFT]);

    if %cmp32 (DATA[DB_DUMP_COUNT], eql, NEG1)
    then
        if GET_VDB (ENTITY_NODE,
                    .DATA [SB_NODE_DATA],
                    136,
                    FB[FB_BYTES_LEFT])
        then
            DUMP_COUNT_DEFAULTED = $false
        else
            begin
            %movi32 (%o'76000', FB[FB_BYTES_LEFT]);
            %asli32 (3, FB[FB_BYTES_LEFT]);
            DUMP_COUNT_DEFAULTED = $true;
            end
    else
        DUMP_COUNT_DEFAULTED = $false;

    end;

!
! Open the file if we can.....
!

    if not FILE_OPEN (FB) then return $false;

!
! If we need a RPM before dumping, try to get it now.
! First send an ASSISTANCE VOLUNTEER
    if .DATA [SB_NEED_RPM]
    then begin
         
         local BUFFER_PTR, MOP_FUNCTION;

         BUFFER_PTR = .DATA [SB_BUFFER_PTR];
         PUTB (ASSISTANCE_VOLUNTEER, BUFFER_PTR);
         $NMU_DLX_WRITE ( .DATA [SB_CIRCUIT_HANDLE],
                          DLX_DATA,
                          .DATA [SB_BUFFER_PTR],
                          1,
                          .DATA [SB_RESPONSE_PTR]);

         if NML$DLW_READ_LDAQ (.DATA [SB_CIRCUIT_HANDLE],
                              DLX_DATA,
                              .DATA [SB_BUFFER_PTR],
                              MOP_BUFFER_LENGTH,
                              .DATA [SB_RESPONSE_PTR],
                              .DATA [SB_HARDWARE_ADDRESS]) gtr 0
         then begin

              BUFFER_PTR = .DATA [SB_BUFFER_PTR];
              MOP_FUNCTION = GETB (BUFFER_PTR);

              if .MOP_FUNCTION neq REQUEST_DUMP_SERVICE
              then begin
                   $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                                'Expected RPM, received = %D', .MOP_FUNCTION);

                   return $false;
                   end;
              end
         else begin
              $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                           'Timed out waiting for confirming RPM');
              return $false;
              end;
         end;

!
! Until all bytes have been dumped
!    Send a dump request for the next block of memory
!    Receive the dump data
!    Validate the dump data response
!    Put data into dump image file
!
!

    $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_SUC);

    DUMPER_STATE = DUMPER_ASSUMED;

    while %cmpi32 (FB[FB_BYTES_LEFT], gtr, 0)
    do
      case .DUMPER_STATE from DUMPER_ASSUMED to DUMPER_READY of
          set
          !
          ! If we need to load the secondary dumper and there is one,
          ! then do so.  Otherwise give up.
          !
          [DUMPER_NEEDED] :

	      if .SECONDARY_DUMPER eqla 0
	      then
		  begin

                  ! If NI device, then we don't need a secondary loader
                  ! so just proceed to DUMPER_RUNNING state
                  if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
                  then DUMPER_STATE = .DUMPER_STATE + 1
                  else begin

                       $RESPONSE_X (.DATA[SB_RESPONSE_PTR], NICE$_PAM, 131);
                       exitloop;
                       end;
                  end
              else 
                   if SECONDARY_LOAD (.DATA,
                                      FILE_SECONDARY_DUMPER,
                                      .SECONDARY_DUMPER,
                                      DUMP_)
                   then begin
                        $RESPONSE (.DATA[SB_RESPONSE_PTR], NICE$_SUC);
                        DUMPER_STATE = .DUMPER_STATE + 1;
                        end
                   else exitloop;

          !
          ! Construct and send the REQUEST DUMP message
          !
          [DUMPER_ASSUMED, DUMPER_RUNNING, DUMPER_READY] :
              DUMP_REQUEST:begin
              local
                   BUF_PTR,
                   DUMP_LENGTH,
                   MOP_MSG_LEN,
                   RESULT;

              if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
              then MOP_MSG_LEN = NI_MOP_LOAD_DATA_LENGTH
              else MOP_MSG_LEN = DTE_MOP_LOAD_DATA_LENGTH;
      
              BUF_PTR = .DATA [SB_BUFFER_PTR];
              PUTB (REQUEST_DUMP, BUF_PTR);
              PUT32 (FB[FB_CURRENT_ADDRESS], BUF_PTR);

              %mov32 (FB[FB_BYTES_LEFT], DUMP_LENGTH);

              if %cmpi32 (DUMP_LENGTH, gtr, .MOP_MSG_LEN)
              then %movi32 (.MOP_MSG_LEN, DUMP_LENGTH);

              PUTW (DUMP_LENGTH, BUF_PTR);

              %(do a $NMU_DLX_WRITE_READ)%

              decru TIMES from NMU$K_MOP_RETRY_LIMIT to 1 do begin
                  NMU$SCHED_PAUSE();    ! Let the others in first
                  if .DUMPER_STATE neq DUMPER_RUNNING
                  then
                      begin
                      if not $NMU_DLX_WRITE (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                      .DATA [SB_BUFFER_PTR], 7,
                                      .DATA [SB_RESPONSE_PTR])

                      then exitloop RESULT = -1;
                      end
                  else
                      DUMPER_STATE = .DUMPER_STATE + 1;

                  RESULT = (if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
                            then
                            NML$DLW_READ_LDAQ (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                               .DATA [SB_BUFFER_PTR], MOP_BUFFER_LENGTH,
                                               .DATA [SB_RESPONSE_PTR],
                                               .DATA [SB_HARDWARE_ADDRESS])
                            else
                            $NMU_DLX_READ (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                                           .DATA [SB_BUFFER_PTR], MOP_BUFFER_LENGTH,,
                                           .DATA [SB_RESPONSE_PTR]));

                  if .RESULT neq -2 then exitloop;
                  end;

              if .RESULT leq 0
              then
                  leave DUMP_REQUEST with DUMPER_STATE = .DUMPER_STATE + 1;

              begin
              local
                  BUF_PTR;

              BUF_PTR = .DATA [SB_BUFFER_PTR];

              selectone GETB (BUF_PTR) of
                  set

! MOP_MODE_RUNNING and REQUEST_DUMP_SERVICE are the same MOP function
!                  [MOP_MODE_RUNNING] :
                  [REQUEST_DUMP_SERVICE] :
                      if .RESULT geq 8
                      then
                          begin
                          local
                              MEMZISE : THIRTY_TWO_BIT,
                              TEMP;
                          bind
                              DEVTYPE = TEMP,
                              MOPVER = TEMP,
                              FEATURES = TEMP;

                          DEVTYPE = GETB (BUF_PTR);

                          if (.SERVICE_DEVICE geq 0) and
                             (.SERVICE_DEVICE neq .DEVTYPE)
                          then
                              begin
                              $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_LPE);
                              exitloop;
                              end;

                          if GETB (BUF_PTR) neq 1
                          then
                              begin
                              $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_LPE);
                              exitloop;
                              end;

                          GET32 (BUF_PTR, MEMZISE);
                          FEATURES = GETB (BUF_PTR);

                          if not .FEATURES <1, 1>
                          then
                              leave DUMP_REQUEST with DUMPER_STATE = .DUMPER_STATE + 1;

                          if .DUMP_COUNT_DEFAULTED
                          then
                              begin
                              %mov32 (MEMZISE, FB[FB_BYTES_LEFT]);
                              %sub32 (FB[FB_CURRENT_ADDRESS], FB[FB_BYTES_LEFT]);
                              end;

                          end;
                  [MEMORY_DUMP_DATA] :
                      begin
                      local
                           DUMPED_ADDRESS : THIRTY_TWO_BIT;

                      RESULT = .RESULT - 5;

                      if .RESULT lss 0 then exitloop;

                      !
                      ! Check for the correct dump address
                      ! Adjust dump address for next dump request
                      !
                      GET32 (BUF_PTR, DUMPED_ADDRESS);

                      if %cmp32 (DUMPED_ADDRESS, neq, FB[FB_CURRENT_ADDRESS])
                      then
                          begin
                          $RESPONSE_X (.DATA [SB_RESPONSE_PTR], NICE$_LPE, 0,
                                       'Address received in MEMORY DUMP DATA is incorrect', 117);
                          exitloop;
                          end;

                      %addi32 (.RESULT, FB[FB_CURRENT_ADDRESS]);
                      %subi32 (.RESULT, FB[FB_BYTES_LEFT]);

                      !
                      ! Write data to image file
                      !
                      if not NMU$FILE_WRITE (.FB[FB_FILE_HANDLE], .BUF_PTR, .RESULT, .FB[FB_RESPONSE_PTR])
                      then leave DUMP_REQUEST with DUMPER_STATE = .DUMPER_STATE + 1;
                      end;
                  [otherwise] :
                      leave DUMP_REQUEST with DUMPER_STATE = .DUMPER_STATE + 1;
                  tes;

              end;
              end;
          !
          ! The dumper is broken! Long live the dumper!
          !
          [inrange, outrange] :
              exitloop;

          tes;

! End of 'WHILE bytes left to dump' loop
! If a dumped a NI device, then send 'dump complete'
    if .DATA [SB_SRVC_CKT_DEVICE] eql DEVTYP_NI
    then begin
         local BUF_PTR;

         BUF_PTR = .DATA [SB_BUFFER_PTR];
         PUTB (DUMP_COMPLETE, BUF_PTR);
         $NMU_DLX_WRITE ( .DATA [SB_CIRCUIT_HANDLE],
                          DLX_DATA,
                          .DATA [SB_BUFFER_PTR],
                          1,
                          .DATA [SB_RESPONSE_PTR]);
         end;

!
! Close dump file
!

    FILE_CLOSE (FB);
    $true
    end;				!End of DUMP_NODE
%routine ('EXECUTE_TRIGGER', TB :ref SERVICE_DATA_BLOCK, RTN) : NML_LKG_DB novalue =
!++
! Functional description:
!
!        This routine is called after the NICE request message has been
!        decomposed. It attempts an OPEN to the Data Link Layer using
!        both the PHYSICAL ETHERNET ADDRESS and the HARDWARE ADDRESS. 
!        If the open is successful, TRIGGER_NODE is called to actually
!        perform the function.  This code used to reside in NML$TRIGGER.
!        It was broken out into a separate routine to give the LOAD_NODE
!        routine an entry point to perform a TRIGGER function if it needs
!        to do so ( i.e. target node does not respond to the first MEMORY
!                   LOAD message.)
!
! Formal parameters:
!
!        .TB            Address of a SSERVICE_DATA_BLOCK
!        .RTN           Address of location to store Return Values
!
! Implicit inputs: none
!
! Routine value: none
! Side effects: none
!
!--

    begin

    bind
        RTN_COD = .RTN;

    RTN_COD = NICE$_SUC;
    TB [SB_USAGE] = DLX_TRIGGER;

    !
    ! Open the link if we can
    !

    if not CIRCUIT_OPEN (.TB)
    then
        RTN_COD = ch$rchar (.TB [SB_RESPONSE_PTR]);

    !
    ! Execute the load.
    ! Deallocate the buffers.
    !

    if .RTN_COD eql NICE$_SUC
    then
	begin
	TRIGGER_NODE (.TB);
        RTN_COD = ch$rchar (.TB [SB_RESPONSE_PTR]);
        CIRCUIT_CLOSE (.TB);
	end;

    !
    ! Do it again for the HARDWARE ADDRESS
    !

    RTN_COD = NICE$_SUC;                ! Reset return code
    TB [SB_PHY_ADDR] = .TB [SB_HARDWARE_ADDRESS];

    if not CIRCUIT_OPEN (.TB)
    then
        RTN_COD = ch$rchar (.TB [SB_RESPONSE_PTR]);

    !
    ! Execute the load.
    ! Deallocate the buffers.
    !

    if .RTN_COD eql NICE$_SUC
    then
	begin
	TRIGGER_NODE (.TB);
        RTN_COD = ch$rchar (.TB [SB_RESPONSE_PTR]);
        CIRCUIT_CLOSE (.TB);
	end;

    end;                          ! End of EXECUTE_TRIGGER
%routine ('TRIGGER_NODE', DATA : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description:
!
!        This routine is called after the NICE request message has been
!        decomposed.  It performs the bootstrap ROM trigger function
!        on a target node.
!
! Formal parameters:
!
!    .DATA    Address of trigger data block
!
! Implicit inputs: none
!
! Routine value: none
! Side effects: none
!
!--

    begin
    if .DATA [SB_SERVICE_PASSWORD] eql 0
    then
        begin
        if not GET_VDB (ENTITY_NODE,
                        .DATA [SB_NODE_DATA],
                        111,
                        DATA [SB_SERVICE_PASSWORD])
        then
            begin
            $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_PAM, 111);
            return $false;
            end;
        end;

    begin
    local
         PASSWORD_PTR,
         BUFFER_PTR;

    BUFFER_PTR = .DATA [SB_BUFFER_PTR];
    PASSWORD_PTR = .DATA [SB_SERVICE_PASSWORD];

    PUTB (ENTER_MOP_MODE, BUFFER_PTR);

    decr COUNT from 8 to 1 do
        PUTB (GETB (PASSWORD_PTR), BUFFER_PTR);

    PUTB (0, BUFFER_PTR);               ! Write PROCESSOR field

    PUTB (0, BUFFER_PTR);               ! Write CONTROL field

    PUTB (-1, BUFFER_PTR);              ! Write SOFTWARE ID field

    end;

    if not $NMU_DLX_WRITE (.DATA [SB_CIRCUIT_HANDLE], DLX_DATA,
                      .DATA [SB_BUFFER_PTR], 12,
                      .DATA [SB_RESPONSE_PTR])
    then
        return $false;

    $RESPONSE (.DATA [SB_RESPONSE_PTR], NICE$_SUC);
    $true
    end;				!End of TRIGGER_NODE
%routine ('FILE_OPEN', FB : ref FILE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine opens the file.
!
! Formal parameters
!
!    .FB      Address of the file data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    File opened
!    $false   Open failure - response set
!
! Side effects: none
!
!--

    begin
    local
         FILE_HANDLE;

    FILE_HANDLE = NMU$FILE_OPEN (.FB[FB_FILE_TYPE],
                                 0 %(not required)%,
                                 .FB[FB_FILE],
                                 .FB[FB_RESPONSE_PTR]);

    if .FILE_HANDLE eql 0 then return $false;

    FB[FB_FILE_HANDLE] = .FILE_HANDLE;
    $true
    end;				!End of FILE_OPEN
%routine ('FILE_READ_LABEL', FB : ref FILE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine reads the load parameters from a .SYS image
!        file.  When this routine completes (successfully) the
!        file pointer is positioned at the start of the load image.
!
!
! Formal parameters
!
!    .FB      Address of the load data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    Parameters read from file and put in load data block
!    $false   Failure while reading parameters
!
! Side effects: none
!
!--

    begin

    local
         BYTE_BUFFER;

!
! Skip to and read the base address where image is to be loaded
!

    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], L$BSA, .FB[FB_RESPONSE_PTR])
    then return $false;

    begin
    local
         PTR;

    PTR = ch$ptr (BYTE_BUFFER,, 8);

    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        return $false;
        end;

    %movi32 (GETW (PTR), FB[FB_CURRENT_ADDRESS]);
    end;
!
! Skip to and read the number of bytes in the image
!
    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], L$BLDZ, .FB[FB_RESPONSE_PTR])
    then return $false;

    begin
    local
         PTR;

    PTR = ch$ptr (BYTE_BUFFER,, 8);

    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        return $false;
        end;

    %movi32 (GETW (PTR), FB[FB_BYTES_LEFT]);
    end;
    %asli32 (6, FB[FB_BYTES_LEFT]);
!
! Skip to and read the flags word in the header
!
    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], L$BFLG, .FB[FB_RESPONSE_PTR])
    then return $false;

    begin
    local
         PTR,
         VALUE;

    PTR = ch$ptr (BYTE_BUFFER,, 8);

    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        return $false;
        end;

    VALUE = GETW (PTR);

    if .VALUE <14, 1> eql 0
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_IFC, .FB[FB_FILE_TYPE],
                   'Header found in image file', 111);
        return $false;
        end;

    end;
!
! Skip to and read the transfer address for the image
!
    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], L$BXFR, .FB[FB_RESPONSE_PTR])
    then return $false;

    begin
    local
         TEMP,
         PTR;

    PTR = ch$ptr (BYTE_BUFFER,, 8);

    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        return $false;
        end;

    TEMP = GETW (PTR);
    %movt32 (TEMP, FB[FB_TRANSFER_ADDRESS]);
    end;
!
! Skip to and read the block offset to the image in file (512 bytes
! per block).
!
    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], L$BBLK, .FB[FB_RESPONSE_PTR])
    then return $false;

    begin
    local
         PTR;

    PTR = ch$ptr (BYTE_BUFFER,, 8);

    if 0 geq NMU$FILE_READ (.FB[FB_FILE_HANDLE], .PTR, 2, .FB[FB_RESPONSE_PTR])
    then
        begin
        $RESPONSE_X (.FB[FB_RESPONSE_PTR], NICE$_FIO, .FB[FB_FILE_TYPE],
                     'Could not read image file', 119);
        return $false;
        end;

    BYTE_BUFFER = GETW (PTR) * 512;
    end;

    if not NMU$FILE_SEEK (.FB[FB_FILE_HANDLE], .BYTE_BUFFER, .FB[FB_RESPONSE_PTR])
    then return $false;

    $true
    end;				!End of FILE_READ_LABEL
%routine ('FILE_CLOSE', FB : ref FILE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine closes the file.
!
! Formal parameters
!
!    .FB      Address of the file data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    Parameters read from file and put in load data block
!    $false   Failure while reading parameters
!
! Side effects: none
!
!--

    begin
    NMU$FILE_CLOSE (.FB[FB_FILE_HANDLE], .FB[FB_RESPONSE_PTR]);
    $true
    end;				!End of FILE_CLOSE
%routine ('FETCH_PARAMETERS', REQ : ref REQUEST_BLOCK, DB : ref SERVICE_DATA_BLOCK, PRM : ref vector) =

!++
! Functional description:
!
!	This routine extracts the parameters supplied in the NICE request
!       message, checks to make sure they are applicable to the request,
!       and stores them in the service block.
!
! Formal parameters:
!
!	.REQ	NML Request Block address
!       .DB     dump/load/trigger block address
!       .PRM    address of plit vector with acceptable parameter numbers
!
! Implicit inputs: none
!
! Routine value:
!
!	NICE return code.
!
! Side effects: none
!
!--

    begin
    local
         ENTITY_TYPE,
         PARM_NO,
         PARM_DATA: THIRTY_TWO_BIT;

    ENTITY_TYPE =.REQ[RB_NICE_ENTITY_TYPE] ; ! Save real entity type
    REQ[RB_NICE_ENTITY_TYPE] = ENTITY_NODE ; ! Parameters are all NODE 

    while GET_NEXT_PARAMETER (.REQ, PARM_NO, PARM_DATA)
    do
       begin

       selectone
           begin
           local
                ADR : ref vector,
                CNT;
           ADR = .PRM;
           CNT = .ADR [-1];

           do
             begin
             local
                  VAL;

             VAL = .ADR [0];
             ADR = ADR [1];

             if .PARM_NO eql .VAL then exitloop .VAL;

             end
           while (CNT = .CNT - 1) neq 0

           end
       of
           set
           [10]:                        ! PHYSICAL ADDRESS
               begin
               if GETB (PARM_DATA) neq 6 ! It must be HI-6
               then
                   begin
                   $RESPONSE (.DB[SB_RESPONSE_PTR], $NICE$ERR_IMF);
                   exitloop;
                   end;
               DB[SB_PHY_ADDR] = .PARM_DATA; ! Save pointer to data
               end;
           [110]:                       ! SERVICE CIRCUIT
               DB[SB_SERVICE_CIRCUIT] = .PARM_DATA ;
           [111]:                       ! SERVICE PASSWORD
               DB[SB_SERVICE_PASSWORD] = .PARM_DATA ;
           [112]:                       ! SERVICE DEVICE
               DB[LB_SERVICE_DEVICE] = .PARM_DATA ;
           [113]:                       ! CPU
               selectone ch$rchar (.PARM_DATA) of
                   set
                   [0 to 3] : DB[LB_CPU] = .PARM_DATA;
                   [otherwise] :
                       begin
                       $RESPONSE_X (.DB[SB_RESPONSE_PTR], NICE$_IPV, 113);
                       exitloop;
                       end;
                   tes;
           [114]:
               DB[SB_HARDWARE_ADDRESS] = .PARM_DATA;
           [115]:
               DB[SB_NODE_VERSION] = .PARM_DATA ;
           [120]:                       ! LOAD FILE
               DB[LB_LOAD_FILE] = .PARM_DATA ;
           [121]:                       ! SECONDARY LOADER
               DB[LB_SECONDARY_LOADER] = .PARM_DATA ;
           [122]:                       ! TERTIARY LOADER
               DB[LB_TERTIARY_LOADER] = .PARM_DATA ;
           [123]:                       ! DIAGNOSTIC FILE
               DB[LB_DIAGNOSTIC_FILE] = .PARM_DATA ;
           [125]:                       ! SOFTWARE TYPE
                selectone .PARM_DATA of
                    set
                    [0 to 2] : DB[LB_SOFTWARE_TYPE] = .PARM_DATA;
                    [otherwise] :
                        begin
                        $RESPONSE_X (.DB[SB_RESPONSE_PTR], NICE$_IPV, 125);
                        exitloop;
                        end;
                    tes;
           [126]:                       ! SOFTWARE IDENTIFICATION
               DB[LB_SOFTWARE_IDENTIFICATION] = .PARM_DATA ;
           [130]:                       ! DUMP FILE
               DB[DB_DUMP_FILE] = .PARM_DATA ;
           [131]:                       ! SECONDARY DUMPER
               DB[DB_SECONDARY_DUMPER] = .PARM_DATA ;
           [135]:                       ! DUMP ADDRESS
               %mov32 (PARM_DATA, DB[DB_DUMP_ADDRESS]);
           [136]:                       ! DUMP COUNT
               %mov32 (PARM_DATA, DB[DB_DUMP_COUNT]);
           [141]:                       ! HOST
               begin
               local
                    FMT,
                    NOD_PTR;

               NOD_PTR = ch$ptr (DB[LB_HOST],,8) ;
               FMT = GETB (PARM_DATA);  ! Get node id format byte

               selectoneu .FMT of
                   set
                   [0]:
                       begin            ! Node address
                       PUTB (GETB (PARM_DATA), NOD_PTR);
                       PUTB (GETB (PARM_DATA), NOD_PTR);
                       PUTB (0,NOD_PTR) ; ! Null node name
                       end;
                   [1 to 6]:
                       begin            ! Node name
                       PUTB (0,NOD_PTR); ! Node address of zero
                       PUTB (0,NOD_PTR);
                       PUTB (.FMT,NOD_PTR) ; ! Length of node name
                       ch$move (.FMT,.PARM_DATA,.NOD_PTR) ;
                       end;
                   [otherwise]:
                       begin
                       $RESPONSE_X (.DB[SB_RESPONSE_PTR],NICE$_IID,ENTITY_NODE,
                                    'HOST Node Id', 101) ;
                       exitloop;
                       end;
                   tes;

               end;
           [500]:                       ! NAME
               DB [LB_NAME] = .PARM_DATA;
           [502]:                       ! ADDRESS
               DB [LB_ADDRESS] = .PARM_DATA;
           [2501]:
               DB [LB_CONSOLE_SECONDARY_LOADER] = .PARM_DATA;
           [2502]:
               DB [LB_CONSOLE_LOAD_FILE] = .PARM_DATA;
           [otherwise]:
               begin
               $RESPONSE_X (.DB[SB_RESPONSE_PTR],NICE$_PNA,.PARM_NO) ;
               exitloop;
               end;
           tes;

       end;

    REQ[RB_NICE_ENTITY_TYPE] = .ENTITY_TYPE ; ! Restore real entity type

    ch$rchar (.DB[SB_RESPONSE_PTR])
    end;				! End of FETCH_PARAMETERS
%routine  ('GET_NEXT_PARAMETER', REQ : ref REQUEST_BLOCK, PARM_NO, PARM_DATA) =

!++
! Functional description:
!
!	Obtains the next parameter in the NICE request message.
!
! Formal parameters:
!
!	.REQ		The address of the NML Request Block for this request.
!       .PARM_NO	Address of variable to return parameter number.
!       .PARM_DATA	Address of variable in which to return parameter data.
!
! Implicit inputs:
!
!	.REQ [RB_NICE_PARAMETERS]	Contains a pointer to the previous
!				 	 parameter.
!       .REQ [RB_NICE_PARAMETER_LENGTH]	Contains the length of the previous
!					 parameter in bytes.
!
! Routine value:
!
!	$true, if the next parameter exists.
!       $false, otherwise.
!
! Side effects: none
!
!--

    begin

    local
         PARM_PTR ;                     ! Pointer to NICE parameter

    REQ[RB_NICE_PARAMETERS] = ch$plus (.REQ[RB_NICE_PARAMETERS],
                                       .REQ[RB_NICE_PARAMETER_LENGTH]) ;

    !
    ! Return false when all parameters have been processed.
    !

    if ch$diff (.REQ[RB_NICE_PARAMETERS],.REQ[RB_NICE_POINTER]) geq .REQ[RB_NICE_LENGTH]
    then begin
         REQ[RB_NICE_PARAMETER_LENGTH] = 0 ;
         return $false;
         end;

    !
    ! Get length of the parameter, to include the data portion plus
    ! two bytes for the DATA ID field.
    !

    PARM_PTR = .REQ[RB_NICE_PARAMETERS] ; ! Make copy of pointer
    .PARM_NO = GETW (PARM_PTR) ;         ! Get parameter number
    REQ[RB_NICE_PARAMETER_LENGTH] = NML$PARAMETER_DATA (.REQ[RB_NICE_ENTITY_TYPE],
                                                        ..PARM_NO,
                                                        PARM_PTR,
                                                        .PARM_DATA) + 2 ;

    !
    ! If this parameter is a qualifier, then call ourself recursively
    ! to skip over the qualifier to the next real parameter.
    !

    if .REQ[RB_NICE_PARAMETERS] eql .REQ[RB_NICE_QUALIFIER]
    then return GET_NEXT_PARAMETER (.REQ, .PARM_NO, .PARM_DATA);

    return $true

    end;				!End of GET_NEXT_PARAMETER
%routine ('GET_VDB', ENTITY_TYPE, ENTITY, PAR_NO, RESP_PTR) =

!++
!
! Functional description:
!
!   It is possible to downline load nodes that do not have a database entry.
!   This is the case for DECSERVER-100's.  The code in the NMLDTL module
!   needs to fetch many parameters from the node database - if it exists.
!
!   This routine is an envelop routine to the NML$GET_VDB_PARAMETER routine.
!   It checks if a zero entity ID is supplied.  If so, it just returns $false.
!   Otherwise, it call NMLVDB.
!
! Parameters are exactly as for call to NML$GET_VEB_PARAMETER
!
!--

    return
       if .ENTITY eql 0
       then $false
       else NML$GET_VDB_PARAMETER (.ENTITY_TYPE, .ENTITY, .PAR_NO, .RESP_PTR)
    ;                                   ! End of GET_VDB
%routine ('SET_SUBSTATE', SB : ref SERVICE_DATA_BLOCK) : NML_LKG_DB novalue =

!++
! Functional description:
!
!        Set the substate for a given circuit.
!
! Formal parameters:
!
!    .DATA    Address of trigger data block
!
! Implicit inputs: none
!
! Routine value: none
! Side effects: none
!
!--

    begin

    if .SB[SB_SUBSTATE] geq 0
    then NML$SET_VDB_PARAMETER (ENTITY_CIRCUIT, .SB[SB_SERVICE_CIRCUIT],
                                CPARM_SUBSTATE, %ref (.SB[SB_SUBSTATE]))
    else NML$CLR_VDB_PARAMETER (ENTITY_CIRCUIT, .SB[SB_SERVICE_CIRCUIT],
                                CPARM_SUBSTATE);

    end;				!End of SET_SUBSTATE
%routine ('CPU_IS_PDP11', DB : ref SERVICE_DATA_BLOCK) : NML_LKG_DB =

!++
! Functional description
!
!        This routine checks that the CPU parameter is PDP11.
!
! Formal parameters
!
!    .DB      Address of the service data block
!
! Implicit inputs: none
!
! Routine value:
!
!    $true    CPU parameter is PDP11
!    $false   Not PDP11 - response set
!
! Side effects: none
!
!--

    begin
    local
         CPU;

    if (CPU = .DB [LB_CPU]) eql -1
    then
        if not GET_VDB (ENTITY_NODE,
                        .DB [SB_NODE_DATA],
                         113,
                         CPU)
        then
            begin
            $RESPONSE_X (.DB [SB_RESPONSE_PTR], NICE$_PAM, 113);
            return $false;
            end;

    if .CPU neq CPU_PDP11
    then
        begin
        $RESPONSE_X (.DB [SB_RESPONSE_PTR], NICE$_IPV, 113);
        return $false;
        end;

    $true
    end;				!End of CPU_IS_PDP11

end				!End of Module NMLDTL

eludom
! Local Modes:
! Mode:BLISS
! Auto Save Mode:0
! Comment Column:40
! Comment Rounding:+1
! End: