Google
 

Trailing-Edge - PDP-10 Archives - BB-X117B-SB_1986 - 10,7/nml/dtet10.b36
There are no other files named dtet10.b36 in the archive.
!DSKB:DTET10.B36[10,6062,NML] 1985-05-15 08:51:39, Edit by APPELLOF
!
!	Complete Davenport's work adding routines to log CHK11
!	output to ERROR.SYS.
!	Add routines CHK11_TTY_PSI_INTERRUPT,
!	OPEN_CHK11_TTY, CLOSE_CHK11_TTY, READ_CHK11_OUTPUT
!
! DSKT:DTET10.B36[10,6026,NML703] 19-Oct-84 14:50:11, Edit by DAVENPORT
!
!	Remove calls to NMU$SCHED_PAUSE in routines TRIGGER_ROM and
!	ROM_DUMP.  NMLDLW and NMLDTL need these to be uninterruptable
!	so as to interlock against each other.
!
! DSKT:DTET10.B36[10,6026,NML703] 16-Oct-84 09:37:32, Edit by DAVENPORT
!
!	Added code to OPEN_DTE_DEVICE to check for CPU not running and give
!	an appropriate error message.  Changed START_PROTOCOL to clear the
!	DTE before trying to start protocol.  Also now tries four times
!	before giving up.
!
%sbttl 'TOPS-10 Specific DTE Service Routines'


!
! External routines
!

external routine
    NMU$MEMORY_GET,
    NMU$MEMORY_RELEASE,
    NMU$FILE_BUILD_BUFFERS,
    NMU$FILE_KILL_BUFFERS: novalue;


!
! Forward routines
!

forward routine
    CHK11_TTY_PSI_INTERRUPT: vanilla_interrupt_linkage novalue,
    OPEN_CHK11_TTY: novalue,
    CLOSE_CHK11_TTY: novalue,
    READ_CHK11_OUTPUT: novalue,
    READ_DTE_DATA_BUFFER,
    WRITE_DTE_DATA_BUFFER,
    DTE_PSI_INTERRUPT: vanilla_interrupt_linkage
		       novalue;


!
! Macros
!

macro
    DTE_ARG_BLOCK = vector [10] volatile %,	! DTE. UUO argument block
    FILOP_ARG_BLOCK = vector [10] volatile %,	! FILOP. UUO argument block
    PSI_ARG_BLOCK = vector [3] volatile %,	! PISYS. UUO argument block
    TRMOP_ARG_BLOCK = vector [2] volatile %;	! TRMOP. UUO argument block
!
! Items in CHK11 (222) entry in ERROR.SYS made by NML through DAEMON
! taken from SPEAR
!  In the code, all offsets will be + 2 to account for arguments to
! The DAEMON UUO

LITERAL
	NH_MID=%O'0',	!TRANSMITTING NODE ID#,, DESTINATION NODE ID#
			!contains 0 in NML entry
	NH_XID=%O'1',	!PNTR TO ASCIZ TRANSMITTING NODE NAME
			!contains CIRCUIT_ID in NML entry
	NH_RID=%O'2',	!PNTR TO ASCIZ Destination NODE NAME
			!contains 0 in NML entry
	NH_ADN=%O'3',	!PNTR TO ASCIZ Adjacent Node Name
			!ie, the node at the other end of the line
			!contains 0 in NML entry
	NH_FUN=%O'4',	!FUNCTION WORD AS DESCRIBED BELOW
			!contains 0 in NML entry
	NH_NUM=%O'5',	!RIGHT JUSTIFIED NUMBER OF BYTES IN LOG
			!DATA SECTION
	NH_SEQ=%O'6',	!HARDWARE ENTRY SEQ. NUMBER FROM THE XMIT NODE
			!contains 0 in NML entry
	NH_LID=%O'7',	!CONTROLLER #,,LINE or unit #
			!(-1 IS NULL indicating NO LINE or unit IS 
			!ASSOCIATED WITH THIS ENTRY, 0=single line unit ie. DL11)
	NH_LI1=%O'10',	!RESERVED,,STATION #
			!(WORDS NH_LID %O'',& NH_LI1 %O''ARE USED TO MAKE UP AN
			!ASCIZ LINE IDENTIFICATION )
			!contains 0 in NML entry
	NH_DAT=%O'11',	!NODE UPTIME IN MILLISECS. WHEN
			!THIS EVENT/ERROR WAS DETECTED.
			!contains 0 in NML entry
	NH_TRY=%O'12',	!FLAGS AND RETRY COUNT
			!contains 0 in NML entry
	NH_RSM=%O'13',	!REASON FOR ENTRY (OCTAL)
			!contains 0 in NML entry
	NH_PTR=%O'14',	!PNTR TO START OF LOG DATA.
	NH_LEN = NH_PTR + 1;	!Length of entry header
%routine ('GET_DTE_SYSID', CD: ref CD_BLOCK) =

!++
! Function description:
!
!	This routine returns the system specific identifier
!	for the DTE described by the circuit data block.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!
! Routine value:
!
!	The system identifier for the DTE
!
!--

begin

    !
    ! Create the id from the CPU and DTE numbers
    !

    .CD [CD_CONTROLLER]^18 + .CD [CD_DEVICE]

end;				! of GET_DTE_SYSID
%routine ('OPEN_DTE_DEVICE', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is called by NMU$DTE_OPEN to perform
!	system specific operations for servicing a DTE.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			System specific open succeeded
!	$false			Error opening DTE
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	DTE_DEVICE_NAME,
	BUFFER_SIZE,
	FILOP_BLOCK: FILOP_ARG_BLOCK,
	PSI_BLOCK: PSI_ARG_BLOCK;

    bind
	OBUF = CD [CD_DTE_OBUF]: vector [3],
	IBUF = CD [CD_DTE_IBUF]: vector [3];

    !
    ! First check if the DTE's CPU is running
    !

    T1 = _CCOKP + 2*(.CD [CD_CONTROLLER]);
    if not UUO (1, GETTAB (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Unable to obtain CPU status for device %X',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    if .T1 gtr 0
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'CPU not running for device %X',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    !
    ! Create a SIXBIT device name for the specific DTE
    !

    DTE_DEVICE_NAME = %sixbit 'DTE000' or .CD [CD_CONTROLLER]^6 or .CD [CD_DEVICE];

    !
    ! Open the DTE for I/O
    !

    FILOP_BLOCK [$FOFNC] = FO$PRV or FO$ASC or $FOSIO;
    FILOP_BLOCK [$FOIOS] = UU$AIO or IO$MAI or $IOBYT;
    FILOP_BLOCK [$FODEV] = .DTE_DEVICE_NAME;
    FILOP_BLOCK [$FOBRH] = OBUF^18 + IBUF;

!    T1 = ($FOBRH+1)^18 + FILOP_BLOCK;
!
!    if not UUO (1, FILOP$ (T1))
!    then
!	begin
!	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
!		       'Unable to open %X for I/O.  FILOP. error %O',
!		       ch$ptr (CD [CD_NAME],,8),
!		       .T1);
!	    return $false;
!	end;
!
!    CD [CD_DTE_CHAN] = (.FILOP_BLOCK [$FOFNC] and FO$CHN)^-18;

    if not UUO (1, OPEN (1, FILOP_BLOCK [$FOIOS]))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Unable to open %X for I/O',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    CD [CD_DTE_CHAN] = 1;

    !
    ! Get size of buffers to allocate
    !

    T1 = FILOP_BLOCK [$FOIOS];

    if not UUO (1, DEVSIZ (T1))
    then
	begin
	    CLOSE_DTE_DEVICE (.CD);
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Unable to determine %X I/O buffer size',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    BUFFER_SIZE = .T1 <0,18,0>;

    !
    ! Allocate I/O buffers for DTE device
    !

    if ((OBUF [$BFADR] = NMU$FILE_BUILD_BUFFERS (1, .BUFFER_SIZE)) eqla 0)
	or
       ((IBUF [$BFADR] = NMU$FILE_BUILD_BUFFERS (1, .BUFFER_SIZE)) eqla 0)
    then
	begin
	    CLOSE_DTE_DEVICE (.CD);
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Unable to allocate I/O buffers for %X',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    !
    ! Set up for PSI interrupts for this DTE
    !

    NMU$SCHED_EVENT (CD [CD_DTE_EVB], $true);

    CD [CD_DTE_PSI] = ALLOCATE_INTERRUPT_CHANNEL (DTE_PSI_INTERRUPT, .CD);

    PSI_BLOCK [$PSECN] = .CD [CD_DTE_CHAN];
    PSI_BLOCK [$PSEOR] = (.CD [CD_DTE_PSI]*4)^18 or PS$RID or PS$ROD or PS$RDO;
    PSI_BLOCK [$PSEPR] = 0;

    T1 = PS$FAC + PSI_BLOCK;

    if not UUO (1, PISYS$ (T1))
    then
	begin
	    CLOSE_DTE_DEVICE (.CD);
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Unable to enable PSI system for %X',
		       ch$ptr (CD [CD_NAME],,8));
	    return $false;
	end;

    !
    ! Open CHK11 terminal
    !

    OPEN_CHK11_TTY (.CD);

    $true

end;				! of OPEN_DTE_DEVICE
%routine ('CLOSE_DTE_DEVICE', CD: ref CD_BLOCK) =

!++
! Functional description:
!
!	This routine is called by NMU$DTE_CLOSE to perform
!	system specific operations for releasing a DTE.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!
! Routine value:
!
!	$true			System specific release succeeded
!	$false			Error releasing DTE
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	RETURN_VALUE,
	FILOP_BLOCK: FILOP_ARG_BLOCK,
	PSI_BLOCK: PSI_ARG_BLOCK;

    bind
	OBUF = CD [CD_DTE_OBUF]: vector [3],
	IBUF = CD [CD_DTE_IBUF]: vector [3];

    !
    ! Release CHK11 terminal and flush output
    !

    CLOSE_CHK11_TTY (.CD);

    !
    ! Remove DTE from PSI system
    !

    PSI_BLOCK [$PSECN] = .CD [CD_DTE_CHAN];
    PSI_BLOCK [$PSEOR] = (.CD [CD_DTE_PSI]*4)^18 or PS$RID or PS$ROD or PS$RDO;
    PSI_BLOCK [$PSEPR] = 0;

    T1 = PS$FRC + PSI_BLOCK;

    UUO (1, PISYS$ (T1));

    RELEASE_INTERRUPT_CHANNEL (.CD [CD_DTE_PSI]);

    !
    ! Close the DTE device
    !

    FILOP_BLOCK [$FOFNC] = (.CD [CD_DTE_CHAN])^18 or $FOCLS;

!    T1 = ($FOFNC+1)^18 + FILOP_BLOCK;
!
!    RETURN_VALUE = UUO (1, FILOP$ (T1));

    UUO (0, CLOSE (.CD [CD_DTE_CHAN], 0));
    UUO (0, RELEAS (.CD [CD_DTE_CHAN], 0));
    RETURN_VALUE = $true;

    !
    ! Deallocate I/O buffers
    !

    if .OBUF [$BFADR] neqa 0
    then
	NMU$FILE_KILL_BUFFERS (.OBUF [$BFADR]);
    if .IBUF [$BFADR] neqa 0
    then
	NMU$FILE_KILL_BUFFERS (.IBUF [$BFADR]);

    OBUF [$BFADR] = 0;
    IBUF [$BFADR] = 0;

    .RETURN_VALUE

end;				! of CLOSE_DTE_DEVICE
%routine ('START_PROTOCOL', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Function description:
!
!	This routine is used to initialize protocol on a
!	DTE circuit.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			Protocol successfully initialized
!	$false			Error initializing protocol
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	DTE_BLOCK: DTE_ARG_BLOCK;	! DTE. UUO argument block

    incr COUNT from 1 to 4
    do
	begin

	    !
	    ! First see if primary protocol is already running
	    !

	    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

	    T1 = $DTEGS ^ 18 + DTE_BLOCK;

	    if not UUO (1, DTE$ (T1))
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'Could not check status on %X.  DTE. error %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

	    if ((.T1 and DT$RLD) eql 0)
		and
	       ((.T1 and DT$PPC) neq 0)
	    then
		return $true;

	    !
	    ! Clear the DTE to a known state
	    !

	    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

	    T1 = $DTECL ^ 18 + DTE_BLOCK;

	    if not UUO (1, DTE$ (T1))
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'Could not clear %X.  DTE. error %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

	    !
	    ! Set DTE line user to DECNET
	    !

	    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];
	    DTE_BLOCK [1] = %sixbit 'DECNET';

	    T1 = $DTESU ^ 18 + DTE_BLOCK;

	    if not UUO (1, DTE$ (T1))
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'Could not set %X user to DECNET.  DTE. error %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

	    !
	    ! Try to start primary protocol
	    !

	    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

	    T1 = $DTEST ^ 18 + DTE_BLOCK;
	    if UUO (1, DTE$ (T1))
	    then
		return $true;

	    NMU$SCHED_PAUSE ();

	end;

    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
	       'Could not start primary protocol on %X.  DTE. error %O',
	       ch$ptr (CD [CD_NAME],,8),
	       .T1);

    $false

end;				! of START_PROTOCOL
%routine ('STOP_PROTOCOL', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Function description:
!
!	This routine is used to terminate protocol on a
!	DTE circuit.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			Protocol successfully terminated
!	$false			Error clearing protocol
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	DTE_BLOCK: DTE_ARG_BLOCK;	! DTE. UUO argument block

    !
    ! Set up argument block with DTE identifier
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

    T1 = $DTECL ^ 18 + DTE_BLOCK;

    if not UUO (1, DTE$ (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_HWF, 0,
		       'Could not stop protocol on %X.  DTE. error %O',
		       ch$ptr (CD [CD_NAME],,8),
		       .T1);
	    return $false;
	end;

    $true

end;				! of STOP_PROTOCOL
%routine ('TRIGGER_ROM', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Function description:
!
!	This routine is called to trigger the DN20 boot-
!	strap ROM.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			Bootstrap ROM triggered successfully
!	$false			Error triggering bootstrap ROM
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	DTE_BLOCK: DTE_ARG_BLOCK;	! DTE. UUO argument block

    !
    ! Set DTE line user to 'PROGRA' so we can use it
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];
    DTE_BLOCK [1] = %sixbit 'PROGRA';

    T1 = $DTESU ^ 18 + DTE_BLOCK;

    if not UUO (1, DTE$ (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_HWF, 0,
		       'Could not set %X user to NML.  DTE. error %O',
		       ch$ptr (CD [CD_NAME],,8),
		       .T1);
	    return $false;
	end;

    !
    ! Trigger DN20 ROM for LOAD and DUMP
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

    T1 = $DTEPR ^ 18 + DTE_BLOCK;

    if not UUO (1, DTE$ (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_HWF, 0,
		       'Could not trigger ROM bootstrap on %X.  DTE. error %O',
		       ch$ptr (CD [CD_NAME],,8),
		       .T1);
	    return $false;
	end;

!   NMU$SCHED_PAUSE ();

    $true

end;				! of TRIGGER_ROM
%routine ('ROM_DUMP', CD: ref CD_BLOCK, DUMP_PAGE, RSP_POINTER) =

!++
! Function description:
!
!	This routine is called to dump 2K bytes from the DN20
!	using the ROM.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.DUMP_PAGE		Page number to store dump data
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			Dump successful
!	$false			Error during ROM assisted dump
!
!--

begin

    builtin
	UUO;

    register
	T1;

    literal
	MAX_ROM_DUMP_WORDS = %o'200';

    local
	COUNT,
	DUMP_COUNT,
	DUMP_POINTER,
	DTE_BLOCK: DTE_ARG_BLOCK;		! DTE. UUO argument block

    !
    ! Initialize count of words dumped and dump pointer
    !

    DUMP_COUNT = 0;
    DUMP_POINTER = ch$ptr (.DUMP_PAGE^9,, 16);

    !
    ! Loop until dump page is filled with dump data
    !

    while .DUMP_COUNT lss (2 * 512)
    do
	begin

	    !
	    ! Setup argument block for next transfer of up
	    ! to MAX_ROM_DUMP_WORDS of DN20 memory.
	    !

	    COUNT = min ((2*512 - .DUMP_COUNT), MAX_ROM_DUMP_WORDS);
	    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];
	    DTE_BLOCK [1] = .DUMP_POINTER;
	    DTE_BLOCK [2] = .COUNT;

	    !
	    ! Read dump data, assisted by primary boot ROM in DN20
	    !

	    T1 = $DTEDM ^ 18 + DTE_BLOCK;

	    if not UUO (1, DTE$ (T1))
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_HWF, 0,
			       'ROM assisted dump failed on %X. DTE. error %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

!	    NMU$SCHED_PAUSE ();

	    !
	    ! Update dump count and pointer
	    !

	    DUMP_COUNT = .DUMP_COUNT + .COUNT;
	    DUMP_POINTER = ch$plus (.DUMP_POINTER, .COUNT);

	end;

    $true

end;				! of ROM_DUMP
%routine ('READ_MOP_DATA', CD: ref CD_BLOCK, PTR, LEN, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is used by NMU$DTE_READ to read a MOP data
!	messages from a DN20 front end.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.PTR			Pointer to message buffer
!	.LEN			Number of bytes in message buffer to write
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	Number of bytes read (-1 if error)
!
!--

begin

    bind
	IBUF = CD [CD_DTE_IBUF]: vector [3] volatile;

    if .IBUF [$BFCTR] eql 0
    then
	if not READ_DTE_DATA_BUFFER (.CD, .RSP_POINTER)
	then
	    return $false;

    NMU$SCHED_PAUSE ();

    LEN = min (.IBUF [$BFCTR], .LEN);

    decr COUNT from .LEN to 0
    do
	ch$wchar_a (ch$rchar_a (IBUF [$BFPTR]), PTR);

    IBUF [$BFCTR] = .IBUF [$BFCTR] - .LEN;

    .LEN

end;				! of READ_MOP_DATA
%routine ('READ_DTE_DATA_BUFFER', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is called by READ_MOP_DATA to read the
!	next buffer of data from the DTE device.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			If successful
!	$false			If I/O error or timeout
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	FILOP_BLOCK: FILOP_ARG_BLOCK;

    while not
	begin
	    FILOP_BLOCK [$FOFNC] = (.CD [CD_DTE_CHAN])^18 or $FOINP;
	    T1 = ($FOFNC+1)^18 + FILOP_BLOCK;
	    UUO (1, FILOP$ (T1))
	end
    do
	begin
	    if (.T1 and IO$ERR) neq 0
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'I/O error reading %X MOP data.  Status = %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

	    if not NMU$SCHED_WAIT (CD [CD_DTE_EVB], 120)
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'Timeout reading %X MOP data',
			       ch$ptr (CD [CD_NAME],,8));
		    return $false;
		end;
	end;

    $true

end;				! of READ_DTE_DATA_BUFFER
%routine ('WRITE_MOP_DATA', CD: ref CD_BLOCK, PTR, LEN, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is used by NMU$DTE_WRITE to send MOP data
!	messages to a DN20 front end.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.PTR			Pointer to message buffer
!	.LEN			Number of bytes in message buffer to write
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			If successful
!	$false			If error
!
!--

begin

    bind
	OBUF = CD [CD_DTE_OBUF]: vector [3] volatile;

    decr COUNT from .LEN to 1
    do
	begin
	    if .OBUF [$BFCTR] eql 0
	    then
		if not WRITE_DTE_DATA_BUFFER (.CD, .RSP_POINTER)
		then
		    return $false;

	    OBUF [$BFCTR] = .OBUF [$BFCTR] - 1;
	    ch$wchar_a (ch$rchar_a (PTR), OBUF [$BFPTR]);
	end;

    if not WRITE_DTE_DATA_BUFFER (.CD, .RSP_POINTER)
    then
	return $false;

    $true

end;				! of WRITE_MOP_DATA
%routine ('WRITE_DTE_DATA_BUFFER', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is called by WRITE_MOP_DATA to write the
!	next buffer of data to the DTE device.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			If successful
!	$false			If I/O error or timeout
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	FILOP_BLOCK: FILOP_ARG_BLOCK;

    while not
	begin
	    FILOP_BLOCK [$FOFNC] = (.CD [CD_DTE_CHAN])^18 or $FOOUT;
	    T1 = ($FOFNC+1)^18 + FILOP_BLOCK;
	    UUO (1, FILOP$ (T1))
	end
    do
	begin
	    if (.T1 and IO$ERR) neq 0
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'I/O error writing %X MOP data.  Status = %O',
			       ch$ptr (CD [CD_NAME],,8),
			       .T1);
		    return $false;
		end;

	    if not NMU$SCHED_WAIT (CD [CD_DTE_EVB], 600)
	    then
		begin
		    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
			       'Timeout writing %X MOP data',
			       ch$ptr (CD [CD_NAME],,8));
		    return $false;
		end;
	end;

    $true

end;				! of WRITE_DTE_DATA_BUFFER
%routine ('WRITE_SECONDARY_DATA', CD: ref CD_BLOCK, PTR, LEN, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is used by NMU$DTE_WRITE to load the second-
!	ary bootstrap loader into a DN20 front end.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.PTR			Pointer to message buffer
!	.LEN			Number of bytes in message buffer to write
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			If successful
!	$false			If error
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	LOADER_PAGE,
	LOADER_POINTER,
	MOP_POINTER,
	DTE_BLOCK: DTE_ARG_BLOCK;

    LOADER_PAGE = NMU$PAGE_GET ();
    LOADER_POINTER = ch$ptr (.LOADER_PAGE^9,,16);
    MOP_POINTER = ch$plus (.PTR, 6);

    incr COUNT from 1 to (.LEN-8) by 2
    do
	ch$wchar_a (GETW (MOP_POINTER), LOADER_POINTER);

    !
    ! Set up the DTE. UUO argument block
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];
    DTE_BLOCK [1] = ch$ptr (.LOADER_PAGE^9,,16);
    DTE_BLOCK [2] = 256;

    T1 = $DTELS ^ 18 + DTE_BLOCK;

    !
    ! Load the secondary bootstrap into the DN20
    !

    if not UUO (1, DTE$ (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Failure loading secondary boot on %X.  DTE. error %O',
		       ch$ptr (CD [CD_NAME],,8),
		       .T1);
	    NMU$PAGE_RELEASE (.LOADER_PAGE);
	    return $false;
	end;

    NMU$PAGE_RELEASE (.LOADER_PAGE);

    NMU$SCHED_PAUSE ();

    $true

end;				! of WRITE_SECONDARY_DATA
%routine ('OWNER_IS_DECNET', CD: ref CD_BLOCK, RSP_POINTER) =

!++
! Functional description:
!
!	This routine is used by NMU$DTE_OPEN is insure that the
!	current user of a DTE is either DECNET or NML.  This is
!	to guarantee that NML doesn't interfere with ANF10 DTEs
!	or DTEs currently running diagnostics.
!
! Formal parameters:
!
!	.CD			Pointer to circuit data block
!	.RSP_POINTER		Pointer to NICE response buffer
!
! Routine value:
!
!	$true			If DTE owner is DECNET or NML
!	$false			If DTE owner isn't DECNET or NML
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	DTE_BLOCK: DTE_ARG_BLOCK;

    !
    ! Set up argument block with DTE identifier
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

    T1 = $DTERU ^ 18 + DTE_BLOCK;

    if not UUO (1, DTE$ (T1))
    then
	begin
	    $RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		       'Could not read %X line user.  DTE. error %O',
		       ch$ptr (CD [CD_NAME],,8),
		       .T1);
	    return $false;
	end;

    !
    ! Return now if line user is DECNET or if line
    ! currently not in use
    !

    if (.DTE_BLOCK [1] eql %sixbit 'DECNET')
	or
       (.DTE_BLOCK [1] eql %sixbit 'NOBODY')
    then
	return $true;

    !
    ! Last chance is line owner of NML
    !

    UUO (0, PJOB (T1));

    if (.DTE_BLOCK [1] eql %sixbit 'PROGRA')
	and
       (.DTE_BLOCK [2] eql .T1)
    then
	return $true;

    !
    ! Give reasonable explanation of problem
    !

    if .DTE_BLOCK [1] eql %sixbit 'PROGRA'
    then
	$RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		   '%X is in use by job %D',
		   ch$ptr (CD [CD_NAME],,8),
		   .DTE_BLOCK [2])
    else
	$RESPONSE (.RSP_POINTER, NICE$_OPF, 0,
		   '%X is in use by a protocol other than DECNET',
		   ch$ptr (CD [CD_NAME],,8));

    $false

end;				! of OWNER_IS_DECNET
%routine ('DTE_PSI_INTERRUPT', CD: ref CD_BLOCK): vanilla_interrupt_linkage novalue =

!++
! Functional description:
!
!	Routine called upon a DTE input/output done interrupt.
!
!--

begin

    NMU$SCHED_FLAG (CD [CD_DTE_EVB])

end;				! of DTE_PSI_INTERRUPT
%routine ('OPEN_CHK11_TTY', CD: ref CD_BLOCK): novalue =

!++
!  Functional description:
!
!	Routine called when DTE device is opened to open the CHK11
!	terminal so that CHK11 output can be logged.
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	BUFFER_SIZE,
	DTE_BLOCK: DTE_ARG_BLOCK,
	FILOP_BLOCK: FILOP_ARG_BLOCK,
	TRMOP_BLOCK: TRMOP_ARG_BLOCK,
	PSI_BLOCK: PSI_ARG_BLOCK;

    bind
	IBUF = CD [CD_DTE_CHK11_IBUF]: vector [3];

    CD [CD_DTE_CHK11_CHAN] = 0;

    !
    ! Set up argument block with DTE identifier
    !

    DTE_BLOCK [0] = .CD [CD_DTE_SYSID];

    T1 = $DTEDT ^ 18 + DTE_BLOCK;

    if not UUO (1, DTE$ (T1))
    then
	return $false;

    CD [CD_DTE_CHK11_TTY] = .T1;

    !
    ! Open the CHK11 terminal for I/O
    !

    FILOP_BLOCK [$FOFNC] = FO$PRV or FO$ASC or $FORED;
    FILOP_BLOCK [$FOIOS] = UU$AIO or IO$SUP or $IOASL;
    FILOP_BLOCK [$FODEV] = .CD [CD_DTE_CHK11_TTY];
    FILOP_BLOCK [$FOBRH] = IBUF;

    T1 = ($FOBRH+1)^18 + FILOP_BLOCK;

    if not UUO (1, FILOP$ (T1))
    then
	return $false;

    CD [CD_DTE_CHK11_CHAN] = (.FILOP_BLOCK [$FOFNC] and FO$CHN)^-18;
    !
    ! Get size of buffers to allocate
    !

    T1 = FILOP_BLOCK [$FOIOS];

    if not UUO (1, DEVSIZ (T1))
    then
	begin
	    CLOSE_CHK11_TTY (.CD);
	    return $false;
	end;

    BUFFER_SIZE = .T1 <0,18,0>;

    !
    ! Allocate I/O buffers for CHK11 terminal
    !

    if (IBUF [$BFADR] = NMU$FILE_BUILD_BUFFERS (1, .BUFFER_SIZE)) eqla 0
    then
	begin
	    CLOSE_CHK11_TTY (.CD);
	    return $false;
	end;


    !
    ! Set up terminal TTY NO DEFER
    !

    TRMOP_BLOCK [0] = $TOSET + $TODEM;
    TRMOP_BLOCK [1] = .CD [CD_DTE_CHK11_TTY];
    TRMOP_BLOCK [2] = 0;
    T1 = 3 ^ 18 + TRMOP_BLOCK;
    UUO (1, TRMOP$ (T1));

    !
    ! Allocate memory for CHK11 buffer
    !

    if (CD [CD_DTE_CHK11_BUF_ADDR] = NMU$MEMORY_GET (500)) eqla 0
    then
	begin
	    CLOSE_CHK11_TTY (.CD);
	    return $false;
	end;

    begin
      bind
        DAEMON_BUFFER = .CD [CD_DTE_CHK11_BUF_ADDR]: vector [500];

      CD [CD_DTE_CHK11_BUF_CTR] = (500-(2+NH_LEN))*5;	!NH_LEN words for daemon header
      CD [CD_DTE_CHK11_BUF_PTR] = ch$ptr (DAEMON_BUFFER [2+NH_LEN+4],,7);
      incr I from 0 to 14 do DAEMON_BUFFER [.I] = 0;
      DAEMON_BUFFER[0] = $DMERR;	!Set up DAEMON Function code for later
      DAEMON_BUFFER [1] = %o'222';	!check11 code
      ch$move(ch$rchar(ch$ptr(CD[CD_NAME],,8)),
        ch$ptr(CD[CD_NAME],1,8),ch$ptr(DAEMON_BUFFER[2+NH_LEN],,7));
      DAEMON_BUFFER [2 + NH_XID] = NH_LEN;	!Insert offset to circuit name
      DAEMON_BUFFER [2 + NH_LID] = .CD [CD_DTE_SYSID];	!cpu,,dte#
      DAEMON_BUFFER [2 + NH_PTR] = NH_LEN+4;	!offset to log data
    end;
    !
    ! Set up for PSI interrupts for this terminal
    !

    CD [CD_DTE_CHK11_PSI] = ALLOCATE_INTERRUPT_CHANNEL (CHK11_TTY_PSI_INTERRUPT, .CD);

    PSI_BLOCK [$PSECN] = .CD [CD_DTE_CHK11_CHAN];
    PSI_BLOCK [$PSEOR] = (.CD [CD_DTE_CHK11_PSI]*4)^18 or PS$RID or PS$ROD or PS$RDO;
    PSI_BLOCK [$PSEPR] = 0;

    T1 = PS$FAC + PSI_BLOCK;

    if not UUO (1, PISYS$ (T1))
    then
	begin
	    CLOSE_CHK11_TTY (.CD);
	    return $false;
	end;

end;				! of OPEN_CHK11_TTY
%routine ('CLOSE_CHK11_TTY', CD: ref CD_BLOCK): novalue =

!++
! Functional description:
!
!	Routine called when DTE device is closed to close the CHK11
!	terminal and log its output to the system error file.
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	FILOP_BLOCK: FILOP_ARG_BLOCK,
	PSI_BLOCK: PSI_ARG_BLOCK;

    bind
	IBUF = CD [CD_DTE_CHK11_IBUF]: vector [3],
	DAEMON_BUFFER = .CD [CD_DTE_CHK11_BUF_ADDR]: vector [500];

    !
    ! If CHK11 terminal not open, just return
    !

    if .CD [CD_DTE_CHK11_CHAN] eql 0
    then
	return;

    !
    ! Remove CHK11 terminal from PSI system
    !

    PSI_BLOCK [$PSECN] = .CD [CD_DTE_CHK11_CHAN];
    PSI_BLOCK [$PSEOR] = (.CD [CD_DTE_CHK11_PSI]*4)^18 or PS$RID or PS$ROD or PS$RDO;
    PSI_BLOCK [$PSEPR] = 0;

    T1 = PS$FRC + PSI_BLOCK;

    UUO (1, PISYS$ (T1));

    RELEASE_INTERRUPT_CHANNEL (.CD [CD_DTE_CHK11_PSI]);

    !
    ! Flush any CHK11 terminal output
    !

    READ_CHK11_OUTPUT (.CD);

    !
    ! Close the CHK11 terminal
    !

    FILOP_BLOCK [$FOFNC] = (.CD [CD_DTE_CHK11_CHAN])^18 or $FOREL;

    T1 = ($FOFNC+1)^18 + FILOP_BLOCK;
    UUO (1, FILOP$ (T1));

    if .IBUF [$BFADR] neqa 0
    then
	NMU$FILE_KILL_BUFFERS (.IBUF [$BFADR]);

    IBUF [$BFADR] = 0;

    if DAEMON_BUFFER neqa 0
    then
      begin

      !
      ! Log accumulated CHK11 output to error file
      !

      T1 = ((.(DAEMON_BUFFER [2 + NH_NUM]) + 4)/5);	!Calculate # words in CHK11 log
    if .T1 gtr 0
    then
      begin
      !
      ! Total length of arg block is
      !(# of words)+(length of entry header)+(2 daemon args)+(4 wd circuit id)
      !
      T1 = (.T1 + NH_LEN + 2 + 4 )^18 + DAEMON_BUFFER;
      UUO (1, DAEMON (T1));
      end;

      !
      ! Deallocate CHK11 buffer
      !
  
  	NMU$MEMORY_RELEASE (DAEMON_BUFFER, 500);
	CD [CD_DTE_CHK11_BUF_ADDR] = 0;
      end;

end;				! of CLOSE_CHK11_TTY
%routine ('READ_CHK11_OUTPUT', CD: ref CD_BLOCK): novalue =

!++
! Functional description:
!
!	Routine called upon CHK11 terminal PSI interrupt to read
!	CHK11 output data into CHK11 buffer.
!
!--

begin

    builtin
	UUO;

    register
	T1;

    local
	COUNT,
	CHAR,
	FILOP_BLOCK: FILOP_ARG_BLOCK;

    bind
	IBUF = CD [CD_DTE_CHK11_IBUF]: vector [3] volatile,
	DAEMON_BUFFER = .CD [CD_DTE_CHK11_BUF_ADDR]: vector [500];

    !
    ! Loop while CHK11 output is available
    !

    while
	begin
	    FILOP_BLOCK [$FOFNC] = (.CD [CD_DTE_CHK11_CHAN])^18 or $FOINP;
	    T1 = ($FOFNC+1)^18 + FILOP_BLOCK;
	    UUO (1, FILOP$ (T1))
	end
    do
	begin

	    !
	    ! Copy any characters read into CHK11 buffer
	    !

	    COUNT = .CD [CD_DTE_CHK11_BUF_CTR] - 1;
	    if .COUNT gtr .IBUF [$BFCTR]
	    then
		COUNT = .IBUF [$BFCTR];

	    incr I from 1 to .COUNT
	    do
		begin
		    CHAR = ch$rchar_a (IBUF [$BFPTR]);
		    if .CHAR neq 0 then
		    begin
			DAEMON_BUFFER [2 + NH_NUM] = .DAEMON_BUFFER [2 + NH_NUM] + 1;
			ch$wchar_a (.CHAR, CD [CD_DTE_CHK11_BUF_PTR]);
		    end
		end
	end;

end;				! of READ_CHK11_OUTPUT
%routine ('CHK11_TTY_PSI_INTERRUPT', CD: ref CD_BLOCK): vanilla_interrupt_linkage novalue =

!++
! Functional description:
!
!	Routine called upon CHK11 terminal PSI interrupt to read any
!	CHK11 output text.
!
!--

begin

    READ_CHK11_OUTPUT (.CD);

end;				! of CHK11_TTY_PSI_INTERRUPT