Google
 

Trailing-Edge - PDP-10 Archives - TOPS-20_V6.1_DECnetSrc_7-23-85 - mcb/xpt/tlilin.bli
There is 1 other file named tlilin.bli in the archive. Click here to see a list.
module TLILIN	(
		IDENT = 'X01650'
		) =
begin

!
!                    COPYRIGHT (c) 1980, 1981, 1982
!                    DIGITAL EQUIPMENT CORPORATION
!                        Maynard, Massachusetts
!
!     This software is furnished under a license and may  be  used
!     and copied only in accordance with the terms of such license
!     and with the inclusion of the above copyright notice.   This
!     software  or any other copies thereof may not be provided or
!     otherwise made available to any other person.  No  title  to
!     and ownership of the software is hereby transferred.
!
!     The information  in  this  software  is  subject  to  change
!     without  notice  and should not be construed as a commitment
!     by DIGITAL EQUIPMENT CORPORATION.
!
!     DIGITAL assumes no responsibility for the use or reliability
!     of  its  software  on  equipment  which  is  not supplied by
!     DIGITAL.
!

!++
! FACILITY:	Transport
!
! ABSTRACT:
!
!	Line Support module for Transport Line Interface: handles all
!	changes in circuit state, brings circuit up and down.
!
! ENVIRONMENT:	MCB
!
! AUTHOR: L. Webber , CREATION DATE: 23-Jul-79
!
! MODIFIED BY:
!
!	L. Webber, 23-Jul-79 : VERSION 01
!
! 1.01	26-Oct-79, L. Webber
!	Modified to allocate all its own buffers
!
! 1.02	19-Dec-79, L. Webber
!	Modifications to break out Transport Line Interface into its
!	own process
!
! 1.03	3-Jan-80, L. Webber
!	Remove the OT state
!
! 1.04	2-Apr-80, L. Webber
!	Modify to support node and line sub-data-bases
!
! 1.05	16-May-80, L. Webber
!	Convert LIX to/from line index
!
! 1.06	30-Jun-80, L. Webber
!	Modify to use MCBLIB macros
!
! 1.07	A. Peckham, 15-Oct-80
!	Update to use new function modifiers.
!
! 1.08  L. Webber, 20-Nov-80
!	Put in DTE detection for TOPS-20 adjacent nodes.
!
! 1.09	L. Webber, 11-Dec-80
!	Modify to support MCB 3.1
!
! 1.10	L. Webber, 12-Jan-80
!	Make INI_LIN and TERM_LIN linkages CALL$; return Network
!	Management codes for the two functions.
!
! 1.11	L. Webber, 27-Feb-81
!	Fix bug in TI processing: if a bad message is received, return
!	the message to the DLL.
!
! 1.12	L. Webber, 17-Mar-81
!	Make all fetch references to LINEstate UNSAFE, so that they
!	always cause the data base to be re-referenced.
!
! 1.13	L. Webber, 26-Mar-81
!	Put Phase II DLL block size into Node Init; make segment size the
!	same.
!
! 1.14	L. Webber, 27-Mar-81
!	Add reason code to event logging data
!
! 1.15	L. Webber, 1-Apr-81
!	Fix LINE_FAIL linkage to include modifier; this is only necessary
!	to humor the Test Bed, and adds no code.
!
! 1.16	L. Webber, 1-Apr-81
!	Fix so that RCV_TI and RCV_OTI reject initialization on remote
!	blksize LARGER than local, not smaller.
!
! 1.17	L. Webber, 6-Apr-81
!	Add code for initialization timer.
!
! 1.18	L. Webber, 10-Apr-81
!	Release node ID block on line down (for Phase II nodes).
!
! 1.19	L. Webber, 13-Apr-81
!	Fix mapping bug in RCV_OTI
!
! 1.20	L. Webber, 1-May-81
!	Fix LINTIM to put LIX into CCB gotten and dispatched.
!
! 1.21	L. Webber, 27-May-81
!	Modify to support the new DLL Interface.  This is a major modification.
!	Some of the changes involved are:
!
!	o  The $DLL_REQUEST_CIRCUIT and $DLL_RELEASE_CIRCUIT functions to
!	   the DLL have been introduced.  These functions are queued functions
!	   (they use CCBs).
!	o  "Start" and "stop" have been replaced by $DLL_INITIALIZE and
!	   $DLL_STOP.  These functions are queued functions.
!	o  Because it is not necessary to stop a running circuit before doing
!	   a $DLL_INITIALIZE, the DP state has been eliminated.
!	o  TLI now monitors the DLL circuit state by means of state change AST
!	   CCBs from the DLL.  These state changes trigger many of the
!	   Transport line events once triggered by CCB completions.
!	o  The circuit identification over the DLL Interface has been simpli-
!	   fied.  The circuit is always identified to the DLL by "provider
!	   PIX"; this quantity is set in LINEindex by the Network Management
!	   code prior to TLI being invoked.  The Transport identification for
!	   the circuit, the index of the circuit's entry in the line data base,
!	   is passed to the DLL by $DLL_REQUEST_CIRCUIT and to Network Manage-
!	   ment by the Transport Network Management code; any subsequent calls
!	   to Transport about the circuit uses the Transport ID.  LIXxlate has
!	   been eliminated.  The index of a circuit's data base entry may be
!	   found in the LINEnumber field in the entry.
!
! 1.22	L. Webber, 27-May-81
!	Fix LINEDN so that a running line that goes down loses its node type
!	immediately.
!
! 1.23	L. Webber, 28-May-81
!	Put a canned password in the send OTV code.
!
! 1.24	L. Webber, 1-Jun-81
!	Modify all LOG_EVENT calls to pass the NMXid.
!
! 1.25	L. Webber, 3-Jun-81
!	Zero out link field when CCB_CMP removes a CCB from LINEccb.
!
! 1.26	L. Webber, 19-Jun-81
!	When a REQUEST_CIRCUIT completes, do a LINE_EVENT with the returned
!	status.
!
! 1.27	L. Webber, 22-Jun-81
!	Fix bug in REQ_COMPLETE which was saving the returned CCB in LINEevent
!	without noticing there was already one there.
!
! 1.28	L. Webber, 22-Jun-81
!	Change all $DLRCE's to straight $MCB_SCHEDULE_CCB's
!
! 1.29	L. Webber, 30-Jun-81
!	Fix REQUEST_CIRCUIT to get the provider PIX out of the line data base,
!	not the function CCB.
!
! 1.30	L. Webber, 23-Jul-81
!	Fix LINE_EVENT and REQUEST_CIRCUIT to distinguish whether the circuit
!	is ON or OFF.
!
! 1.31	L. Webber, 3-Aug-81
!	Fix resource retry for FUNCreini to bring the line down if it is
!	running.
!
! 1.32	L. Webber, 5-Aug-81
!	Fix LINE_EVENT so that two successive RUNNING state notifications
!	won't cause a SIGNAL_STOP.
!
! 1.33	L. Webber, 12-Aug-81
!	Finish up fix 1.32; a RUNNING can even occur in DT state.
!
! 1.34	L. Webber, 12-Aug-81
!	Fix LINE_EVENT so that it logs events on transient/persistent errors
!	UNLESS it's an entry to MOP, not IF.
!
! 1.35	L. Webber, 19-Aug-81
!	Fix LINE_EVENT so that a state transition to SYNCHRONIZING or RUNNING
!	causes a $DLL_STOP to be (re-)issued.
!
! 1.36	L. Webber, 20-Aug-81
!	Fix LINE_EVENT not to change the line state on a transient error, but
!	to wait for the DLL state change notification.
!
! 1.37	L. Webber, 20-Aug-81
!	Fix SEND_TV to put in the (null) function value.
!
! 1.38	L. Webber, 8-Sep-81
!	Fix REQ_COMPLETE not to initialize the circuit unless Network
!	Management has set it ON.
!
! 1.39	L. Webber, 10-Sep-81
!	Fix INI_LIN always to set the circuit state to DS when it wants to
!	initialize the circuit, even if it can't get the buffer to do the
!	initialization.  This allows the circuit to be brought up with only
!	one buffer.
!
! 1.40	L. Webber, 11-Sep-81
!	Create the macro SAVSDB, which normally would just put a buffer
!	address into LINEevent.  Currently, because of scarceness of buffers,
!	it releases the buffer.
!
! 1.41	L. Webber, 15-Sep-81
!	Fix RCV_OTI so that it doesn't clear LINEevent when LINEevent contains
!	a restart code.
!
! 1.42	A. Peckham, 21-Sep-81
!	Change transient and persistent error response strategy in LINE_EVENT.
!
! 1.43	L. Webber, 24-Sep-81
!	Modify event logging to use new macros for building the event buffer.
!
! 1.44	L. Webber, 2-Oct-81
!	Fix transient error processing so that, when it reinitializes the
!	circuit, it sets the state to DS.
!
! 1.45	A. Peckham, 6-Oct-81
!	Only re-initialize line on WARNING status codes in LINE_EVENT.
!
! 1.46	L. Webber, 20-Oct-81
!	Add a "Reset" line flag to communicate to Decision that a circuit being
!	taken down had a Phase II node adjacent.  This is because it is
!	necessary to zero out the node type immediately.
!
! 1.47	L. Webber, 28-Oct-81
!	Fix the transient/permanent error processing to declare a running
!	circuit down instead of just restarting it.
!
! 1.48	L. Webber, 29-Oct-81
!	Add code to "pause" a circuit for a few seconds after bringing it
!	down, before bringing it back up or closing it down for good.
!
! 1.49	L. Webber, 4-Nov-81
!	Fix dot bug in 1.48.
!
! 1.50	L. Webber, 5-Nov-81
!	Take out 1.48; it didn't help anything.
!
! 1.51	L. Webber, 4-Jan-82
!	Release the CTL/STP CCB when it comes back from the provider in
!	    REL_CMP.
!	Zero out FNC/MOD in CCB GETCCB gets (for debugging).
!
! 1.52	L. Webber, 12-Jan-82
!	Fix a dot bug in RCV_TV.
!
! 1.53	L. Webber, 12-Mar-82
!	Fix RCV_TI to get a CCB to declare the circut up, rather than waiting
!	for the TI transmit to complete (it may already have done so).
!
! 1.54	L. Webber, 22-Mar-82
!	Add support for the Ph2Circuit circuit type.  This circuit will only
!	talk to Phase II nodes.
!
! 1.55	A. Peckham, 19-Apr-82
!	Eliminate references to LOG_EVENT, GETLINE.
!       Work on INILIN to count down its own timers.
!
! 1.56	A. Peckham, 6-May-82
!	Eliminate references to RLS_BLK, GET_BLK.
!
! 1.57	A. Peckham, 2-Jul-82
!	Rework line handling.
!       Add false node-init code, but do not enable.
!
! 1.58	A. Peckham, 6-Jul-82
!	Utilize new XPT data base for initialization passwords.
!
! 1.59	A. Peckham, 8-Jul-82
!       Start initialization clock after TI is transmitted, instead of
!	DLL_RUNNING transition.  More work on password data base.
!       Rename RCV_OTI to RCV_NI.
!
! 1.60	A. Peckham, 23-Aug-82
!       Fix bug in RCV_NI which caused the DN20 to recognize only KLs
!       with even node numbers.  On the node number, bit 0 instead of bit 7
!       was being tested for the node number extension.
!
! 1.61	A. Peckham, 10-Sep-82
!       Add mechanism for getting initial circuit cost from DLL layer:
!         LINRVR: Zero out C_PRM1 before sending $DLL_REQUEST_CIRCUIT.
!         CCPSTR: If Lcv is zero, move C_PRM1 to Lcv, and if that is zero,
!                 set it to one.
!
! 1.62	A. Peckham, 13-Sep-82
!       Maintain Block_size in RCV_NI and RCV_TI.
!
! 1.63	A. Peckham,  4-Oct-82
!       Do not check user-ECO field of TI message in RCV_TI.
!
! 1.64	A. Peckham,  5-Dec-82
!       Acomodate RSX bug in RCV_TI by not verifying full TI length.
!
! 1.65  D. Brannon,  22-Sep-83
!	Correct the Data Link blocksize check for the TI message
!	in RCV_TI.
!
!--
!
! INCLUDE FILES:
!

require 'XPTMAC';

!
! TABLE OF CONTENTS
!
%if %bliss(bliss16)
%then linkage TLI_LKG_LINEb_CCB = jsr (register = 5, register = 4),
              TLI_LKG_LINEb_FNC = jsr (register = 5, register = 0);
%else macro TLI_LKG_LINEb_CCB = bliss36c %,
            TLI_LKG_LINEb_FNC = bliss36c %;
%fi

forward routine
    BAD_MSG: LINKAGE_DB_CCB novalue,
    CCB_CMP: LINKAGE_DB novalue,
    CCPSTP: MCB_DB_CCB novalue,
    CCPSTR: MCB_DB_CCB novalue,
    CIRCUIT_DOWN: LINKAGE_DB novalue,
    CIRCUIT_STATE: LINKAGE_DB_CCB novalue,
    CIRCUIT_UP: LINKAGE_DB novalue,
    CKTOFF: LINKAGE_DB_CCB novalue,
    CKTON: LINKAGE_DB_CCB novalue,
    GET_FALSE_NI: LINKAGE_DB_CCB novalue,
    GET_PWDb,
    INI_LIN: CALL$ novalue,
    LD_CMP: MCB_DB_CCB novalue,
    LINRVR: LINKAGE_DB novalue,
    LOG_PACKET_HEADER: LINKAGE_DB_CCB novalue,
    LU_CMP: MCB_DB_CCB novalue,
    NEW_FUNCTION: TLI_LKG_LINEb_FNC novalue,
    NEW_STATE: TLI_LKG_LINEb_FNC novalue,
    NEXT_ACTION: LINKAGE_DB novalue,
    NEXT_STATE: TLI_LKG_LINEb_FNC novalue,
    RCV_HLO: LINKAGE_DB_CCB novalue,
    RCV_NI: LINKAGE_DB_CCB novalue,
    RCV_NV: LINKAGE_DB_CCB novalue,
    RCV_TI: LINKAGE_DB_CCB novalue,
    RCV_TV: LINKAGE_DB_CCB novalue,
    TERM_LIN: CALL$ novalue,
    TLIAST: LINKAGE_DB_CCB_MOD novalue,
    XCPDAT: MCB_DB_CCB novalue,
    XCPINI: MCB_DB_CCB novalue,
    XCPSTP: MCB_DB_CCB novalue;

!
! MACROS:
!

macro RAD50(NAME) =
    %IF %BLISS(BLISS16) %THEN %rad50_11
    %ELSE %IF %BLISS(BLISS36) %THEN %rad50_10
    %FI %FI
    %string(NAME) %;

!
! EQUATED SYMBOLS:
!

!
! OWN STORAGE:
!

!
! EXTERNAL REFERENCES:
!
global routine BAD_MSG (LINEb,CCB,CODE): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes an invalid message.  In any part of the initialization
!  sequence where the DLL has been started, this event will cause the
!  initialization sequence to be restarted (or started from the RU state).
!  In the rest of the initialization sequence and all of the termination
!  sequence, the event is ignored.
!
! FORMAL PARAMETERS
!
!	LINEb	Address of data base entry for the circuit over which the
!		bad message was received
!
!       CCB	CCB pointing to bad message.  This parameter is
!		meaningful for reason codes 1-8.
!
!	CODE	Reason message is bad:
!
!		0	None of this routine's business
!		1	Unexpected TI or TV when circuit is up
!		2	Unexpected message during initialization
!		3	Version skew
!		4	Out-of-range or invalid node ID
!		5	Invalid remote blocksize
!		6	Invalid verification seed in TI
!		7	Format error
!		8	Verification reject
!               9	Resource failure during initialization
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	May commence or restart initialization.
!
!--

begin
map CCB: ref block field(C_XPT_fields);
require 'XPTSYM';
bind REASON = uplit(!	DNA "reason"	CODE parameter

			0,		!	0
			2,		!	1
			2,		!	2
			6,		!	3
			7,		!	4
			8,		!	5
			9,		!	6
			2,		!	7
			0,		!	8
			0):		!	9
	      vector;
local PTR,RET;

if .CODE eql 0 then return;

PTR = .CCB[C_ADDR];

!
! Log an event
!

$NM_LOG_BEGIN();

case .CODE from 1 to 9 of		! Fill event buffer and determine code
set

[1]:		begin			! Unexpected TI or TV, running circuit:
    RET = 4^6+$XPT$E_LSF;
    COUNTER_INCREMENT(LINEb,XPTlinedn); !   increment circuit failure counter
    LOG_PACKET_HEADER(.LINEb,.CCB);	!   move packet hdr into event buffer
    PARAMETER_C_1(5,REASON[.CODE]);	!   log the reason code
		end;

[2]:		begin			! Unexpected message while initializing
    RET = 4^6+$XPT$E_ISF;
    COUNTER_INCREMENT(LINEb,XPTini_fail);!  increment init. failure counter
    LOG_PACKET_HEADER(.LINEb,.CCB);	!   move packet hdr into event buffer
    PARAMETER_C_1(5,REASON[.CODE]);	!   log the reason code
		end;

[3 to 6]:	begin			! Version skew, out-of-range/invalid
    local VALUE;			! node ID, invalid remote blocksize,
    pointer P;				! or invalid verification seed:
    P = ch$plus(.PTR,6);		!
    RET = 4^6+$XPT$E_IOF;
    COUNTER_INCREMENT(LINEb,XPTini_fail);!  increment init. failure counter,
    LOG_PACKET_HEADER(.LINEb,.CCB);	!   move packet hdr into event buffer
    if .CODE eql 3 then begin		!   packet header
	PARAMETER_CM(6,3);		!   and the version
	VALUE = getb(P);		!   (if needed)
	PARAMETER_DU_1(,VALUE);		!
	VALUE = getb(P);
	PARAMETER_DU_1(,VALUE);
	VALUE = getb(P);
	PARAMETER_DU_1(,VALUE);
	end;
    PARAMETER_C_1(5,REASON[.CODE]);	!   log the reason code
		end;

[7]:		begin			! Format error:
    RET = 4^6+$XPT$E_FMT;
    COUNTER_INCREMENT(XPTDB,XPTfmt_loss);!  increment format loss counter
    LOG_PACKET_HEADER(.LINEb,.CCB);	!   move packet hdr into event buffer
    PARAMETER_C_1(5,REASON[.CODE]);	!   log the reason code
		end;

[8]:		begin			! Verification reject:
    local VALUE;
    pointer P;
    P = ch$plus(.PTR,1);
    RET = 4^6+$XPT$E_VER;
    COUNTER_INCREMENT(XPTDB,XPTver_rej);!  increment verification counter
    PARAMETER_CM(3,1);			!   and move in
    VALUE = getw(P);			!   the source
    PARAMETER_DU_2(,VALUE);		!   node address
		end;

[9]:		begin			! Resource failure while initializing:
    RET = 4^6+$XPT$E_ISF;
    COUNTER_INCREMENT(LINEb,XPTini_fail);!  increment init. failure counter
    PARAMETER_C_1(5,REASON[.CODE]);	!   log the reason code
		end;

[outrange]:	;

tes;
$NM_LOG_END(.RET,.NMXid);               ! Log the event

CALL$L(INI_LIN,.LINEb);                 ! Re-initialize the line
end;				!End of BAD_MSG
routine CCB_CMP (LINEb): LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Terminates all pending function CCBs for a circuit with "success".
!
! FORMAL PARAMETERS
!
!	LINEb	Address of the line data base entry whose function CCB
!		queue is to be cleared out.
!
! IMPLICIT INPUTS
!
!	LINEccb
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin

require 'XPTSYM';

while true do
    begin
    local CCB: ref block field (C_XPT_fields);
    if (CCB = .LINEccb) eqla 0 then exitloop;   ! If there is a CCB,
    LINEccb = .CCB[C_LNK];              ! then de-link it,
    CCB[C_FNC] = FC_CCP;		!  terminate the CCB
    CCB[C_STS] = NM$SUC;		!  with a "success" code;
    $MCB_SCHEDULE_CCB(.CCB);
    end;

end;			!End of CCB_CMP
global routine CCPSTR (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This is a REQUEST complete.
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!--

begin
map CCB: ref block field(C_fields);
require 'XPTSYM';

LINEfunction = 0;

!
!  Get the initial line cost if necessary.
!

if .Lcv eql 0
then if (Lcv = .CCB[C_PRM1]) eql 0
     then Lcv = .Lcv + 1;

!
!  Set the Transport circuit state, depending on whether the circuit
!  is ON or OFF.
!

if .NMstate eql ON
then NEW_STATE (.LINEb, DS);

!
!  Simulate a line state change event
!

CIRCUIT_STATE(.LINEb,.CCB);

$MCB_RETURN_CCB (.CCB);
CCB_CMP(.LINEb);			! Clear out circuit's function queue
NEXT_ACTION (.LINEb);
end;                                    ! End of CCPSTR
global routine CCPSTP (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This is a RELEASE complete.
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!--

begin
map CCB: ref block field(C_fields);
require 'XPTSYM';

LINEfunction = 0;
DLLstate = DLL_FREE;                    ! We are no longer using this circuit
Freeing = false;                        !   and complete freeing process
OWNED = false;
$MCB_RETURN_CCB(.CCB);			! Return the CCB
CCB_CMP(.LINEb);			! Post all function CCBs complete
NEXT_ACTION (.LINEb);
end;                                    ! End of CCPSTP
routine CIRCUIT_DOWN (LINEb): LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!--

begin
require 'XPTSYM';

case .LINEstate from HA to RU of	! Actions are based
    set					! circuit states:
    [RU,TC]:				!   Running -
        NEW_STATE (.LINEb, LR);
    [DT]:                               !   Terminate
        NEXT_STATE (.LINEb, OFF);
    [TI,TV]:                            !   Handshaking -
        NEW_STATE (.LINEb, DS);
    [inrange]:
        ;                               !   Otherwise - do nothing
    tes;

end;			!End of CIRCUIT_DOWN
routine CIRCUIT_STATE (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!       CCB - CCB containing the state notification
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!--

begin
map CCB: ref block field (C_XPT_fields);
require 'XPTSYM';

selectone .CCB[C_STS] of
    set

!
!  Transition to OFF (or MAINTENANCE, which is equivalent to Transport)
!

    [DLL$_STATE_OFF]:
        begin
        DLLstate = DLL_OFF;
        CIRCUIT_DOWN(.LINEb);
        end;
    [DLL$_STATE_MAINTENANCE]:
        begin
        DLLstate = DLL_MAINTENANCE;
        CIRCUIT_DOWN(.LINEb);
        end;
    [DLL$_STATE_SYNCHRONIZING]:
        begin
        local CB: ref block field (C_XPT_fields);
        DLLstate = DLL_SYNCHRONIZING;
        end;

    [DLL$_STATE_RUNNING]:
        begin
        DLLstate = DLL_RUNNING;
        CIRCUIT_UP(.LINEb);
        end;

    tes;

end;			!End of CIRCUIT_STATE
routine CIRCUIT_UP (LINEb): LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!--

begin
require 'XPTSYM';
local CB: ref block field (C_XPT_FIELDS);

if .LINEstate eql DS
then NEXT_STATE (.LINEb, TI);

end;			!End of CIRCUIT_UP
global routine CKTOFF (LINEb, CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
! Releases a circuit to the DLL.
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
!		CCB[C_LIN]	Index of data base entry for circuit
!		CCB[C_PRM2]	Provider PIX
!
! IMPLICIT INPUTS
!
!	LINEccb
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin
map CCB: ref block field (C_XPT_FIELDS);
require 'XPTSYM';

!
!  Queue the function CCB into the data base
!

CCB[C_LNK] = .LINEccb;
LINEccb = .CCB;

Freeing = true;                         !   indicate we're freeing

!
!  If the circuit needs to be brought down, start with that.
!  Otherwise, get to the releasing.
!

if .LINEstate neq HA                    ! If not halted,
then NEW_STATE (.LINEb, DT)             ! start it on the road to HA state.
else begin
     LINEfunction = FUNCrelease;
     NEXT_ACTION (.LINEb);
     end;

end;				!End of CKTOFF
global routine CKTON (LINEb, CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
! Issues a request for a circuit to the DLL.
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
!		CCB[C_LIN]	Line number (index to data base entry)
!		CCB[C_PRM2]	Provider PIX
!
! IMPLICIT INPUTS
!
!	LINEccb
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin
map CCB: ref block field (C_XPT_FIELDS);
require 'XPTSYM';

!
!  Queue the function CCB into the data base
!

CCB[C_LNK] = .LINEccb;
LINEccb = .CCB;

OWNED = true;                           ! Set state to "owned"

!
!  Line will be halted.
!  Start it on the road to RU state.
!

LINEfunction = FUNCrequest;
LINRVR(.LINEb);
end;				!End of CKTON
routine GET_FALSE_NI (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Constructs the False_NI_msg.
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin
map CCB: ref block field (C_XPT_FIELDS);
require 'XPTSYM';
pointer PTR;

if ch$rchar (.CCB[C_ADDR]) eql OTI_CTLflg
then begin                              ! Copy and update NI.
     PTR = .False_NI_msg_ADDR;
     ch$move(.CCB[C_CNT],.CCB[C_ADDR],.PTR);
     False_NI_msg_CNT = .CCB [C_CNT];
     PTR = ch$plus(.PTR,2);             ! Bypass MSGFLG and SUBFLG
     do 0 while biton(getb(PTR),%o'200'); ! NODEADDR,
     begin                              ! NODENAME
     local TMP;
     TMP = getb(PTR);
     PTR = ch$plus(.PTR,.TMP);
     end;
     PTR = ch$plus(.PTR,1);             !  SERVICES
     if .Request_OTV
     then ch$wchar((ch$rchar(.PTR) or 1),.PTR);
     PTR = ch$plus(.PTR,1);             !  REQUESTS
     byt$short_string(PH2size,PTR);     ! Put in the maximum blocksize
     byt$short_string(PH2size,PTR);     !  and the maximum segment size
     putw($MAXlk,PTR);                  !  and the maximum number of links
     end;

end;			!End of GET_FALSE_NI
routine GET_PWDb (ADDR) =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Returns a password data base address for the given node.
!
! FORMAL PARAMETERS
!
!       ADDR - Node address
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!       Address of PWDb.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin
require 'XPTSYM';

$TLI_for_each_PWDb_do_begin
    if .PWDnode eql 0 then return .PWDb;
    if .PWDnode eql .ADDR then return .PWDb;
$TLI_next_PWDb_end;

0                                       ! Never gets here
end;			!End of GET_PWDb
global routine INI_LIN (LINEb_): CALL$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Initializes a circuit, i.e., starts it up and transport-initializes it.
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	Changes circuit state.  Declares circuit "down" if it is not already.
!	Aborts a Terminate sequence.
! NOTE:	This routine acquires a free CCB.  If it is unable to do so, it
!	will place the function requested in the LINEfunction entry for the
!	circuit.  Upon timeout, the function will be rescheduled.
!
!--

begin
local LINEb;
require 'XPTSYM';
LINEb = .LINEb_;
!
!  Do initialization action depending on the circuit state
!

case .LINEstate from HA to RU of
    set
    [RU,TC,OFF]:                        ! If XPT possibly notified,
        NEW_STATE (.LINEb, LR);         ! then make sure line_down is done.
    [HA,DT,TI,TV]:                      ! otherwise
        NEW_STATE (.LINEb, DS);         ! initialize line.
    [inrange]:
        ;                               ! (already initializing)
    tes;

end;				!End of INI_LIN
global routine LD_CMP (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes "line down" completion
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - circuit that is now "down"
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	Changes circuit state.  If in initialization sequence, starts
!	DLL reinitialization of circuit.  If in termination sequence,
!	either returns SDB to pool or (if requested to release circuit)
!	tell the DLL to release the circuit.
!
!--

begin
map CCB: ref block field (C_XPT_FIELDS);
require 'XPTSYM';

LINEfunction = 0;
$MCB_RETURN_CCB (.CCB);

case .LINEstate from HA to RU of
    set
    [LR]:                               ! Continuing initialization:
        NEXT_STATE (.LINEb, DS);
    [OFF]:                              ! Continuing termination:
        NEXT_STATE (.LINEb, HA);
    [inrange]:
	NEXT_ACTION (.LINEb);
    tes;

end;				!End of LD_CMP
global routine LINRVR (LINEb): LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Called by main Transport timer routine.
!  This routine performs resource recovery.
!
!  RESOURCE RECOVERY
!
!  If a function code (distinguished from a SDB address by being only one
!  byte in significance) is in LINEfunction for a circuit, the routine will
!  attempt to get an SDB for the circuit.  If it succeeds, the SDB will be
!  passed to the routine if the routine takes a CCB, and returned otherwise.
!  The routine to be retried will depend on the value of LINEfunction.
!
! FORMAL PARAMETERS
!
!	NONE.
!
! IMPLICIT INPUTS
!
!	LINEfunction
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!
!--

begin
require 'XPTSYM';

if .LINEfunction leq 0 then return;

case .LINEfunction from FUNClo to FUNChi of
    set
    [FUNCrequest] :
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        CCB[C_PRM1] = 0;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_REQUEST_CIRCUIT(.CCB);
        end;
    [FUNCrelease]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        LINEstate = HA;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_RELEASE_CIRCUIT(.CCB);
        end;
    [FUNCinitialize]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        LINEstate = DS;
        Ph2circuit = false;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_INITIALIZE(.CCB);
        end;
    [FUNCstop]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        LINEstate = DT;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_STOP(.CCB);
        end;
    [FUNC_TI]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        CCB[C_XPT_TYPE] = FUNC_TI;
        CCB[C_BIAS] = 0;
        if not (.Ph2only or .Ph2circuit)
        then begin
             CCB[C_ADDR] = .TI_msg_ADDR;
             CCB[C_CNT] = .TI_msg_CNT;
             end
        else if .Intercept
             then begin
                  CCB[C_ADDR] = .NI_msg_ADDR;
                  CCB[C_CNT] = .NI_msg_CNT;
                  end
             else begin
                  CCB[C_ADDR] = .False_NI_msg_ADDR;
                  CCB[C_CNT] = .False_NI_msg_CNT;
                  end;
        Send_TI = false;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_TRANSMIT(.CCB);
        end;
    [FUNC_TV]:
        begin
        local CCB: ref block field (C_XPT_FIELDS),
              PWDb: ref PWDblock;
        if not $MCB_GET_CCB(CCB) then return;
        CCB[C_XPT_TYPE] = FUNC_TV;
        PWDb = GET_PWDb (.Nid);
        if not (.Ph2only or .Ph2circuit)
        then begin
             CCB[C_ADDR] = PWD3TVmsg_ptr;
             CCB[C_CNT] = PWD3TVmsg_len;
             end
        else begin
             CCB[C_ADDR] = PWD2TVmsg_ptr;
             CCB[C_CNT] = PWD2TVmsg_len;
             end;
        Send_TV = false;
        CCB[C_HANDLE] = .DLLhandle;     !  and DLL handle
        $DLL_TRANSMIT(.CCB);
        end;
    [FUNCline_up]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        XPTstate = ON;
        LINEstate = TC;
        CCB[C_HANDLE] = .XPThandle;     !  and XPT handle
        CCB[C_MOD] = TM_INI;            !   "line up" CCB
        CCB[C_FNC] = FC_RCP;            !   and queue it
        $MCB_SCHEDULE_CCB(.CCB);        !   to Transport
        end;
    [FUNCline_down]:
        begin
        local CCB: ref block field (C_XPT_FIELDS);
        if not $MCB_GET_CCB(CCB) then return;
        XPTstate = OFF;
        case .LINEstate_desired from HA to RU of
            set
            [LR,DS,TI,TV,TC,RU]: LINEstate = LR;
            [DT,OFF,HA]: LINEstate = OFF;
            tes;

        if .Nty eql TOPS20
        then begin                      ! TOPS20 node:
             False_NI_LINEb = 0;        !   no longer available
             $XPT_for_each_LINEb_do_begin       ! Stop all adjacent
                 if .Ph2circuit and not .Intercept      ! aspiring
                 then CALL$L(TERM_LIN,.LINEb);  ! Phase II nodes
             $XPT_next_LINEb_end;
             end;

        Nty = 0;                        ! Zero out adjacent node type

        CCB[C_HANDLE] = .XPThandle;     !  and XPT handle
        CCB[C_MOD] = TM_TRM;            !   "line down" CCB
        CCB[C_FNC] = FC_RCP;            !   and queue it
        $MCB_SCHEDULE_CCB(.CCB);        !   to Transport
        end;
    tes;

LINEfunction = -.LINEfunction;
end;				!End of LINRVR
routine LOG_PACKET_HEADER (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Constructs the False_NI_msg.
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

begin
map CCB: ref block field (C_XPT_FIELDS);
require 'XPTSYM';
local TMP;
pointer PTR;

PTR = .CCB[C_ADDR];
PARAMETER_CM(0,2);                      ! Multiple fields, 2
TMP=getb(PTR);                          ! Message type
PARAMETER_H_1(,TMP);                    ! Hex field, 1 byte: routing flags
TMP=getw(PTR);                          ! Decimal unsigned, 2 bytes:
PARAMETER_DU_2(,TMP);                   !   source node address
end;			!End of LOG_PACKET_HEADER
global routine LU_CMP (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes a "line up" completion from Decision.
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - circuit that is now "up"
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	Changes circuit state.  Returns parameter CCB to SDB pool.
!
!--

begin
map CCB: ref block field (C_XPT_fields);
require 'XPTSYM';

LINEfunction = 0;
$MCB_RETURN_CCB(.CCB);

if .LINEstate eql TC
then NEXT_STATE (.LINEb, RU)
else NEXT_ACTION (.LINEb);

end;				!End of LU_CMP 
routine NEW_FUNCTION (LINEb,FUNC): TLI_LKG_LINEb_FNC novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Set up a new function and initiate it.
!
! FORMAL PARAMETERS
!
!	LINEB	address of the line data base
!	FUNC	the new function to initiate.
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE: TRUE/FALSE
! COMPLETION CODES:
!
!	None
!
! SIDE EFFECTS:
!       None
!--

begin
require 'XPTSYM';

LINEfunction = .FUNC;
LINRVR (.LINEb);
end;                                    ! End of NEW_FUNCTION
routine NEW_STATE (LINEb,STATE): TLI_LKG_LINEb_FNC novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Set the line on the way to the given new state
!
! FORMAL PARAMETERS
!
!	LINEB	address of the line data base
!	STATE	the desired new state.
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE: TRUE/FALSE
! COMPLETION CODES:
!
!	None
!
! SIDE EFFECTS:
!       None
!--

begin
require 'XPTSYM';

LINEstate_desired = .STATE;
if .LINEfunction geq 0
then LINEfunction = 0
else if .LINEfunction lss 0
     then selectone .LINEfunction of
              set
              [-FUNC_TI,-FUNC_TV]:
                  LINEfunction = 0;
              [otherwise]:
                  ;
              tes;
NEXT_ACTION (.LINEb);
end;                                    ! End of NEW_STATE
routine NEXT_ACTION (LINEb): LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Find the next action for the line.
!
! FORMAL PARAMETERS
!
!	LINEB	address of the line data base
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE: TRUE/FALSE
! COMPLETION CODES:
!
!	None
!
! SIDE EFFECTS:
!       None
!--

begin
require 'XPTSYM';

case .LINEstate_desired from HA to RU of
    set
    [HA]:
        begin
        Listen_clock = 0;
        Send_TI = false;
        Send_TV = false;

        if .LINEfunction neq 0 then return;

        if .LINEstate eql HA then return;

        selectone .DLLstate of
            set
            [DLL_OFF,DLL_FREE]:
                ;
            [otherwise]:
                return NEW_FUNCTION (.LINEb, FUNCstop);
            tes;

        if .XPTstate eql ON
        then return NEW_FUNCTION (.LINEb, FUNCline_down);

        if .Freeing and (.DLLstate neq DLL_FREE)
        then return NEW_FUNCTION (.LINEb, FUNCrelease);

        LINEstate = .LINEstate_desired;
        end;
    [DT]:
        begin
        Listen_clock = 0;
        Send_TI = false;
        Send_TV = false;

        if .LINEfunction neq 0 then return;

        if .LINEstate eql DT then return;

        return NEW_FUNCTION (.LINEb, FUNCstop);
        end;
    [OFF]:
        begin
        Listen_clock = 0;
        Send_TI = false;
        Send_TV = false;

        if .LINEfunction neq 0 then return;

        if .LINEstate eql OFF then return;

        selectone .DLLstate of
            set
            [DLL_OFF,DLL_FREE]:
                ;
            [otherwise]:
                return NEW_FUNCTION (.LINEb, FUNCstop);
            tes;

        return NEW_FUNCTION (.LINEb, FUNCline_down);
        end;
    [LR]:
        begin
        Listen_clock = 0;
        Send_TI = false;
        Send_TV = false;

        if .LINEfunction neq 0 then return;

        if .LINEstate eql LR then return;

        return NEW_FUNCTION (.LINEb, FUNCline_down);
        end;
    [DS]:
        begin
        Listen_clock = 0;
        Send_TI = false;
        Send_TV = false;

        if .LINEfunction neq 0 then return;

        if .LINEstate eql DS then return;

        return NEW_FUNCTION (.LINEB, FUNCinitialize);
        end;
    [TI]:                               ! Line running; Waiting for TI
        begin

        if .DLLstate neq DLL_RUNNING then return;

        If .LINEstate neq TI
        then begin
             LINEstate = TI;
             Send_TI = true;
             end;

        if .LINEfunction neq 0 then return;

        selectone true of
            set
            [.Send_TI]:
                return NEW_FUNCTION (.LINEB, FUNC_TI);
            [.Send_TV]:
                return NEW_FUNCTION (.LINEB, FUNC_TV);
            [otherwise]:
                ;
            tes;

        return;
        end;
    [TV]:                               ! TI received; waiting for TV
        begin

        if .DLLstate neq DLL_RUNNING then return;

        if .Nty eql 0 then return;

        LINEstate = TV;

        if .LINEfunction neq 0 then return;

        selectone true of
            set
            [.Send_TI]:
                return NEW_FUNCTION (.LINEB, FUNC_TI);
            [.Send_TV]:
                return NEW_FUNCTION (.LINEB, FUNC_TV);
            [otherwise]:
                ;
            tes;

        return;
        end;
    [TC]:                               ! TI(/TV) exchanged
        begin

        if .LINEfunction neq 0 then return;

        if .DLLstate neq DLL_RUNNING then return;

        selectone true of
            set
            [.Send_TI]:
                return NEW_FUNCTION (.LINEB, FUNC_TI);
            [.Send_TV]:
                return NEW_FUNCTION (.LINEB, FUNC_TV);
            [otherwise]:
                ;
            tes;

        if .Nty neq 0
        then return NEW_FUNCTION (.LINEB, FUNCline_up);

        return;
        end;
    [RU]:
        begin

        if .LINEfunction neq 0 then return;

        if .DLLstate neq DLL_RUNNING then return;

        if .XPTstate neq ON then return;

        if .LINEstate eql RU then return;

        Listen_clock = 0;		! Stop the initialization timer.
        LINEstate = RU;
        CCB_CMP(.LINEb);                ! Clean out the function CCB queue

        selectone .Nty of
            set
            [Full,Small]:
                Listen_clock = .Listen_timer;
            [TOPS20]:
                begin
                False_NI_LINEb = .LINEb;
                $XPT_for_each_LINEb_do_begin    ! Start up all adjacent
                    if .Ph2circuit and not .Intercept   ! aspiring
                    then CALL$L(INI_LIN,.LINEb);        ! Phase II nodes
                $XPT_next_LINEb_end;
                end;
            [otherwise]:
                ;
            tes;

        return;
        end;
    tes;

end;                                    ! End of NEXT_ACTION
routine NEXT_STATE (LINEb,STATE): TLI_LKG_LINEb_FNC novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This is called on an automatic line state change.  It only changes
!  the desired state is not forcing a different state.
!
! FORMAL PARAMETERS
!
!	LINEB	address of the line data base
!	STATE	the desired new state.
!
! IMPLICIT INPUTS
!
!	None
!
! ROUTINE VALUE: TRUE/FALSE
! COMPLETION CODES:
!
!	None
!
! SIDE EFFECTS:
!       None
!--

begin
require 'XPTSYM';

if .LINEstate_desired eql .LINEstate
then LINEstate_desired = .STATE;

NEXT_ACTION (.LINEb);
end;                                    ! End of NEXT_STATE
global routine RCV_HLO (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - Index of circuit data base entry
!	CCB[C_BIAS] - buffer descriptor for the NI
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!
!--

begin
map CCB: ref block field(C_fields);
require 'XPTSYM';
label MSG_CHECK;
MSG_CHECK:
begin                                   ! MSG_CHECK
local CNT;
pointer PTR;

! The message is already mapped.

PTR = ch$plus(.CCB[C_ADDR],1);          ! Address buffer past HELLO flag

if (CNT = .CCB[C_CNT] - 4) lss 0 then leave MSG_CHECK;

!
!  Validate the source node address.
!

if getw(PTR) neq .Nid
then begin                              ! Wrong source node:
     $NM_LOG_BEGIN();                   ! Build event buffer
     LOG_PACKET_HEADER(.LINEb,.CCB);	!   move packet hdr into event buffer
     PARAMETER_CM(4,1);                 !   and expected
     PARAMETER_DU_2(,Nid);              !   node address
     $NM_LOG_END(4^6+$XPT$E_LOF,.NMXid);
     CALL$L(INI_LIN,.LINEb);            !   signal the line should be killed
     leave MSG_CHECK;
     end;

!
!  Validate the data
!

if .CNT neq getb(PTR)                   ! Extra or missing data bytes:
then begin                              !   fail
     CALL$L(INI_LIN,.LINEb);
     leave MSG_CHECK;
     end;

if .CNT neq 0
then do begin
        if getb(PTR) neq %o'252'        !   wrong pattern:
        then begin
             $NM_LOG_BEGIN();           !  log the event
             PARAMETER_C_1(5,uplit(11));
             $NM_LOG_END(4^6+$XPT$E_LLF,.NMXid);
             CALL$L(INI_LIN,.LINEb);
             leave MSG_CHECK;
             end;
        end
     while (CNT = .CNT - 1) neq 0;

end;                                    ! MSG_CHECK
end;				!End of RCV_HLO
global routine RCV_NI (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes a received Node Init message (Phase II variety).  If we are in
!  the TI state (expecting a Transport Init), we will send a Node Init, a
!  Node Verify if one is requested, and declare a "line up".  (For sim-
!  plicity, this implementation will never request verification from a Phase
!  II node.) (The "line up" will be delayed until the transmit of the Trans-
!  port Init and Transport Verify have completed.)  The adjacent node type
!  for the circuit will be set to "Phase II".
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - Index of circuit data base entry
!	CCB[C_BIAS] - buffer descriptor for the NI
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	Will mark the node at the other end as Phase II and will perform
!	special Phase II processing against it from now on (until the circuit
!	goes down).  May commence or restart an initialization sequence if
!	we are in the wrong state.
!
!--

begin
map CCB: ref block field (C_fields);
require 'XPTSYM';
label MSG_CHECK,TI_CHECK;
local GOOD,NODEADDR,NAMEPTR,REQUESTS,BLKSIZE,RET;

MSG_CHECK:
begin                                   ! MSG_CHECK
pointer PTR;

MAPBUF(.CCB,PTR);

!
!  Validate the NI message; if it's no good, treat as a BAD_MSG
!

PTR = ch$plus(.PTR,2);                  ! Bypass MSGFLG and STARTTYPE

begin                                   ! Get NODEADDR
if biton((NODEADDR = getb(PTR)),%o'200')
then begin
     NODEADDR = .NODEADDR <0, 7>;
     NODEADDR = .NODEADDR + getb(PTR)^7;
     end;
end;

if .NODEADDR gtru NN
then begin
     GOOD = 4;                          ! Source address too large
     leave MSG_CHECK;
     end;

begin
local LEN;
NAMEPTR = .PTR;
LEN = getb (PTR);                       ! Get node name length

selectone .LEN of
    set
    [1 to 6]:
        begin
        PTR = ch$plus(.PTR,.LEN);       ! Bump pointer past name
        end;
    [otherwise]:
        begin
        GOOD = 7;
        leave MSG_CHECK;
        end;
    tes;
end;

PTR = ch$plus(.PTR,1);                  ! Bypass FUNCTIONS

REQUESTS = getb(PTR);                   ! Save REQUESTS

BLKSIZE = getw(PTR);                    ! BLKSIZE

if .BLKSIZE gtru .PH2size               ! BLKSIZE too large -
then begin
     GOOD = 5;                          !  fail initialization
     leave MSG_CHECK;
     end;

%(
PTR = ch$plus(.PTR,2);                  ! NSPSIZE

PTR = ch$plus(.PTR,2);                  ! MAXLNKS

PTR = ch$plus(.PTR,3);                  ! ROUTVER

PTR = ch$plus(.PTR,3);                  ! COMMVER

begin
local LEN;
LEN = GETB(PTR);
PTR = ch$plus(.PTR,.LEN);               ! SYSVER
end;
)%

GOOD = 0;
end;                                    ! MSG_CHECK

!
!  Dispatch on circuit state
!

case .LINEstate from HA to RU of
    set
    [TI]:                               ! This is the big case
TI_CHECK:
        begin
        MAP$(.CCB[C_BIAS]);             ! Re-map the buffer

        !
        !  Good Node Init - send Verification (if requested) and change state
        !

        if .GOOD eql 0
        then begin
             Ph2circuit = true;
             if not .DTEcircuit         !  Not DTE circuit -
             then begin
                  if (not .Intercept) and
                     (.False_NI_LINEb eqla 0)
                  then begin
                       NEW_STATE (.LINEb, DT);
                       leave TI_CHECK;
                       end;
                  Nty = PhTwo;          !  type is standard Phase II
                  end
             else begin                 !  DTE circuit -
                  GET_FALSE_NI(.LINEb,.CCB);    ! Save NI for later
                  Nty = TOPS20;         !  Node type is TOPS-20
                  end;
             Nid = .NODEADDR;           ! Set node address,
             Nnml = getb (NAMEPTR);     ! name length,
             ch$move (.Nnml,.NAMEPTR,byt$ptr(Nnm));     ! and name.
             Block_size = .BLKSIZE;     ! Block size.
             if not .Ph2only            ! If Phase II NI not sent yet,
             then Send_TI = true;       ! then set to send one.
             if .REQUESTS<0,1> neq 0    ! If requested,
             then Send_TV = true;       ! set to send OTV.
             if .Request_OTV
             then NEXT_STATE (.LINEb, TV)  ! Wait for TV.
             else NEXT_STATE (.LINEb, TC); ! Request a line-up.
             end;
        end;
    [RU]:                               ! Circuit running, message unexpected -
	GOOD = 1;                       !    and treat as garbage message
    [TV,TC]:				! Circuit initializing,
	GOOD = 2;                       !    and treat as garbage message
    [inrange]:				! In middle of DLL protocol -
        GOOD = 0;
    tes;

if .GOOD neq 0                          ! If error, then
then BAD_MSG(.LINEb,.CCB,.GOOD);        ! treat as garbage message
end;				!End of RCV_NI
global routine RCV_NV (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes a received Node Verification message.  If we
!  are expecting one, we will validate it and if valid, declare
!  the circuit up.  (If we have not received back the NI message we
!  sent, the "line up" will be delayed until we do.)
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - Index of circuit data base entry
!	CCB[C_BIAS] - buffer descriptor for OTV's buffer
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	May commence or restart an initialization sequence if we are in
!	the wrong state.
!
!--

begin
map CCB: ref block field (C_fields);
require 'XPTSYM';
local GOOD;
label MSG_CHECK,TV_CHECK;

MSG_CHECK:
begin                                   ! MSG_CHECK
local CNT;
pointer PTR;
MAPBUF(.CCB,PTR,CNT);

!
!  Validate the NV message; if it's no good, treat as a BAD_MSG
!

CNT = .CNT - 10;                        ! Account for full message

if .CNT lss 0
then begin
     GOOD = 7;
     leave MSG_CHECK;
     end;

GOOD = 0;
end;                                    ! MSG_CHECK

!
!  Dispatch on circuit state
!

case .LINEstate from HA to RU of
    set
    [TV]:                               ! This is the big case
TV_CHECK:
        begin
        local PWDb: ref PWDblock;
        pointer PTR;

        if .GOOD neq 0 then leave TV_CHECK;

        PWDb = GET_PWDb (.Nid);
        PTR = ch$plus (.CCB[C_ADDR],3);

        if ch$rchar(byt$ptr (PWD2rcv)) neq 0
        then if ch$neq (8, .PTR, 8, byt$ptr (PWD2rcv), 0)
             then begin
                  GOOD = 8;
                  leave TV_CHECK;
                  end;

        NEXT_STATE (.LINEb, TC);
        end;
    [RU]:                               ! Circuit running, message unexpected -
	GOOD = 1;                       !    and treat as garbage message
    [TI,TC]:				! Circuit initializing,
	GOOD = 2;                       !    and treat as garbage message
    [inrange]:				! In middle of DLL protocol -
        GOOD = 0;
    tes;

if .GOOD neq 0
then BAD_MSG(.LINEb,.CCB,.GOOD);        ! Treat as garbage message
end;				!End of RCV_NV
global routine RCV_TI (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes a received Transport Init message.  If we are expecting
!  one, we will send a Transport Verify if one is requested and go either
!  to receive a Transport Verify (if we asked for one) or to declare a "line
!  up".  (If we have not received back the TI message we sent, the "line up"
!  will be delayed until we do.)
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - Index of circuit data base entry for circuit
!	CCB[C_BIAS] - buffer descriptor for TI
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	May commence or restart an initialization sequence if we are in
!	the wrong state.
!
!--

begin
map CCB: ref block field (C_fields);
require 'XPTSYM';
local GOOD,NODEADDR,TIINFO,BLKSIZE;
label MSG_CHECK;

!
!  If the circuit is a Ph2Circuit, ignore the message
!

if .Ph2only then return;

MSG_CHECK:
begin                                   ! MSG_CHECK
local CNT;
pointer PTR;

MAPBUF (.CCB,PTR,CNT);

!
!  Validate the TI message; if it's no good, treat as a BAD_MSG
!

if (CNT = .CNT - 9) lss 0 then leave MSG_CHECK;

begin                                   ! CTLFLG
local CTLFLG;
CTLFLG = getb(PTR);
if biton(.CTLFLG,%b'11110000')          ! Check the control header
then begin
     GOOD = 7;                          ! Reserved bits on - no good
     leave MSG_CHECK;
     end;
end;

if (NODEADDR = getw(PTR)) gtru NN       ! SRCNODE
then begin
     GOOD = 4;                          ! Source node address too large
     leave MSG_CHECK;
     end;

begin                                   ! TIINFO
TIINFO = getb(PTR);
if biton(.TIINFO,%b'11111000')
then begin
     GOOD = 7;                          ! Reserved bits on
     leave MSG_CHECK;
     end;
selectone .TIINFO<0,2,0> of
    set
    [2,3]:
        0;
    [otherwise]:
        begin
	GOOD = 7;                       ! Node type no good
        leave MSG_CHECK;
        end;
    tes;
end;
BLKSIZE = getw(PTR);

!
! A received Data Link blocksize greater than or equal
! to the maximum (message size of routing message
! containing NN nodes, hello message size, 246)
!

if (.BLKSIZE lssu (NN^1 + 5)) or
   (.BLKSIZE lssu (3+.HELLO_MSG_CNT)) or
   (.BLKSIZE lssu 246)
then begin
     GOOD = 5;				! Remote blocksize too large
     leave MSG_CHECK;
     end;

begin                                   ! TIVER
if ((getb(PTR) neq 1) or
    (getb(PTR) neq 3))
then begin
     GOOD = 3;				! Incompatible version number
     leave MSG_CHECK;
     end;
getb(PTR);
end;

%(
if getb(PTR) neq 0                      ! RESERVED
then begin
     GOOD = 7;
     end;
)%

GOOD = 0;				! Sorry, nothing wrong
end;                                    ! MSG_CHECK

!
! Dispatch on circuit state
!

case .LINEstate from HA to RU of
    set
    [TI]:                               ! This is the big case
        begin
        !
        !  Now process message
        !

        if .GOOD eql 0
        then begin
             Nty = .TIINFO<0,2,0>;      ! Set routing type.
             Nid = .NODEADDR;           ! Save adjacent node address
             Nnml = 0;                  !  with no name.
             Block_size = .BLKSIZE;     ! This is his block size.
             if biton(.TIINFO,VFY_req)  ! and If requested,
             then Send_TV = true;       ! then schedule one sent.
             if .Request_TV
             then NEXT_STATE (.LINEb, TV)  ! Wait for TV.
             else NEXT_STATE (.LINEb, TC); ! Request a line-up.
             end;
        end;
    [RU]:                               ! Circuit running, message unexpected -
	GOOD = 1;                       !    and treat as garbage message
    [TV,TC]:				! Circuit initializing,
	GOOD = 2;                       !    and treat as garbage message
    [inrange]:                          ! In middle of DLL protocol -
        GOOD = 0;
    tes;

if .GOOD neq 0
then BAD_MSG(.LINEb,.CCB,.GOOD);        ! Treat as garbage message
end;				!End of RCV_TI
global routine RCV_TV (LINEb,CCB): LINKAGE_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Processes a received Transport Verification message.  If we
!  are expecting one, we will validate it and if valid, declare
!  the circuit up.  (If we have not received back the TI message we
!  sent, the "line up" will be delayed until we do.)
!
! FORMAL PARAMETERS
!
!	CCB[C_LIN] - Index of circuit data base entry
!	CCB[C_BIAS] - buffer descriptor for TV's buffer
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	May commence or restart an initialization sequence if we are in
!	the wrong state.
!
!--

begin
map CCB: ref block field (C_fields);
require 'XPTSYM';
local GOOD;
label MSG_CHECK,TV_CHECK;

MSG_CHECK:
begin                                   ! MSG_CHECK
local CNT;
pointer PTR;
MAPBUF(.CCB,PTR,CNT);

!
!  Validate the TV message; if it's no good, treat as a BAD_MSG
!

if (CNT = .CNT - 4) lss 0 then leave MSG_CHECK;

begin                                   ! CTLFLG
local CTLFLG;
CTLFLG = getb(PTR);
if biton(.CTLFLG,%b'11110000')          ! Check the control header
then begin
     GOOD = 7;                          ! Reserved bits on - no good
     leave MSG_CHECK;
     end;
end;

if getw(PTR) neq .Nid                   ! SRCNODE
then begin
     GOOD = 4;                          ! Source node is not the adjacent node
     leave MSG_CHECK;
     end;

%(
begin                                   ! FCNVAL
local LEN;
LEN = getb(PTR);
PTR = ch$plus(.PTR,.LEN);
end;
)%

GOOD = 0;
end;                                    ! MSG_CHECK

!
!  Dispatch on circuit state
!

case .LINEstate from HA to RU of
    set
    [TV]:                               ! This is the big case
TV_CHECK:
        begin
        local PWDb: ref PWDblock;
        pointer PTR;

        if .GOOD neq 0 then leave TV_CHECK;

        PWDb = GET_PWDb (.Nid);
        PTR = ch$plus (.CCB[C_ADDR],3);

        if .PWD3rcv_length neq 0
        then if (getb(PTR) neq .PWD3rcv_length) or
                 ch$neq (.PWD3rcv_length, .PTR,
                         .PWD3rcv_length, byt$ptr (PWD3rcv), 0)
             then begin
                  GOOD = 8;
                  leave TV_CHECK;
                  end;

        NEXT_STATE (.LINEb, TC);
        end;
    [RU]:                               ! Circuit running, message unexpected -
	GOOD = 1;                       !    and treat as garbage message
    [TI,TC]:				! Circuit initializing,
	GOOD = 2;                       !    and treat as garbage message
    [inrange]:				! In middle of DLL protocol -
        GOOD = 0;
    tes;

if .GOOD neq 0
then BAD_MSG(.LINEb,.CCB,.GOOD);        ! Treat as garbage message
end;				!End of RCV_TV
global routine TERM_LIN (LINEb): CALL$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  Terminates a circuit, i.e., stops the circuit at the Data Link Level and
!  generates a "line down" event.  The sequence that this routine starts
!  differs from that of INI_LIN in that the circuit will not be restarted;
!  to restart the circuit, call INI_LIN.
!
! FORMAL PARAMETERS
!
!       LINEb - line data base
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	Changes circuit state.  Declares circuit "down" if it is not down
!	already.  Terminates a Initialize sequence.
! NOTE:	This routine acquires a free CCB.  If it is unable to get one,
!	the function requested will be put in the LINEfunction cell for the
!	circuit.  The function will be retried on timeout.
!
!--

begin
require 'XPTSYM';

begin                                   ! Kill all start requests
local Chain;
Chain = .LINEccb;
LINEccb = 0;
while true do
    begin
    local CCB: ref block field (C_fields);
    if (CCB = .Chain) eqla 0 then exitloop;
    Chain = .CCB[C_LNK];
    if (.CCB[C_MOD] eql FM_STR)         !  If this is...
    then begin                          !    ...an initialization function,
         CCB[C_STS] = CE_DIS;           !      then return the CCB to the
         CCB[C_FNC] = FC_CCP;           !      caller with a failure code
         $MCB_SCHEDULE_CCB(.CCB);
         end
    else begin                          ! otherwise
         CCB[C_LNK] = .LINEccb;         ! hold on to it.
         LINEccb = .CCB;
         end;
    end;
end;

!
!  Do termination processing depending on the circuit state
!

case .LINEstate from HA to RU of
    set
    [OFF,DT]:                           ! Do nothing
        ;
    [HA]:                               ! Already halted -
	CCB_CMP(.LINEb);		!  clean out function queue
    [inrange]:				! Start termination:
        NEW_STATE (.LINEb, DT);
    tes;

end;				!End of TERM_LIN
global routine TLIAST (DATABASE,CCB,MODIFIER): LINKAGE_DB_CCB_MOD novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
! Processes an asynchronous event from the DLL.  There are three types of
! asynchronous events:
!
!	o  State Change		This allows Transport to follow the DLL
!				state.  State changes drive part of the
!				TLI state machine.
!
!	o  Transient Error	These are "software" errors, which indicate
!				an interruption of the DLL protocol on
!				the circuit.  Receipt of such an error will
!				cause Transport to recycle the circuit, if
!				it is up.
!
!	o  Persistent Error	These are errors from which the device or
!				protocol cannot recover on its own.  If it
!				is a warning, recovery can be made through
!                               initialization, otherwise stop using
!                               the circuit.
!
! All transient and persistent errors (except entry to MOP) cause logging of
! circuit events; no state change does so directly.
!
! FORMAL PARAMETERS
!
!	DATABASE	Dummy parameter; not used
!	CCB		Function CCB for the asynchronous event
!	  CCB[C_MOD]		Event class:
!				  DLL$K_STATE
!				  DLL$K_TRANSIENT_ERROR
!				  DLL$K_PERSISTENT_ERROR
!	  CCB[C_STS]		Specific event type:
!
!				  For DLL$K_STATE:
!					DLL$_STATE_OFF
!					DLL$_STATE_SYNCHRONIZING
!					DLL$_STATE_RUNNING
!					DLL$_STATE_MAINTENANCE
!
!				  For DLL$K_TRANSIENT_ERROR:
!					DLL$_START_RECEIVED
!					DLL$_TRANSMIT_THRESHOLD
!					DLL$_RECEIVE_THRESHOLD
!					DLL$_SELECTION_THRESHOLD
!
!				  For DLL$K_PERSISTENT_ERROR:
!					DLL$_DEVICE_RESPONSE_ERROR
!					DLL$_DEVICE_SERVICE_ERROR
!					DLL$_DEVICE_OPERATION_ERROR
!
!	MODIFIER	Dummy parameter; not used
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!--

begin

local LINEb;
require 'XPTSYM';
map CCB: ref block field (C_XPT_FIELDS);
local CB: ref block field (C_XPT_FIELDS);
local REASON;
literal
    REASONstop = 1^8,
    REASONlog = 1^9,
    REASONinit = 1^10;

!
!  Address the circuit data base entry
!

LINEb = $TLI_GET_LINE_DB(.CCB[C_LIN]);
REASON = 0;

select .CCB[C_MOD] of
    set
    [DLL$K_STATE]:                      ! State change
        CIRCUIT_STATE(.LINEb,.CCB);
    [DLL$K_TRANSIENT_ERROR]:            ! Transient errors
        REASON = REASONinit + REASONlog + 0;	! Synch lost
    [DLL$K_PERSISTENT_ERROR]:           ! Persistent errors
        selectone .block [CCB[C_STS], STS$V_SEVERITY] of
            set
            [STS$K_WARNING]:
                REASON = REASONinit + REASONlog + 0;	! Synch lost
            [otherwise]:
                REASON = REASONstop + REASONlog + 1;    ! Data errors
            tes;
    tes;


!
!  Log an event (except for MOP Mode Received)
!

    if biton(.REASON,REASONlog) then begin
	local VALUE;
	$NM_LOG_BEGIN();
	VALUE = .REASON<0,8>;			! Move in the
	PARAMETER_C_1(5,VALUE);			!   reason code

	$NM_LOG_END(4^6+
	(case .LINEstate from HA to RU of
	set
	[HA,DT,OFF,DS]:	begin
	    COUNTER_INCREMENT(LINEb,XPTini_fail);! Increment the
	    $XPT$E_ILF				!   appropriate counter
			end;			!   ("line down" or
	[inrange]:	begin			!   "initialization failure")
	    COUNTER_INCREMENT(LINEb,XPTlinedn); !   and declare the
	    $XPT$E_LLF				!   appropriate event
			end;
	tes),.NMXid);
	end;

!
!  If the error is correctible, reinitialize the circuit.
!  If it is permanent, terminate the circuit.
!

    if biton(.REASON,REASONinit)        ! Correctible error -
    then selectone .LINEstate of        !   if necessary,
             set
             [RU,TC]:                   !   declare the
                 NEW_STATE (.LINEb, LR);
             [otherwise]:               !   otherwise just
                 NEW_STATE (.LINEb, DS);
             tes;

    if biton(.REASON,REASONstop)        ! Permanent error -
    then NEW_STATE (.LINEb, DT);        !  bring the circuit down for good

!
!  Release the function CCB
!

$MCB_RETURN_CCB(.CCB);
end;			!End of TLIAST
global routine XCPDAT (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This will be a TI/TV/OTI/OTV message
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!--

begin
map CCB:ref block field (C_XPT_FIELDS);
require 'XPTSYM';

if .CCB[C_XPT_TYPE] eql FUNC_TI
then if .LINEstate eql TI
     then Listen_clock = .Init_timer;

CCB[C_XPT_TYPE] = -.CCB[C_XPT_TYPE];

if .LINEfunction eql .CCB[C_XPT_TYPE]   ! (These functions can be cancelled)
then LINEfunction = 0;

$MCB_RETURN_CCB(.CCB);

NEXT_ACTION (.LINEb);
end;                                    ! End of XCPDAT
global routine XCPINI (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This will be a TI/TV/OTI/OTV/HELLO message
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!--

begin
map CCB:ref block field (C_XPT_FIELDS);
require 'XPTSYM';

LINEfunction = 0;
$MCB_RETURN_CCB (.CCB);
NEXT_ACTION (.LINEb);
end;                                    ! End of XCPINI
global routine XCPSTP (LINEb,CCB): MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!  This will be a TI/TV/OTI/OTV/HELLO message
!
! FORMAL PARAMETERS
!
!	CCB	Function CCB
!
! IMPLICIT INPUTS
!
!	LINEstate
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!--

begin
map CCB:ref block field (C_XPT_FIELDS);
require 'XPTSYM';

LINEfunction = 0;
$MCB_RETURN_CCB (.CCB);
NEXT_ACTION (.LINEb);
end;                                    ! End of XCPSTP
end                                     ! End of module TLILIN
eludom